我需要在运行时为方法生成代码。能够运行任意代码并拥有文档字符串非常重要。
我想出了一个结合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__
是否有更好/更安全/更惯用的方式来实现相同的结果?
答案 0 :(得分:70)
基于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
答案 1 :(得分:12)
函数文档字符串和名称是可变属性。你可以在内部函数中做任何你想做的事情,或者甚至有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
答案 2 :(得分:8)
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的帮助下,应该解决闭包问题。
答案 3 :(得分:0)
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()
答案 4 :(得分:0)
更通用的解决方案:
您可以调用类Dummy的实例的任何方法。 该文档字符串是基于方法名称生成的。 只需返回它们即可演示对任何输入参数的处理。
代码
user_id
输出
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__)
答案 5 :(得分:-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