http://habrahabr.ru/post/257757/
Имеется страшилка, обладающая невероятным количеством подчеркиваний, лямбд и чрезвычайно редкой функцией __import__:
Что за зверь и что он делает?
Конечно же, мы можем как нормальные люди перепечатать код в интерпретатор и посмотреть, что будет. Но раз уж время давно за полночь, разбираться интереснее руками.
Код переписать всё-таки придётся. Если вы боитесь поддаться соблазну запуска — пишите лучше на бумажке.
Итак, для начала попробуем отобразить всё это без чёртового наклона, при этом постараемся (насколько это возможно) придать тексту читаемый вид:
getattr(
__import__(True.__class__.__name__[1] + [].__class__.__name__[2]),
().__class__.__eq__.__class__.__name__[:2] + ().__iter__().__class__.__name__[5:8]
)
(
1,
(
lambda _, __: _(_, __)
)(
lambda _, __: chr(__ % 256) + _(_, __ // 256) if __ else "",
882504235958091178581291885595786461602115
)
)
От PEP8 мы далеки, да и отправлять такое на код ревью пока не стоит, но уже гораздо лучше.
Имеется getattr, значит первые скобки «вернут» нам функцию, а вторые будут списком аргументов.
Первым аргументом getattr берёт объект, вторым — предполагаемый атрибут. Начнём с объекта.
Фактически, функция __import__ — это то, во что превращаются привычные нам «from X import Y as Z». Функция очень редкая и её использование в «боевой» ситуации не на каждом углу встретишь. Подробнее разобраться в её устройстве можно в
документации, мы же для ускорения процесса заявим, что в нашем случае данная функция аналогична выражению:
from True.__class__.__name__[1] + [].__class__.__name__[2]
import ().__class__.__eq__.__class__.__name__[:2] + ().__iter__().__class__.__name__[5:8])
С первой частью просто — идем по ступенькам. Под нужды True и False в питоне имеется специальный тип «bool», и именно это вернет нам цепочка __class__.__name__. Возьмем первый элемент, это будет буква «o».
Поиски второй буквы не многим сложнее — [] это список, список это «list», «list»[2] это «s».
Первая часть мини-головоломки успешно решена:
from os import ().__class__.__eq__.__class__.__name__[:2] + ().__iter__().__class__.__name__[5:8])
Разбираемся со второй частью. "()" это кортеж, т.е. tuple, __eq__ это «магическая» обёртка для оператора сравнения "==". Тут мы впервые наступим в известную субстанцию, если подумаем, что "==.__class__" это какой-нибудь «function». В действительности это «wrapper_descriptor», что никто бы и не заметил в другом случае, но здесь это очень важно. К сожалению, я не посвящен в тайну именования классов для встроенных магических методов, поэтому надеемся на её раскрытие в комментариях. Возьмем срез «wrapper_descriptor»[:2], получим «wr».
Дальше можно не разбираться, ибо модуль «os» имеет только один метод, начинающийся на «wr» и это, очевидно, write.
Разбор второй части этого слова вы можете выполнить самостоятельно, ничего сложного.
from os import write
Как мы выяснили ранее, теперь мы должны вызвать функцию write с не очень понятными аргументами.
from os import write
write(1,
( lambda _, __: _(_, __) ) (
lambda _, __: chr(__ % 256) + _(_, __ // 256) if __ else "",
882504235958091178581291885595786461602115
)
)
Первым аргументом должен идти дескриптор, в который мы собираемся писать. В нашем случае это 1, и это… stdout! Проще говоря, наш os.write будет работать как print.
Дальше происходит следующее: первая анонимная функция оборачивается в скобки, значит её вызов осуществляется здесь же.
Чтобы лучше понять что она делает, запишем её как обыкновенный метод, предварительно немного подумав над содержимым.
lambda _, __: _(_, __)
Мы принимаем два аргумента, далее происходит обращение к методу __call__ первого. Логично предположить, что первый аргумент является функцией, тогда:
def function_one(inner, argument):
return inner(inner, argument)
Не происходит ничего, кроме «проброса» аргументов к функции, передавшейся нам первым параметром. Что же это за функция?
lambda _, __: chr(__ % 256) + _(_, __ // 256) if __ else "",
Видим метод chr, следовательно, мы преобразуем цифру в символ. Перепишем по-человечески:
def function_two(inner, ordC):
if ordC:
return chr(ordC % 256) + inner(inner, ordC // 256)
else:
return ""
Внимательно посмотрев на сиё, осознаём, что происходит следующее: мы берем число, делим его с остатком на 256, остаток от деления сохраняем как символ, а частное рекурсивно передаем дальше до тех пор, пока число не станет меньше 256 (т.е. число // 256 == 0). Не так уж и хитро.
Огромное число, которое мы передаем, записано выше. Раз уж мы со всем разобрались, давайте попробуем собрать всё воедино и написать что-то подобное на человеческом питоне.
from os import write
def recursive_print(number):
if number:
write(1, chr(number % 256))
recursive_print(number // 256)
else:
return
recursive_print(882504235958091178581291885595786461602115)
И хоть в России для дня матери отведен другой день, данному совету всё же стоит последовать.