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()