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

时间:2018-09-10 14:08:41

标签: python python-3.x lambda python-import

我编写了一个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'是一个常量,但是在我的某些代码中,我被迫基于另一个变量查找类变量,因此需要使用评估功能。

2 个答案:

答案 0 :(得分:3)

抱歉,您做错了

作为一般规则,当您输入eval()exec时,您做错了(这些“功能”的实际用例非常罕见,我二十年来从未用过) )。大多数python语句都是可执行的(它们在运行时执行),并且是内置于stdlib或stdlib中的功能的语法糖。

对于您而言,此处的正确解决方案是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

答案 1 :(得分:2)

此问题的具体原因取决于您在函数内部调用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]它们存储在固定长度的数组中,并且每个局部变量的索引都是在函数编译时计算的。函数没有具备可变局部变量的功能-它们没有存储它们的位置,也没有办法知道如何检索它们。