Python 中的元编程 - 动态调用方法
MetaProgramming in Python - calling a method dynamically
是否可以使用变量作为变量名来调用函数?
示例:
def my_method(foo="bar"):
print(foo)
var = "foo='baz'"
my_method(var)
>>> baz
现在,我想不出做这种事情的方法(用变量中的值替换变量名)。
这种事情可能吗?
我知道您可以做一些与此类似的事情,例如:
def my_method(foo, bar, baz):
print(foo, bar, baz)
var = ['one','two','three']
my_method(*var)
>>> one two three
但我无法在 python 中为我可能需要的任何元编程找到统一的通用解决方案。有吗?也许该语言无法提供通用的元编程解决方案。
您可以为 exec
提供一个字典,它将在其中存储变量,然后将其解包为函数的关键字参数。
def my_method(foo="bar"):
print(foo)
var_a = "foo='baz'"
kwargs = {}
# See safety note at the bottom of this answer.
exec(var_a, {'__builtins__': {}}, kwargs)
my_method(**kwargs )
# prints: 'baz'
您甚至可以使用装饰器将这种行为赋予函数。
def kwargs_as_string(f):
def wrapper(string, **more_kwargs):
kwargs = {}
# See safety note at the bottom of this answer.
exec(string, {'__builtins__': {}}, kwargs)
return f(**kwargs, **more_kwargs)
return wrapper
@kwargs_as_string
def my_method(foo="bar"):
print(foo)
my_method("foo='baz'")
# prints: 'baz'
安全注意事项
为了安全起见,我们为 exec
提供一个空的全局 __builtins__
,否则将在该键下插入对 built-in 模块字典的引用。这可能会导致麻烦。
var_a = '__import__("sys").stdout.write("You are in trouble")'
exec(var_a, {}, {})
# prints: You are in trouble
exec(var_a, {'__builtins__': {}}, {})
# raises a NameError: name '__import__' is not defined
假设您可以使用 JSON 格式化字符串...
import json
args = json.loads(
"""
{
"kwargs": {
"a": 2,
"b": 1
},
"args": [3, 4]
}
""")
def foo(a, b):
print("a: {}".format(a))
print("b: {}".format(b))
foo(**args["kwargs"])
foo(*args["args"])
# output:
# a: 2
# b: 1
# a: 3
# b: 4
我找到了这三个选项,最后一个对我来说最有用。
def foo():
def bar(baz):
print('dynamically called bar method using', baz)
packages = {'bar': bar}
getattr(packages['bar'], "__call__")('getattr with map')
packages['bar']('just the map')
locals()['bar']('just locals()')
foo()
python test_dynamic_calling.py
dynamically called bar method using getattr with map
dynamically called bar method using just the map
dynamically called bar method using just locals()
是否可以使用变量作为变量名来调用函数? 示例:
def my_method(foo="bar"):
print(foo)
var = "foo='baz'"
my_method(var)
>>> baz
现在,我想不出做这种事情的方法(用变量中的值替换变量名)。
这种事情可能吗?
我知道您可以做一些与此类似的事情,例如:
def my_method(foo, bar, baz):
print(foo, bar, baz)
var = ['one','two','three']
my_method(*var)
>>> one two three
但我无法在 python 中为我可能需要的任何元编程找到统一的通用解决方案。有吗?也许该语言无法提供通用的元编程解决方案。
您可以为 exec
提供一个字典,它将在其中存储变量,然后将其解包为函数的关键字参数。
def my_method(foo="bar"):
print(foo)
var_a = "foo='baz'"
kwargs = {}
# See safety note at the bottom of this answer.
exec(var_a, {'__builtins__': {}}, kwargs)
my_method(**kwargs )
# prints: 'baz'
您甚至可以使用装饰器将这种行为赋予函数。
def kwargs_as_string(f):
def wrapper(string, **more_kwargs):
kwargs = {}
# See safety note at the bottom of this answer.
exec(string, {'__builtins__': {}}, kwargs)
return f(**kwargs, **more_kwargs)
return wrapper
@kwargs_as_string
def my_method(foo="bar"):
print(foo)
my_method("foo='baz'")
# prints: 'baz'
安全注意事项
为了安全起见,我们为 exec
提供一个空的全局 __builtins__
,否则将在该键下插入对 built-in 模块字典的引用。这可能会导致麻烦。
var_a = '__import__("sys").stdout.write("You are in trouble")'
exec(var_a, {}, {})
# prints: You are in trouble
exec(var_a, {'__builtins__': {}}, {})
# raises a NameError: name '__import__' is not defined
假设您可以使用 JSON 格式化字符串...
import json
args = json.loads(
"""
{
"kwargs": {
"a": 2,
"b": 1
},
"args": [3, 4]
}
""")
def foo(a, b):
print("a: {}".format(a))
print("b: {}".format(b))
foo(**args["kwargs"])
foo(*args["args"])
# output:
# a: 2
# b: 1
# a: 3
# b: 4
我找到了这三个选项,最后一个对我来说最有用。
def foo():
def bar(baz):
print('dynamically called bar method using', baz)
packages = {'bar': bar}
getattr(packages['bar'], "__call__")('getattr with map')
packages['bar']('just the map')
locals()['bar']('just locals()')
foo()
python test_dynamic_calling.py
dynamically called bar method using getattr with map
dynamically called bar method using just the map
dynamically called bar method using just locals()