如果我使用自定义字典作为函数的全局变量,为什么我不能访问内置函数?

时间:2018-05-30 12:33:31

标签: python function global cpython

我有一个dict子类,如下所示:

class MyDict(dict):
    def __getitem__(self, name):
        return globals()[name]

此类可以与evalexec一起使用而不会出现问题:

>>> eval('bytearray', MyDict())
<class 'bytearray'>
>>> exec('print(bytearray)', MyDict())
<class 'bytearray'>

但如果我使用types.FunctionType构造函数实例化一个函数对象,该函数无法访问任何内置函数:

import types

func = lambda: bytearray
func_copy = types.FunctionType(func.__code__,
                              MyDict(),
                              func.__name__,
                              func.__defaults__,
                              func.__closure__)

print(func_copy())
# Traceback (most recent call last):
#   File "untitled.py", line 16, in <module>
#     print(func_copy())
#   File "untitled.py", line 8, in <lambda>
#     func = lambda: bytearray
# NameError: name 'bytearray' is not defined

MyDict()globals()或事件dict(globals())替换{'__builtins__': __builtins__}会使代码按预期打印<class 'bytearray'>

我不明白这个例外的来源。谁能解释这种行为?为什么它适用于eval但不适用于函数对象?

1 个答案:

答案 0 :(得分:1)

不是一个完整的答案,但似乎正在发生的事情是CPython在访问内置函数时忽略了自定义__getitem__。它似乎将MyDict视为正常(非子类)dict。如果dict中实际存在'__builtins__'键,则一切正常:

class MyDict(dict):
    def __getitem__(self, name):
        return globals()[name]


import types

globs = MyDict()
globs['__builtins__'] = __builtins__

func = lambda: bytearray
func_copy = types.FunctionType(func.__code__,
                              globs,
                              func.__name__,
                              func.__defaults__,
                              func.__closure__)

print(func_copy())
# output: <class 'bytearray'>

问题仍然是为什么这只发生在FunctionType,而不是evalexec