Разгадываем картинку из твиттера компании 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) 

И хоть в России для дня матери отведен другой день, данному совету всё же стоит последовать.

ссылка на оригинал статьи http://habrahabr.ru/post/257757/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *