代码之家  ›  专栏  ›  技术社区  ›  nemesisdesign

如何动态调用Python函数

  •  43
  • nemesisdesign  · 技术社区  · 15 年前

    我有这个密码:

    fields = ['name','email']
    
    def clean_name():
        pass
    
    def clean_email():
        pass
    

    我怎么打电话 clean_name() clean_email() 动态的?

    例如:

    for field in fields:
        clean_{field}()
    

    我使用了花括号,因为这是我在PHP中使用的方法,但显然不起作用。

    如何使用Python实现这一点?

    9 回复  |  直到 9 年前
        1
  •  59
  •   the Tin Man    9 年前

    如果不想使用 globals, vars 如果不希望单独的模块和/或类封装要动态调用的函数,可以将它们作为当前模块的属性调用:

    import sys
    ...
    getattr(sys.modules[__name__], "clean_%s" % fieldname)()
    
        2
  •  35
  •   Cthulhon    9 年前

    使用 global 是一种非常非常糟糕的方式。你应该这样做:

    fields = {'name':clean_name,'email':clean_email}
    
    for key in fields:
        fields[key]()
    

    将函数映射到字典中的值。

    同时使用 vars()[] 也错了。

        3
  •  34
  •   the Tin Man    9 年前

    最好有一本这种功能的词典,而不是去查 globals() .

    通常的方法是编写具有以下函数的类:

    class Cleaner(object):
        def clean_name(self):
            pass
    

    然后使用 getattr 要访问它们:

    cleaner = Cleaner()
    for f in fields:
        getattr(cleaner, 'clean_%s' % f)()
    

    你甚至可以更进一步,做这样的事情:

    class Cleaner(object):
        def __init__(self, fields):
            self.fields = fields
    
        def clean(self):
            for f in self.fields:
                getattr(self, 'clean_%s' % f)()
    

    然后继承它并声明 clean_<name> 继承类上的方法:

    cleaner = Cleaner(['one', 'two'])
    cleaner.clean()
    

    实际上,这可以进一步扩展,使它更干净。第一步可能是添加支票 hasattr() 如果这种方法存在于你的课堂上。

        4
  •  7
  •   Magnus Hoff    15 年前

    globals() 会给你一个 dict 全局命名空间的。从中可以获得所需的功能:

    f = globals()["clean_%s" % field]
    

    那就叫它:

    f()
    
        5
  •  5
  •   TheHowlingHoaschd    7 年前

    我已经遇到这个问题两次了,终于想出了一个 安全的 不丑 解决办法(依我看)。

    扼要重述 以前的答案:

    全局变量 是一种老套、快速且简单的方法,但必须与函数名保持超级一致,如果变量被覆盖,则在运行时可能会中断。而且它是非蟒蛇,不安全,不道德,雅达雅达。。。

    辞典 (即字符串到函数的映射)更安全且易于使用。。。但这让我一直很恼火,我不得不把字典作业分散到我的文件中,这些作业很容易丢失。

    装饰工 把字典里的答案拼在一起。Decorators是将副作用转换附加到函数定义的一种很好的方法。

    示例时间

    fields = ['name', 'email', 'address']
    
    # set up our function dictionary
    cleaners = {}
    
    # this function will add stuff into the dictionary
    def add_cleaner(key):
        # this is a parametered decorator, it returns the actual decorator
    
        def actual_decorator(func):
            # add func to the dictionary
            cleaners[key] = func
            return func
    
        return actual_decorator
    

    每当您定义一个cleaner函数时,请将其添加到声明中:

    @add_cleaner('email')
    def email_cleaner(email):
        #do stuff here
        return result
    

    函数一经解析就被添加到字典中,并可以如下调用:

    cleaned_email = cleaners['email'](some_email)
    

    您可能需要在脚本的底部添加这一行,以确保没有忘记其中一行。;)

    assert(set(cleaners.keys()).issubset(fields))
    
        6
  •  4
  •   the Tin Man    9 年前

    另一种方法是:

    myscript.py:

    def f1():
        print 'f1'
    
    def f2():
        print 'f2'
    
    def f3():
        print 'f3'
    

    测试.py:

    import myscript
    
    for i in range(1, 4):
        getattr(myscript, 'f%d' % i)()
    
        7
  •  2
  •   Matt Joiner    15 年前
    for field in fields:
        vars()['clean_' + field]()
    
        8
  •  2
  •   Spacedman    15 年前

    另一种方法是:定义函数,然后用名称作为键定义dict:

    >>> z=[clean_email, clean_name]
    >>> z={"email": clean_email, "name":clean_name}
    >>> z['email']()
    >>> z['name']()
    

    然后把名字作为键循环。

    或者这个怎么样?构造一个字符串并使用“eval”:

    >>> field = "email"
    >>> f="clean_"+field+"()"
    >>> eval(f)
    

    然后循环并构造用于eval的字符串。

    请注意,任何需要构造字符串进行计算的方法都被视为笨拙的。

        9
  •  1
  •   martineau    15 年前

    我会使用一个字典,将字段名映射到清理函数。如果某些字段没有相应的清除功能,则 for 通过为这些情况提供某种默认函数,可以保持循环处理的简单性。我的意思是:

    fields = ['name', 'email', 'subject']
    
    def clean_name():
        pass
    def clean_email():
        pass
    
    # (one-time) field to cleaning-function map construction
    def get_clean_func(field):
        try:
            return eval('clean_'+field)
        except NameError:
            return lambda: None  # do nothing
    clean = dict((field, get_clean_func(field)) for field in fields)
    
    # sample usage
    for field in fields:
        clean[field]()
    

    上面的代码通过确定一个名为 clean_<field> 存在于命名为 fields 列表。您可能只需要执行一次,因为只要字段列表或可用的清理函数没有更改,它就会保持不变。