[Из песочницы] Разгадываем картинку из твиттера компании Intel
Имеется страшилка, обладающая невероятным количеством подчеркиваний, лямбд и чрезвычайно редкой функцией __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) И хоть в России для дня матери отведен другой день, данному совету всё же стоит последовать.