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 给出了您实例中的正确解决方案。但是,要使 exec
和 eval
以可靠的方式工作,您应该尽可能提供全局变量和局部变量以供它们使用。它们可以是您想要的任何词典,而不仅仅是您上下文中的全局词典或本地词典。
例如
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] 它们存储在一个固定长度的数组中,每个局部变量的索引是在函数编译时计算的。函数不具备可变的局部变量——它们没有存储它们的地方,也没有办法知道如何检索它们。
我编写了一个 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 给出了您实例中的正确解决方案。但是,要使 exec
和 eval
以可靠的方式工作,您应该尽可能提供全局变量和局部变量以供它们使用。它们可以是您想要的任何词典,而不仅仅是您上下文中的全局词典或本地词典。
例如
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] 它们存储在一个固定长度的数组中,每个局部变量的索引是在函数编译时计算的。函数不具备可变的局部变量——它们没有存储它们的地方,也没有办法知道如何检索它们。