Поради щодо гольфу в Python


248

Які загальні поради щодо гольфу в Python? Я шукаю ідеї, які можна застосувати до проблем із кодом-гольфу, які також принаймні дещо специфічні для Python (наприклад, "видалити коментарі" - це не відповідь).

Будь ласка, опублікуйте одну пораду на відповідь.


27
О, я бачу цілий набір подібних питань, що надходять до кожної мови ...
Р. Мартіньо Фернандес,

4
@Marthinho Я згоден. Щойно запустили еквівалент С ++ . Я не вважаю, що це погано, до тих пір, поки ми не бачимо однакових відповідей, перепублікованих для багатьох із цих питань.
marcog

50
Люблю питання, але я повинен продовжувати говорити собі: "Це ТІЛЬКО для розваги НЕ для виробничого коду"
Грег Гуйда,

2
Чи не повинно це питання бути вікі-публікацією спільноти?
снідахань

3
@dorukayhan Nope; це дійсний код-гольф поради питання, просити поради про скорочення пітона коду для CG'ing цілей. Такі запитання цілком справедливі для сайту, і жоден із цих тегів прямо не говорить про те, що питання має бути CW'd, на відміну від ТА, яка вимагала, щоб завдання CG були CW'd. Крім того, написання хорошої відповіді та пошук таких порад завжди заслуговує чогось, що забирається, якщо питання - вікі спільноти (rep).
Ерік Аутгольфер

Відповіді:


152

Використовуйте a=b=c=0замість a,b,c=0,0,0.

Використовуйте a,b,c='123'замість a,b,c='1','2','3'.


2
це приємна порада в цілому :)

28
Зауважте, що це не обов'язково буде працювати для визначення змінних об'єктів, які ви будете змінювати на місці. a = b = [1] насправді відрізняється від a = [1]; b = [1]
isaacg

6
Найсмішніше, що стосується першої поради - це те, що він працює і в Java.
Джастін

1
@Justin Так, але лише з примітивними типами
HyperNeutrino

11
Але НІКОЛИ не використовуйте a = b = c = [] або будь-яку інстанцію об'єкта, оскільки всі змінні будуть вказувати на один і той же екземпляр. Мабуть, це не те, що ти хочеш.
PhE

146

Умови можуть бути тривалими. У деяких випадках ви можете замінити простий умовний (a,b)[condition]. Якщо conditionце правда, то bповертається.

Порівняйте

if a<b:return a
else:return b

До цього

return(b,a)[a<b]

37
Це зовсім не те саме. Перший оцінює лише той вираз, який повертається, а другий завжди оцінює їх обидва. Ці роблять коротке замикання: a if a<b else bтаa<b and a or b
Марін

3
(lambda(): b, lambda(): a)[a < b]()зробіть власне коротке замикання з лямбдами
Мінг-Тан

3
@marinus, вони не рівні: просто врахуйте P and A or Bбудь-який А, який дає bool(A)=False. Але (P and [A] or [B])[0]зробимо роботу. Довідково дивіться diveintopython.net/power_of_introspection/and_or.html .
kgadek

6
Лямбди набагато довші, ніж умовний вираз.
user2357112

18
@ user2357112 Але вони змушують вас виглядати набагато крутіше, коли ви їх використовуєте. :]
Chase Ries

117

Чудова річ, яку я зробив одного разу:

if 3 > a > 1 < b < 5: foo()

замість:

if a > 1 and b > 1 and 3 > a and 5 > b: foo()

Оператори порівняння Python рок.


Використовуючи, що в Python 2 все порівняно, ви також можете уникнути andоператора таким чином. Наприклад, якщо a, b, cі dє цілими числами,

if a<b and c>d:foo()

можна скоротити одним символом до:

if a<b<[]>c>d:foo()

При цьому використовується те, що кожен список більший за будь-яке ціле число.

Якщо cі dє списки, це стає ще кращим:

if a<b<c>d:foo()

22
Звичайно, якби це справді гольф, це було б3>a>1<b<5
Рейф Кеттлер,

4
Любіть симетрію. Нагадує мені про старий трюк з гольфу Perl для пошуку мінімуму $ a та $ b: [$a => $b]->[$b <= $a]:)
Саймон Уітакер

Зауважте, що другий приклад (без списків) також можна зробити зif(a<b)+(c>d):foo()
WorldSEnder

6
+ Має бути а *. Було orб+
WorldSEnder

1
foo()if 3>a>1<b<5
Ерік Аутгольфер

103

Якщо ви використовуєте вбудовану функцію неодноразово, можливо, буде більш простором дати їй нове ім'я, якщо використовуєте різні аргументи:

r=range
for x in r(10):
 for y in r(100):print x,y

6
Однак насправді не зберегли жодного байта.
user2357112

4
r = діапазон, а інші два r - 9 символів; Використання діапазону двічі - 10 символів. У цьому прикладі не велика економія, але все, що потрібно, це ще одне використання діапазону, щоб побачити значну економію.
Френк

13
@Frank Додатковий новий рядок - ще один символ.
L3viathan

2
Дійсно, два повторення занадто мало, щоб заощадити на назві функції довжиною п'ять. Вам потрібно: довжина 2: 6 повторень, довжина 3: 4 повторень, довжина 4 або 5: 3 повторень, довжина> = 6: 2 повторень. AKA (довжина-1) * (повторення-1)> 4.
Ørjan Johansen

Зверніть увагу, це стосується всіх мов, які мають першокласні функції.
bfontaine

94

Іноді для вашого коду Python потрібно мати два рівні відступи. Очевидно, що потрібно використовувати один і два пробіли для кожного рівня відступу.

Однак Python 2 вважає символи вкладок та пробілів різними рівнями відступу.

Це означає, що перший рівень відступу може бути одним пробілом, а другий - одним символом вкладки.

Наприклад:

if 1:
 if 1:
\tpass

Де \tсимвол вкладки.


1
Класно, я ніколи про це не думав!
Jules Olléon

97
Це не вдається в python3: ви більше не можете змішувати пробіли та вкладки (це погано для codegolf, але добре в усіх інших випадках).
Бакуріу

1
У python 3.4 це, здається, працює добре.
трихоплакс

3
@trichoplax , In python 3.4.3 Я отримуюTabError: inconsistent use of tabs and spaces in indentation.
zgorycat

Для довідки, на вкладці варто 8 пробілів.
Ерік Аутгольфер

87

Використовуйте підстановку рядків і execобробляйте довгі ключові слова, такі lambda, які часто повторюються у вашому коді.

a=lambda b:lambda c:lambda d:lambda e:lambda f:0   # 48 bytes  (plain)
exec"a=`b:`c:`d:`e:`f:0".replace('`','lambda ')    # 47 bytes  (replace)
exec"a=%sb:%sc:%sd:%se:%sf:0"%(('lambda ',)*5)     # 46 bytes  (%)

Цільовий рядок дуже часто 'lambda ', який становить 7 байт. Припустимо, ваш фрагмент коду містить nвипадки 'lambda 'та є sбайти довгими. Тоді:

  • plainОпція sбайт довжиною.
  • replaceОпція s - 6n + 29байт довжиною.
  • %Опція s - 5n + 22 + len(str(n))байт довжиною.

З сюжету байтів, збереженихplain для цих трьох варіантів, ми бачимо, що:

  • Для n <5 лямбдів вам краще взагалі нічого не робити.
  • Для n = 5 написання exec"..."%(('lambda ',)*5)економить 2 байти, і це найкращий варіант.
  • Для n> 5 написання exec"...".replace('`','lambda ')- найкращий варіант.

Для інших випадків ви можете проіндексувати таблицю нижче:

          1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 (occurences)
       +---------------------------------------------------------
     3 |  -  -  -  -  -  -  -  -  -  -  -  -  -  -  r  r  r  r  r  
     4 |  -  -  -  -  -  -  -  -  -  r  r  r  r  r  r  r  r  r  r  
     5 |  -  -  -  -  -  -  -  r  r  r  r  r  r  r  r  r  r  r  r  
     6 |  -  -  -  -  -  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
     7 |  -  -  -  -  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
     8 |  -  -  -  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
     9 |  -  -  -  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
    10 |  -  -  %  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
    11 |  -  -  %  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
    12 |  -  -  %  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r = replace
    13 |  -  -  %  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r   % = string %
    14 |  -  %  %  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r   - = do nothing
    15 |  -  %  %  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
  (length)

Наприклад, якщо рядок lambda x,y:(довжина 11) трапляється у вашому коді 3 рази, вам краще писати exec"..."%(('lambda x,y:',)*3).


4
це має отримати більше голосів, це дуже корисна порада.
bigblind

7
це вкрай рідко , що це працює. вартість replaceвеличезна.
кабінка

4
Однак, коли це працює, це дуже допомагає.
підземниймонорельс

Цікаво, ніколи навіть цього не замислюйтесь!
Клавдіу

Я додав нового оператора для лямбда на моїй мові на основі python: =>це просто рядок = lambda . Наприклад, f=>:0було б f = lambda: 0.
NoOneIsHere

78

Використовуйте розширені нарізки, щоб вибрати один рядок з багатьох

>>> for x in 0,1,2:print"fbboaaorz"[x::3]
... 
foo
bar
baz

проти

>>> for x in 0,1,2:print["foo","bar","baz"][x]
... 
foo
bar
baz

У цьому бульовому дворядковому випадку також можна писати

b*"string"or"other_string"

для

["other_string","string"][b]

На відміну від переплетення, це працює для рядків будь-якої довжини, але може мати проблеми з пріоритетом оператора, якщо bзамість цього є вираз.


Зауважимо, що перший приклад точно такої ж довжини, якfor x in ("foo","bar","baz"): print x
Mateen Ulhaq

1
@MateenUlhaq, це лише приклад того, як відображаються різні значення x. Гольфізована частина - "fbboaaorz"[x::3]проти ["foo","bar","baz"][x]того, xяк отримане значення буде ще однією частиною вашого рішення для гольфу.
гніблер

72

Використовуйте `n`для перетворення цілого числа в рядок замість використання str(n):

>>> n=123
>>> `n`
'123'

38
Приємно, але не працює з Python3.
Олександру

2
Увага: дійсно працює для цілих чисел, але не для рядків, наприклад.
Накілон

41
btw. `` скорочено до repr
Олександру

9
Цілі особи, менші за -2 ** 31 або більше 2 ** 31-1 (Довгі), отримують позначку "L" в кінці.
hallvabo

6
Це також можна використовувати для друку поплавців до повної точності
gnibbler

69

Зберігайте таблиці пошуку як магічні числа

Скажімо, ви хочете жорстко кодувати булеву таблицю пошуку, наприклад, який із перших дванадцяти англійських номерів містить n.

0: False
1: True
2: False
3: False
4: False
5: False
6: False
7: True
8: False
9: True
10:True
11:True
12:False

Тоді ви можете коротко реалізувати цю таблицю пошуку як:

3714>>i&1

з отриманим 0або 1бути рівним , Falseщоб True.

Ідея полягає в тому, що магічне число зберігає таблицю як бітстринг bin(3714)= 0b111010000010, причому -та nцифра (з кінця) відповідає запису nth-й таблиці. Ми nотримуємо доступ до цього запису, перемістивши бітовий nпробіл праворуч і взявши останню цифру на &1.

Цей спосіб зберігання дуже ефективний. Порівняйте з альтернативами

n in[1,7,9,10,11]
'0111010000010'[n]>'0'

Ви можете мати багатобітові записи таблиці зберігання таблиці, які можна витягнути

 340954054>>4*n&15

витягти відповідний чотирибітний блок.


Чи можемо ми мати приклад результату для чотирибітного блоку? Ви використовували правило для n-бітного блоку?
JeromeJ

8
Hex іноді може бути навіть меншим.
Joonazan

4
Це корисно для багатьох мов.
Кіос

1
@Joonazan Hex менший для номерів понад 999 999 .
Mateen Ulhaq

60

Згорніть дві числові петлі в одну

Скажіть, ви повторюєте клітинки m*nсітки. Замість двох вкладених forциклів, однієї для рядка і одного для стовпців, зазвичай коротше використовувати одну петлю, щоб переглядати m*nкомірки сітки. Ви можете витягти рядок і стовпець комірки всередині циклу.

Оригінальний код:

for i in range(m):
 for j in range(n):
  do_stuff(i,j)

Код для гольфу:

for k in range(m*n):
  do_stuff(k/n,k%n)

По суті, ви повторюєте декартовий продукт двох діапазонів, кодуючи пару (i,j)як x=i*n+j. Ви зберігаєте дорогий rangeдзвінок і рівень відступу всередині циклу. Порядок ітерації незмінний.

Використовуйте //замість /в Python 3. Якщо ви посилаєтеся iі jбагато разів, це може бути швидше , щоб призначити їх значення i=k/n, j=k%nвсередині циклу.


5
Це круто. Я ніколи не розумів, що це можливо!
theonlygusti

Я бачив це в порадах щодо JavaScript. Це досить корисна хитрість на більшості мов.
Кіос

7
Для довідки, поширити це на 3 петлі:for i in range(m*n*o): do_stuff(i/n/o,i%(n*o)/o,i%o)
mbomb007

3
Для nциклів: repl.it/EHwa
mbomb007

У деяких випадках itertools.productможе бути набагато більш лаконічним, ніж вкладені петлі, особливо при генерації декартових виробів. a1, a2, b1, b2є прикладами декартового твору 'ab'і'12'
Аарон3468

54

Якщо наступний маркер починається з eабо E. Ви можете видалити пробіл після номера.

Наприклад:

if i==4 and j==4:
    pass

Стає:

if i==4and j==4:
    pass

Використовуючи це у складних операторах одного рядка, можна зберегти досить багато символів.

EDIT: як зазначав @marcog, 4or aбуде працювати, але не так, a or4як це плутається з ім'ям змінної.


37
if(i,j)==(4,4):ще коротше, і в цьому окремому випадкуif i==j==4:
гніблер

3
Пов’язано: 4or aпрацює, але ніa or4
marcog

17
0orтакож не працює ( 0oє приставкою для восьмеричних чисел).
Nabb

5
@Nabb Не те, що це все одно має значення, оскільки 0 or xзавжди повернусь x. Може добре вирізати 0 or.
ɐɔıʇǝɥʇuʎs

5
0orчудово, як частина більшого числа. 10 or xеквівалентно 10or x.
трихоплакс

53

Для цілого числа nможна писати

  • n+1 як -~n
  • n-1 як ~-n

тому що біт фліп ~xдорівнює -1-x. При цьому використовується однакова кількість символів, але можна опосередковано вирізати пробіли чи парени для переваги оператора.

Порівняйте:

while n-1:  #Same as while n!=1 
while~-n:

c/(n-1)
c/~-n

or f(n)+1
or-~f(n) 

(n-1)/10+(n-1)%10
~-n/10+~-n%10

Оператори ~і Унарний -вище пріоритет , ніж *, /, %, в відміну від виконуваного файлу +.


11
Різновид цього трюку я зіткнувся сьогодні: -~-xекономить один байт vs. (1-x).
Лінн

4
Іншим корисним додатком є ​​те, що a+b+1можна більш стисло записати як a-~b.
Strigoides

І n-i-1просто n+~i.
ruohola

51

Хороший спосіб перетворити ітерабельний список у список на Python 3 :

уявіть, у вас є якісь ітерабельні, як

i = (1,2,3,4)
i = range(4)
i = (x**2 for x in range(5))

Але вам потрібен список:

x=list(i)  #the default way
*x,=i      #using starred assignment -> 4 char fewer

Дуже корисно складати список символів з рядка

s=['a','b','c','d','e']
s=list('abcde')
*s,='abcde'

1
набравши, *s,='abcde'а потім sвибиває мій інтерактивний python3 з segfault :(
daniero

@daniero Вау. Тільки на інтерактивній консолі? Звучить дуже дивно. Спробуйте на чистій консолі або повідомте про помилку
JBernardo

1
Мій Python 3.5 працює чудово.
NoOneIsHere

для i = (x ** 2 для x у діапазоні (5)) я отримую цей код повернутий <об’єкт генератора <genexpr> за 0x03321690>
Джордж

7
І якщо ви робите це в виразі, ви можете зробити [*'abcde'].
Esolanging Fruit

46

Замість цього range(x)ви можете використовувати *оператора в списку будь-чого, якщо вам насправді не потрібно використовувати значення i:

for i in[1]*8:pass

на відміну від

for i in range(8):pass

Якщо вам потрібно зробити це більше двох разів, ви можете призначити будь-яку ітерабельну змінну та помножити цю змінну на потрібний діапазон:

r=1,
for i in r*8:pass
for i in r*1000:pass

Примітка . Це часто довше exec"pass;"*8, тому цей трюк слід використовувати лише тоді, коли це не є можливим.


@proudhaskeller Я думаю, що пункт, який ви видалили, полягав у тому, що "Окрім очевидних заощаджень персонажів, які ви отримуєте, оскільки [1]*8вони коротші range(8), ви також можете заощадити простір, оскільки for i in[...це законно, а for i in range...поки немає".
підземниймонорельс

о, правда, я цього не зрозумів. виправлено зараз
гордий haskeller

7
exec"pass;"*8значно коротше.
DJMcMayhem

1
якщо r=1, r*8це 8, і ви не можете повторити число. Я думаю, ти мав на увазіr=[1]
Артеміда Фаул

1
@ArtemisFowl, ні, це нормально, як є, кома після 1 створює кортеж, який можна перетворити.
саша

43

Ви можете використовувати старий добрий чужий смайлик для зворотної послідовності:

[1, 2, 3, 4][::-1] # => [4, 3, 2, 1]

38

Розширене ітерабельне розпакування ("Призначення зірочкою", лише Python 3)

Найкращий спосіб пояснити це на прикладі:

>>> a,*b,c=range(5)
>>> a
0
>>> b
[1, 2, 3]
>>> c
4

Ми вже бачили користь для цього - перетворення ітерабельного списку в список на Python 3 :

a=list(range(10))
*a,=range(10)

Ось ще кілька застосувань.

Отримання останнього елемента зі списку

a=L[-1]
*_,a=L

У деяких ситуаціях це також можна використовувати для отримання першого елемента для збереження на паронах:

a=(L+[1])[0]
a,*_=L+[1]

Призначення порожнього списку та інших змінних

a=1;b=2;c=[]
a,b,*c=1,2

Видалення першого або останнього елемента непустого списку

_,*L=L
*L,_=L

Вони коротші, ніж альтернативи L=L[1:]та L.pop(). Результат також можна зберегти в іншому списку.

Поради люб’язно надані @grc


Оце Так! Я писав a=1;L=[]так багато разів. Дивно, що ви можете зберегти символи на чомусь такому прямолінійному, як це.
xnor

@xnor Це дякує grc. З одним лише іншим елементом це не так добре ( a,*L=1,), але він все-таки зберігає один шар :)
Sp3000

не забувайте, ви також можете отримати як перший, так і останній елемент спискуa,*_,b=L
Cyoce

36

встановити літерали в Python2.7

Ви можете писати такі набори, як це. S={1,2,3}Це також означає, що ви можете перевірити на членство, використовуючи {e}&Sзамість e in Sних збереження одного символу.


4
І це також рятує персонажа в ifs, оскільки немає пробілів ( if{e}&S:)
Artyer

1
Зверніть увагу , що ви можете замінити not inна {e}-Sз цим трюком
Black Owl KAI

35

Протягом століть мене це непокоїло, що я не міг придумати короткий спосіб отримати весь алфавіт. Якщо ви використовуєте rangeдостатньо, що R=rangeварто мати у своїй програмі, то

[chr(i+97)for i in R(26)]

коротший, ніж наївний

'abcdefghijklmnopqrstuvwxyz'

, але в іншому випадку це більше одного символу. Мене переслідувало, що розумний, який вимагав певних знань про значення ascii, виявився більш багатослівним, ніж просто набравши всі літери.

Поки я не побачив цю відповідь за алфавітом моєї дочки . Я не можу досить добре відслідковувати історію редагування, щоб зрозуміти, чи був цей геній роботою ОП чи це була пропозиція коментодавця, але це (я вважаю) найкоротший спосіб створити ітерабельний з 26 листів в римському алфавіті.

map(chr,range(97,123))

Якщо справа не має значення, ви можете зняти інший символ, використовуючи великі регістри:

map(chr,range(65,91))

Я mapзанадто багато використовую, не знаю, як це мені ніколи не приходило в голову.


4
Можна використовувати це у фактичному кодуванні, я відчуваю себе таким дурним, коли жорстке кодування цих речей: ')
ToonAlfrink

37
У фактичному кодуванні використовуйте string.lowercase- ось для чого це потрібно.
Кевін S

1
якщо вам потрібні обидва випадки, найкоротший шлях, який я знаю, - це фільтр (str.isalpha, map (chr, range (256))). Це лише ледь коротше, ніж s = map (chr, range (256)); s + = map (str.lower, s)
квінтопія

@quintopia: Чому 256 замість 122 ( ord('z'))? Крім того, що він однакової довжини ... Також, якщо вам потрібні буквено-цифрові характеристики, замініть str.isalphaу версії @ quintopia на str.isalnum. (Але якщо вам потрібен лише один випадок, весь рядок з 36 символів не довший filter(str.isalnum,map(chr,range(90))).)
Тім Педерік

2
Якщо ви будете несправедливими і використовуєте діапазон, як R, моя версія є коротшою, ніж ваша початкова: '%c'*26%tuple(R(97,123))(лише 24 символи), якщо ви пишете, rangeце так само, як алфавіт - велика версія коротша
JBernardo

32

Хоча у python не є операторів перемикання, ви можете імітувати їх словниками. Наприклад, якщо ви хотіли такий перемикач:

switch (a):
    case 1:
        runThisCode()
        break
    case 2:
        runThisOtherCode()
        break
    case 3:
        runThisOtherOtherCode()
        break

Ви можете використовувати ifоператори, або ви можете використовувати це:

exec{1:"runThisCode()",2:"runThisOtherCode()",3:"runThisOtherOtherCode()"}[a]

або це:

{1:runThisCode,2:runThisOtherCode,3:runThisOtherOtherCode}[a]()

що краще, якщо всі кодові шляхи - це функції з однаковими параметрами.

Для підтримки значення за замовчуванням зробіть це:

exec{1:"runThisCode()"}.get(a,"defaultCode()")

(або це :)

­­{1:runThisCode}.get(a,defaultCode)()

Ще однією перевагою цього є те, що якщо у вас є надлишки, ви можете просто додати їх після закінчення словника:

exec{'key1':'code','key2':'code'}[key]+';codeThatWillAlwaysExecute'

І якщо ви просто хотіли використовувати перемикач, щоб повернути значення:

def getValue(key):
    if key=='blah':return 1
    if key=='foo':return 2
    if key=='bar':return 3
    return 4

Ви можете просто зробити це:

getValue=lambda key:{'blah':1,'foo':2,'bar',3}.get(key,4)

2
Це те, що я б глибоко розглядав, як використовувати в дикій природі. Я так пропускаю свої заяви про переключення! +1
HalosGhost

1
Хоча замість використання словника з пронумерованими клавішами в першому прикладі, вам слід просто скористатися списком
Cyoce

1
Якщо у вас є рядки в якості ключів, використовуйте dict(s1=v1,s2=v2,...,sn=vn)замість {'s1':v1,'s2':v2,...,'sn':vn}збереження 2 * n-4 байти і краще, якщо n> = 3
Чорна сова Кай

31

Якщо у вас є два булевих значення, aі b, якщо ви хочете з’ясувати, чи є і те, aі інше b, використовуйте *замість and:

if a and b: #7 chars

проти

if a*b: #3 chars

якщо будь-яке значення хибне, воно буде оцінюватися як 0у цьому висловлюванні, а ціле значення є істинним, лише якщо воно не є нульовим.


9
Або ви могли б використовувати &: a=b=False,a&b
ɐɔıʇǝɥʇuʎs

3
використовувати +для orякщо ви можете гарантуватиa != -b
undergroundmonorail

2
|працює у будь-яких ситуаціях.
КалькуляторFeline

1
*замість and/ &&зберігає деякі байти багатьма мовами.
wastl

26

Використовуйте представлення рядків Python 2

Python 2 дозволяє перетворити об'єкт xу його рядкове представлення `x`вартістю лише 2 символи. Використовуйте це для завдань, які легше виконувати на рядку об'єкта, ніж сам об'єкт.

Приєднуйтесь до персонажів

Давши список символів l=['a','b','c'], можна створити ''.join(l)як `l`[2::5], що зберігає байт.

Причина в тому, що `l`є "['a', 'b', 'c']"(з пробілами), тому можна витягувати букви з фрагментом списку, починаючи з другого символу з нульовим індексом aі беручи звідти кожен п'ятий символ. Це не працює, щоб з'єднати рядки з декількома символами або втечі символів, представлених як '\n'.

Об'єднайте цифри

Аналогічно, даючи не порожній список цифр типу l=[0,3,5], можна об'єднати їх у рядок '035'як `l`[1::3].

Це дозволяє економити щось подібне map(str,l). Зауважте, що вони повинні бути одноцифровими і не можуть мати плавців, як 1.0змішані. Також це не вдається в порожньому списку, створюючи ].

Перевірте наявність негативів

Тепер для нетрудового завдання. Припустимо, у вас є список lреальних чисел і хочете перевірити, чи містить він будь-які від’ємні числа, що створює булеву форму.

Ви можете зробити

'-'in`l`

який перевіряє наявність від'ємного знаку в рядку повтор. Це коротше, ніж будь-яке

any(x<0for x in l)
min(l+[0])<0   

По-друге, min(l)<0в порожньому списку не вдасться, тому вам доведеться хеджувати.


Об’єднання одноцифрових нарізань рядків також ефективно в Python 3, хоча і менше: str(l)[2::5]12 байт проти 19 для ''.join(map(str,l)). Фактична ситуація, коли ця ситуація виникла (де lбула заява генератора, а не список), врятувала мене лише одним байтом ... що все ще варто!
Тім Педерік

25

Функцію однієї лінії можна виконати за допомогою лямбда:

def c(a):
  if a < 3: return a+10
  else: return a-5

можна перетворити на (зауважте, що немає місця 3andта 10or)

c=lambda a:a<3and a+10or a-5

21
або c=lambda a:a+[-5,10][a<3]. і / або хитрість корисніша, коли ви
залежите від

3
У вашій функції else: може бути відкинуто, як returnзупиняє виконання функції, тому все, що випливає, виконується лише в тому випадку, коли ifумова не вдалася, але якщо elseумова справжня. Таким чином elseможна сміливо оминути. (Пояснюється докладно для неофітів там)
JeromeJ

c (-10) повертає -15, тоді як він повинен повернути 0
Анвіт

абоc=lambda a:a-5+15*(a<3)
JayXon

25

петлі до 4-х елементів можуть бути краще поставити кортеж, а не використовувати діапазон

for x in 0,1,2:

проти

for x in range(3):

24

Стеля і підлога

Якщо ви хочете отримати результат округлення для поділу, як і //для підлоги, ви можете використовувати math.ceil(3/2)на 15 або набагато коротше -(-3//2)на 8 байт.

math.floor(n)   : 13 bytes+12 for import
n//1            : 4  bytes

math.ceil(n)    : 12 bytes+12 for import
-(-n//1)        : 8  bytes

5
Це врятувало мене близько 20 байт, дякую!
Морган Трапп

1
іноді ви можете піти n//1+1замість ceil, але це означає ceil (n) = n + 1, але він повинен працювати для всіх не цілих значень
fejfo

round(x)є (x+.5)//1, +1 байт, але останній починається з символу a (, і якщо xце сума, що складається з постійної величини, це може бути корисно.
користувач202729

23

Використовуйте +=замість appendіextend

A.append(B)  

можна скоротити до:

A+=B,

B,тут створюється одноелементний кортеж, який можна використовувати для розширення так Aсамо, як [B]у A+=[B].


A.extend(B)

можна скоротити до:

A+=B

5
У багатьох (але не у всіх) випадках, return 0або return 1еквівалентно return Falseабо return True.
підземниймонорельс

5
(1) працює лише в тому випадку, якщо ви вже знаєте, що число від’ємне, і тоді ви можете зберегти ще два символи, просто скориставшись знаком мінус. -xа не x*-1. --8.32а не -8.32*-1. Або просто 8.32...
трихоплакс

Цитування ОП: Будь ласка, опублікуйте одну пораду за кожну відповідь.
nyuszika7h

Зауважимо, що в A+=B B- a tuple.
Ерік Аутгольфер

23

Вибір одного з двох чисел на основі умови

Ви вже знаєте використовувати вибір списку [x,y][b]з булевим bдля потрійного виразу y if b else x. Змінні x, yі bтакож можуть бути виразами, хоча зауважте, що обидва xі yоцінюються, навіть якщо вони не обрані.

Ось деякі потенційні оптимізації, коли xі yчисел.

  • [0,y][b] -> y*b
  • [1,y][b] -> y**b
  • [x,1][b] -> b or x
  • [x,x+1][b] -> x+b
  • [x,x-1][b] -> x-b
  • [1,-1][b] -> 1|-b
  • [x,~x][b] -> x^-b
  • [x,y][b] -> x+z*b(або y-z*b), де z = yx.

Ви також можете переключитися, xі, yякщо можете bзамість цього переписати, це його заперечення.


22

Використовуйте ~ для індексації із зворотного боку списку

Якщо Lце список, використовуйте L[~i]для отримання i'-го елемента зі зворотного боку.

Це i'-вий елемент реверсу L. Бітове доповнення ~iдорівнює -i-1, і таким чином виправляє помилку "за одним" L[-i].


21

PEP448 - Додаткові узагальнення розпакування

З випуском Python 3.5 маніпуляції зі списками, кортежами, наборами та диктами щойно отримали гольфіст.

Перетворення ітерабельного в набір / список

Порівняйте пари:

set(T)
{*T}

list(T)
[*T]

tuple(T)
(*T,)

Набагато коротше! Однак зауважте, що якщо ви просто хочете щось перетворити у список і призначити його змінній, звичайне розширене ітерабельне розпакування коротше:

L=[*T]
*L,=T

Подібний синтаксис працює для кортежів:

T=*L,

що як би розширене ітерабельне розпакування, але зірочкою та комою з іншого боку.

Приєднання до списків / кортежів

Розпакування трохи коротше, ніж з'єднання, якщо вам потрібно додати список / кортеж в обидві сторони:

[1]+T+[2]
[1,*T,2]

(1,)+T+(2,)
(1,*T,2)

Друк вмісту кількох списків

Це не обмежується print, але, безумовно, звідки вийде більша частина пробігу. PEP448 тепер дозволяє проводити багаторазове розпакування, наприклад:

>>> T = (1, 2, 3)
>>> L = [4, 5, 6]
>>> print(*T,*L)
1 2 3 4 5 6

Оновлення декількох елементів словника

Це, мабуть, трапляється не дуже часто, але синтаксис можна використовувати для економії на оновлення словників, якщо ви оновлюєте щонайменше три елементи:

d[0]=1;d[1]=3;d[2]=5
d={**d,0:1,1:3,2:5}

Це в основному заперечує будь-яку потребу dict.update.


6
Це виглядає гірше, ніж Perl, але це працює ...
Mega Man

20

Змінити import *наimport*


Якщо ви ще не чули, import*зберігає символи!

from math import*

на 1 символ більше, ніж import math as mви можете видалити всі екземпляриm.

Навіть одноразове використання - це економія!


19
>>> for i in range(x):s+=input()

якщо значення i є марним:

>>> for i in[0]*x:s+=input()

або

>>> exec's+=input();'*x

8
Ви можете зробити другий приклад for i in[0]*x:s+=input()для економії іншого місця. Крім того, ви можете видалити пробіл між виконанням та першою лапкою, яку ви отримаєтеexec's+=input();'*x
Джастін Піл,

не повинен бути другий рядок:for i in[0]*x:s+=input()
micsthepick

Dupe (новіше, але більше оновлень)
користувач202729
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.