print('Hello World!') 在 python 的代码中不使用 'e' 也不使用 'p'

print('Hello World!') not using 'e' nor 'p' in the code with python

这个问题可能看起来很傻。 这是一个简单无用的任务,但我相信这是学习东西的好方法(我在做这种技巧时确实学到了很多东西)。

所以,想法在标题中: 不允许 'e' => 不允许 eval() 或 exec() 不允许 'p' => 不允许 print() 或 import

我尝试的是从以下位置获取打印功能:

dir(__builtins__)[133]

但它是作为字符串返回的,我找不到将其作为可调用函数获取的方法。

由于内置字典的顺序是随机的,这不是一件很有用的事情,除非你像调用 print 一样快乐地调用 staticmethod

但了解如何从那里进入您想要达到的下一步可能是值得的。

模块只是对象,就像任何其他对象一样。所以,你可以用 getattr 得到他们的属性。好的,这违反了您的规则。但是大多数对象(包括模块)都将它们的属性存储在字典中。所以:

>>> dir(__builtins__)[133]
'staticmethod'
>>> __builtins__.__dict__[_]
<type 'staticmethod'>

有什么方法可以让我们真正得到 print?好吧,也许不能保证,但非常接近:

>>> [v for k, v in __builtins__.__dict__.items() if 'rint' in k]
[('print', <function print>)]

糟糕,我用 e 来表示 items,对吧?罚款:

>>> [__builtins__.__dict__[k] for k in __builtins__.__dict__ if 'rint' in k]
[<function print>)]
>>> [__builtins__.__dict__[k] for k in __builtins__.__dict__ if 'rint' in k][0]('zzz')
zzz

但与此同时,您打算如何构建没有 e 的字符串 'Hello World!'?有很多选项——\x\u 转义,或者像 rot13 这样非常愚蠢的东西。所有这些选项都可以让您轻松获得字符串 'print'。所以,我不确定你为什么首先要从模块名称中提取 print

>>> __builtins__.__dict__['\x70rint']
<function print>

正如 John Anderson 在评论中指出的那样,使用 inspect 模块比使用 __dict__ 更好。一方面,它适用于在别处存储属性的对象——甚至是使用自定义 __dir____getattr__.

动态生成它们的对象
>>> [v for k, v in inspect.getmembers(__builtins__) if k == 'print'][0]
<function print>

问题是,我们如何获取inspect模块呢?我们可以使用相同的backslash/etc。很容易获得名称 'inspect' 的技巧,但是(除非我们可以假设它已经导入到 sys.modules——并且 sys 也已经导入),我们需要 import__import__ 或使用 importlib 或……它们都有一个 p。然后,要到达 getmembers,我们需要通过 inspect 模块的字典来按名称查找它。

但是,也许一次比每次都好:

>>> i = __builtins__.__dict__['__im\x70ort__']
>>> ins = i('ins\x70\x65ct')
>>> gm = ins.__dict__['g\x65tm\x65mb\x65rs']
>>> builty = lambda nam: dict(gm(__builtins__))[nam]
>>> builty('\x70rint')
<function print>
>>> builty('\x65val')
<function eval>

当然,使用 lambda 在语句中创建命名函数而不是在表达式中创建匿名函数是一种反模式,但是整个 post 就是一堆反模式模式。我在这里这样做是为了避免需要 defreturn,它们都有 e(正如 Elliot Frisch 所指出的)。 (很难注意到您键入的所有 e;甚至 Gadsby 中也有三个意外的 e,而且它以没有 e 的小说而闻名……)


我敢肯定有人希望我会展示如何通过使用编译的字节码文字来解决这个问题,对吗?

不幸的是,只有两种方法可以 运行 一个代码对象:要么将它包装在一个函数对象中——这需要获得 FunctionType,这需要一个 import 或者给 type 的电话——或者你 execeval 它——已经结束了。

此外,在 Python 3 中,您要做的就是将常量 "print""Hello, World" 填入 co_namesco_consts,以及那么它只是一个 LOAD_NAMELOAD_CONSTCALL_FUNCTIONRETURN_VALUE(巧合的是,它恰好以 e: b'e\x00d\x00\x83\x01S\x00' 开头)。

在Python 2中,另一方面,print是一个语句,并且有一个特殊的PRINT_ITEM字节码,所以它可能更有趣一点:

>>> ty = builty('typ\x65')
>>> gattr = builty('g\x65tattr')
>>> f = lambda: None
>>> c = gattr(f, 'func_cod\x65')
>>> ct = ty(c)
>>> cc = ct(0, 0, 1, 0x43, 'd\x00\x00\x04GS', ('H\x65llo World!',),
...         (), (), '', 'h\x65llo', 0, '')
>>> xval = builty('\x65val')
>>> xval(cc)
Hello World!
'Hello World!'

(最后多出来的引号输出是因为懒得去挖None所以刚返回"Hello World!",代码是LOAD_CONST 0; DUP_TOP; PRINT_ITEM; RETURN_VALUE。)