49

我需要在运行时为方法生成代码。能够运行任意代码并拥有文档字符串非常重要。

我想出了一个结合exec和的解决方案setattr,这是一个虚拟示例:

class Viking(object):
    def __init__(self):
        code = '''
            def dynamo(self, arg):
                """ dynamo's a dynamic method!
                """
                self.weight += 1
                return arg * self.weight
            '''
        self.weight = 50

        d = {}
        exec code.strip() in d
        setattr(self.__class__, 'dynamo', d['dynamo'])


if __name__ == "__main__":
    v = Viking()
    print v.dynamo(10)
    print v.dynamo(10)
    print v.dynamo.__doc__

是否有更好/更安全/更惯用的方法来实现相同的结果?

4

6 回答 6

80

基于 Theran 的代码,但将其扩展到类上的方法:



class Dynamo(object):
    pass

def add_dynamo(cls,i):
    def innerdynamo(self):
        print "in dynamo %d" % i
    innerdynamo.__doc__ = "docstring for dynamo%d" % i
    innerdynamo.__name__ = "dynamo%d" % i
    setattr(cls,innerdynamo.__name__,innerdynamo)

for i in range(2):
    add_dynamo(Dynamo, i)

d=Dynamo()
d.dynamo0()
d.dynamo1()


哪个应该打印:


in dynamo 0
in dynamo 1

于 2009-02-10T22:46:31.380 回答
13

函数文档字符串和名称是可变属性。你可以在内部函数中做任何你想做的事情,甚至可以有多个版本的内部函数供 makedynamo() 选择。无需使用字符串构建任何代码。

这是解释器的一个片段:

>>> def makedynamo(i):
...     def innerdynamo():
...         print "in dynamo %d" % i
...     innerdynamo.__doc__ = "docstring for dynamo%d" % i
...     innerdynamo.__name__ = "dynamo%d" % i
...     return innerdynamo

>>> dynamo10 = makedynamo(10)
>>> help(dynamo10)
Help on function dynamo10 in module __main__:

dynamo10()
    docstring for dynamo10
于 2009-02-10T18:38:47.840 回答
10

Python 将允许您在函数中声明一个函数,因此您不必做这些exec诡计。

def __init__(self):

    def dynamo(self, arg):
        """ dynamo's a dynamic method!
        """
        self.weight += 1
        return arg * self.weight
    self.weight = 50

    setattr(self.__class__, 'dynamo', dynamo)

如果你想拥有多个版本的函数,你可以将所有这些放在一个循环中,并改变你在setattr函数中命名它们的名称:

def __init__(self):

    for i in range(0,10):

        def dynamo(self, arg, i=i):
            """ dynamo's a dynamic method!
            """
            self.weight += i
            return arg * self.weight

        setattr(self.__class__, 'dynamo_'+i, dynamo)
        self.weight = 50

(我知道这不是很好的代码,但它明白了这一点)。至于设置文档字符串,我知道这是可能的,但我必须在文档中查找它。

编辑:您可以通过设置文档字符串dynamo.__doc__,因此您可以在循环体中执行以下操作:

dynamo.__doc__ = "Adds %s to the weight" % i

另一个编辑:在@eliben 和@bobince 的帮助下,应该解决关闭问题。

于 2009-02-10T18:38:43.740 回答
1
class Dynamo(object):
    def __init__(self):
        pass

    @staticmethod
    def init(initData=None):
        if initData is not None:
            dynamo= Dynamo()
            for name, value in initData.items():
                code = '''
def %s(self, *args, **kwargs):
%s
                            ''' % (name, value)
                result = {}
                exec code.strip() in result
                setattr(dynamo.__class__, name, result[name])

            return dynamo

        return None

service = Dynamo.init({'fnc1':'pass'})
service.fnc1()
于 2018-02-16T21:48:11.173 回答
0

更通用的解决方案:

您可以调用 Dummy 类实例的任何方法。文档字符串是根据方法名称生成的。通过仅返回它们来演示任何输入参数的处理。

代码

class Dummy(object):

    def _mirror(self, method, *args, **kwargs):
        """doc _mirror"""
        return args, kwargs

    def __getattr__(self, method):
        "doc __getattr__"

        def tmp(*args, **kwargs):
            """doc tmp"""
            return self._mirror(method, *args, **kwargs)
        tmp.__doc__ = (
                'generated docstring, access by {:}.__doc__'
                .format(method))
        return tmp

d = Dummy()    
print(d.test2('asd', level=0), d.test.__doc__)
print(d.whatever_method(7, 99, par=None), d.whatever_method.__doc__)

输出

(('asd',), {'level': 0}) generated docstring, access by test.__doc__
((7, 99), {'par': None}) generated docstring, access by whatever_method.__doc__
于 2020-04-09T11:46:54.050 回答
-1

请原谅我的英语不好。

我最近需要生成动态函数来绑定每个菜单项以打开 wxPython 上的特定框架。这就是我所做的。

首先,我创建了菜单项和框架之间的映射列表。

menus = [(self.menuItemFile, FileFrame), (self.menuItemEdit, EditFrame)]

映射上的第一项是菜单项,最后一项是要打开的框架。接下来,我将每个菜单项中的 wx.EVT_MENU 事件绑定到特定框架。

for menu in menus:
    f = genfunc(self, menu[1])
    self.Bind(wx.EVT_MENU, f, menu[0])

genfunc 函数是动态函数生成器,代码如下:

def genfunc(parent, form):
    def OnClick(event):
        f = form(parent)
        f.Maximize()
        f.Show()
    return OnClick
于 2010-03-29T16:51:41.610 回答