Lambda 函数无法访问调用函数的导入?

Lambda function cannot access import of calling function?

我编写了一个 lambda 函数来处理对动态加载库的调用,但我似乎遇到了与导入相关的命名空间问题。我的测试用例如下:

def dot(*args):
    ''' return a dot notation string
    '''
    return '.'.join(map(str, args))


def test(import_name, classname):
    exec('import ' + import_name)
    get_global = lambda glob: eval(dot(import_name, classname, glob))
    technodename = get_global('technodename')
    metalstack = get_global('metalstack')
    return technodename, metalstack

print(test('PROJECT', 'Quabanatu'))

执行lambda函数时出现的错误是:

NameError: name 'PROJECT' is not defined

但是如果我执行:

    technodename = eval(dot(import_name, classname, 'metalstack' ))

而不是调用 get_global 它工作得很好。此外,如果我在程序开始时导入 PROJECT 库,lambda 函数工作正常。我错过了什么?

顺便说一句,我知道在这种情况下这不是最好的方法,因为 'technodname' 是一个常量,但在我的某些代码中,我不得不根据以下条件查找 class 变量另一个变量因此需要使用 eval 函数。

抱歉但是:你做错了

作为一般规则,当您输入 eval()exec 时,您就做错了("features" 的实际用例非常罕见,我从来不需要20 年后)。大多数 python 语句是可执行的(它们在运行时执行)并且是作为内置函数或标准库的一部分公开的功能的语法糖。

对于您的情况,这里的正确解决方案是 import_lib.import_module(module_name)getattr(obj, attrname):

import importlib

def get_global(module, *names):
   obj = module
   for name in names:
       obj = getattr(obj, name)
   return obj

def test(module_name, classname):
    module = importlib.import_module(module_name)
    technodename = get_global(module, classname, "technodename")
    metalstack = get_global(module, classname, "metalstack")
    return technodename, metalstack

这个问题的具体原因在于您在函数内部调用 exec 的方式。当 exec 以单参数形式使用时,它使用调用上下文的局部变量。这对模块和 类 很好,但在函数内部会变得复杂。 exec 使用存储在 dict 中的局部变量(模块和 类 也是如此)。但是,函数局部变量的存储方式不同 [1]。因此 exec 必须创建函数局部变量的字典视图并使用它。这意味着对此字典的任何更改都不会反映在函数的实际局部变量中。

def f():
    # NB. do not rely on any behaviour that results from mutating the dict returned by locals
    x = 'value' 
    assert locals()['x'] == 'value'
    assert 'builtins' not in locals()
    exec('import builtins')
    try:
        builtins
    except NameError as e:
        print(e)
    else:
        assert False
    print(locals()['builtins'])

# prints
# name 'builtins' is not defined
# <module 'builtins' (built-in)>

bruno desthuilliers 给出了您实例中的正确解决方案。但是,要使 execeval 以可靠的方式工作,您应该尽可能提供全局变量和局部变量以供它们使用。它们可以是您想要的任何词典,而不仅仅是您上下文中的全局词典或本地词典。

例如

def g(module_name, attr):
    locals_ = {}
    globals_ = {}
    # two-arg form where locals and globals are the same
    exec('import ' + module_name, globals_)
    assert module_name not in locals_
    assert module_name in globals_
    exec('result = {}.{}'.format(module_name, attr), globals_, locals_)
    assert 'result' in locals_
    assert 'result' not in globals_
    return eval('{}.{}'.format(module_name, attr), globals_)

assert g('builtins', 'str') is str

[1] 它们存储在一个固定长度的数组中,每个局部变量的索引是在函数编译时计算的。函数不具备可变的局部变量——它们没有存储它们的地方,也没有办法知道如何检索它们。