Рассекая линию из (запутанных?) Питон


Я читал другой вопрос о переполнении стека (Zen of Python ), и я наткнулся на эту строку в ответе Хайме Сориано:

import this
"".join([c in this.d and this.d[c] or c for c in this.s])

Ввод выше в оболочку Python выводит:

"The Zen of Python, by Tim PetersnnBeautiful is better than ugly.nExplicit is
better than implicit.nSimple is better than complex.nComplex is better than 
complicated.nFlat is better than nested.nSparse is better than dense.
nReadability counts.nSpecial cases aren't special enough to break the rules.
nAlthough practicality beats purity.nErrors should never pass silently.
nUnless explicitly silenced.nIn the face of ambiguity, refuse the temptation to
guess.nThere should be one-- and preferably only one --obvious way to do it.
nAlthough that way may not be obvious at first unless you're Dutch.nNow is 
better than never.nAlthough never is often better than *right* now.nIf the 
implementation is hard to explain, it's a bad idea.nIf the implementation is
easy to explain, it may be a good idea.nNamespaces are one honking great idea 
-- let's do more of those!"
И поэтому, конечно, я был вынужден потратить все утро, пытаясь понять приведенный выше список... понимание... вещь. Я не решаюсь категорически объявить его запутанным, но только потому, что я программирую всего полтора месяца и поэтому не уверен, стоит ли это делать. не такие конструкции являются обычным явлением в python.

this.s содержит закодированную версию вышеупомянутой распечатки:

"Gur Mra bs Clguba, ol Gvz CrgrefnnOrnhgvshy vf orggre guna htyl.nRkcyvpvg vf orggre guna vzcyvpvg.nFvzcyr vf orggre guna pbzcyrk.nPbzcyrk vf orggre guna pbzcyvpngrq.nSyng vf orggre guna arfgrq.nFcnefr vf orggre guna qrafr.nErnqnovyvgl pbhagf.nFcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.nNygubhtu cenpgvpnyvgl orngf chevgl.nReebef fubhyq arire cnff fvyragyl.nHayrff rkcyvpvgyl fvyraprq.nVa gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.nGurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.nNygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.nAbj vf orggre guna arire.nNygubhtu arire vf bsgra orggre guna *evtug* abj.nVs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.nVs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.nAnzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"

И this.d содержит словарь с шифром, который декодирует this.s:

{'A': 'N', 'C': 'P', 'B': 'O', 'E': 'R', 'D': 'Q', 'G': 'T', 'F': 'S', 'I': 'V', 'H': 'U', 'K': 'X', 'J': 'W', 'M': 'Z', 'L': 'Y', 'O': 'B', 'N': 'A', 'Q': 'D', 'P': 'C', 'S': 'F', 'R': 'E', 'U': 'H', 'T': 'G', 'W': 'J', 'V': 'I', 'Y': 'L', 'X': 'K', 'Z': 'M', 'a': 'n', 'c': 'p', 'b': 'o', 'e': 'r', 'd': 'q', 'g': 't', 'f': 's', 'i': 'v', 'h': 'u', 'k': 'x', 'j': 'w', 'm': 'z', 'l': 'y', 'o': 'b', 'n': 'a', 'q': 'd', 'p': 'c', 's': 'f', 'r': 'e', 'u': 'h', 't': 'g', 'w': 'j', 'v': 'i', 'y': 'l', 'x': 'k', 'z': 'm'}
Насколько я могу судить, поток выполнения в коде Хайме выглядит следующим образом:
1. цикл c for c in this.s присваивает значение c
2. если оператор c in this.d принимает значение True, оператор "и" выполняет все, что находится непосредственно справа от него, в этом случае this.d[c].
3. если оператор c in this.d принимает значение False (чего никогда не происходит в коде Хайме), оператор "или" выполняет все, что находится справа от него, в данном случае цикл c for c in this.s.

Я прав насчет этого потока?

Даже если я прав насчет порядка казни, это все равно оставляет у меня массу вопросов. Почему выполняется первым, даже если код для него идет последним в строке после нескольких условных операторов? В других другими словами, почему цикл for начинает выполняться и присваивает значение, но затем только фактически возвращает значение в более поздний момент выполнения кода, если вообще возвращает?

Кроме того, для бонусных очков, что за странная строка в файле Дзен о голландцах?

Edit: хотя мне стыдно говорить это сейчас, еще три секунды назад я полагал, что Гвидо ван Россум был итальянцем. Прочитав его статью в Википедии, я, по крайней мере, понимаю, если не полностью понимаю, почему там есть эта строка.

6 8

6 ответов:

Операторы в строке понимания списка ассоциируются следующим образом:

"".join([(((c in this.d) and this.d[c]) or c) for c in this.s])

Удаление списка понимание:

result = []
for c in this.s:
   result.append(((c in this.d) and this.d[c]) or c)
print "".join(result)

Удаление and/or логический трюк, который используется для эмуляции if-else заявление:

result = []
for c in this.s:
   if c in this.d:
      result.append(this.d[c])
   else:
      result.append(c)
print "".join(result)

Вы правы насчет потока.

Цикл имеет вид [dosomething(c) for c in this.s] это понимание списка и должен быть прочитан как dosomething для всех c в этом.s.

Голландская часть-это Гвидо ван Россум, создатель Python является голландский.

Ваш анализ близок. Это понимание списка. (кстати, тот же результат будет получен, если исключить внешние квадратные скобки, которые будут называться генераторным пониманием)

Здесь есть некоторая документация .

Основной формой понимания списка является

[expression for var in enumerable if condition]

Они оцениваются в следующем порядке:

  1. перечисляемый оценивается
  2. каждое значение в свою очередь присваивается var
  3. условие есть проверено
  4. выражение вычисляется
Результатом является список значений выражения для каждого элемента в перечисляемом, для которого условие было истинным.

В этом примере не используется условие, поэтому после добавления некоторых скобок остается следующее:

[(c in this.d and this.d[c] or c) for c in (this.s)]

this.s является перечисляемым. c - это итерационная переменная. c in this.d and this.d[c] or c - это выражение.

c in this.d and this.d[c] or c использует природу короткого замыкания логических операторов python для достижения того же, что и this.d[c] if c in this.d else c.

В целом, я бы не назвал это запутанным вообще. Как только вы поймете силу понимания списка, это будет выглядеть вполне естественно.

Как правило, список понятий имеет следующий вид:

[ expression for var in iterator ]

Когда я записываю понимание списка, я часто начинаю с написания

[ for var in iterator ]
Потому что многие годы процедурного программирования внедрили в мой разум аспект for-loop как часть, которая приходит первой. И, как вы правильно заметили, цикл for-это та часть, которая, кажется, "выполняется" первой.

Для каждого прохода через цикл вычисляется выражение. (Незначительный момент: выражения являются оцениваются, выполняются операторы.)

Таким образом, в данном случае мы имеем
[ expression for c in this.s ]

Это.s-это строка. В Python строки являются итераторами! Когда вы пишете

for c in some_string:

Цикл повторяется над символами в строке. Так что c берет на себя каждый из персонажей в этом.С в порядке.

Теперь выражение

c in this.d and this.d[c] or c
Это то, что известно как троичная операция . Эта связь объясняет логику, но основная идея -
if c in this.d:
    the expression evaluates to this.d[c]
else:
    the expression evaluates c

Условие c in this.d, таким образом, просто проверить, что дикт this.d имеет ключ со значением c. Если да, то верните this.d[c], а если нет, то верните c себя.

Другой способ написать это был бы

[this.d.get(c,c) for c in this.s]

(второй аргумент метода get - это значение по умолчанию, возвращаемое, когда первый аргумент не находится в dict).

ПС. Троичная форма

condition and value1 or value2

Подвержен ошибкам. (Рассмотрим, что происходит, если condition истинно, но value1 является None. Поскольку condition истинно, вы можно было бы ожидать, что тернарная форма оценит value1, то есть None. Но так как None имеет булево значение False, то тернарная форма вместо этого принимает значение value2. Таким образом, если вы не будете внимательны и не осознаете эту ловушку, троичная форма может ввести в заблуждение.)

Для современных версий Python лучшим способом написать это было бы

value1 if condition else value2
Он не подвержен ловушке, упомянутой выше. Если condition истинно, то выражение всегда принимает значение value1.

Но в частный случай выше, я бы предпочел this.d.get(c,c).

"".join([c in this.d and this.d[c] or c for c in this.s]) конечно, это запутано. Вот дзенская версия:

this.s.decode('rot13')

Моя версия с современным if else и генератором:

import this ## prints zenofpython
print '-'*70
whatiszenofpython = "".join(this.d[c] if c in this.d else c for c in this.s)
zen = ''
for c in this.s:
    zen += this.d[c] if c in this.d else c
print zen

Вербальная версия: импортируйте это, основная программа его расшифровывает и печатает сообщение это.с Чтобы расшифровать сообщение, замените те буквы, которые находятся в диктанте.d с их декодированными встречными частями (верхний / нижний регистр разные). Остальные буквы не нужно менять, а печатать так, как они есть.