我正在使用functools.partial
来创建一个闭包,并且使用setattr
来创建可以从类实例中调用。这里的想法是在运行时创建一组方法。
#!/usr/bin/python
from functools import partial
class MyClass(object):
def __init__(self, val):
self.val = val
@classmethod
def generateMethods(self):
def dummy(conf1, self):
print "conf1:", conf1
print "self.val:", self.val
print
for s in ('dynamic_1', 'dynamic_2'):
closed = partial(dummy, s)
setattr(self, "test_{0}".format(s), closed)
在我看来,partial
会将s
的当前值绑定到dummy
的第一个arg,当这是self
时,会释放if __name__ == '__main__':
# Dynamically create some methods
MyClass.generateMethods()
# Create an instance
x = MyClass('FOO')
# The dynamically created methods aren't callable from the instance :(
#x.test_dynamic_1()
# TypeError: dummy() takes exactly 2 arguments (1 given)
# .. but these work just fine
MyClass.test_dynamic_1(x)
MyClass.test_dynamic_2(x)
从一个实例调用。
这不符合我的预期
{{1}}
是否可以动态创建闭包方法,但可以从类的实例中调用?
答案 0 :(得分:2)
问题在于,当您使用实例调用它们时,它们实际上不是绑定方法,即它们不了解实例。绑定方法在调用时自动将self
插入到底层函数的参数中,它存储在绑定方法的__self__
属性中。
因此,覆盖__getattribute__
并查看正在提取的对象是否是partial
类型的实例,如果是,则使用types.MethodType
将其转换为绑定方法。
<强>代码:强>
#!/usr/bin/python
from functools import partial
import types
class MyClass(object):
def __init__(self, val):
self.val = val
@classmethod
def generateMethods(self):
def dummy(conf1, self):
print "conf1:", conf1
print "self.val:", self.val
print
for s in ('dynamic_1', 'dynamic_2'):
closed = partial(dummy, s)
setattr(self, "test_{0}".format(s), closed)
def __getattribute__(self, attr):
# Here we do have access to the much need instance(self)
obj = object.__getattribute__(self, attr)
if isinstance(obj, partial):
return types.MethodType(obj, self, type(self))
else:
return obj
if __name__ == '__main__':
MyClass.generateMethods()
x = MyClass('FOO')
x.test_dynamic_1()
x.test_dynamic_2()
答案 1 :(得分:1)
我认为新的functools.partialmethod
适用于这个确切的用例。
直接来自文档:
>>> class Cell(object):
... def __init__(self):
... self._alive = False
... @property
... def alive(self):
... return self._alive
... def set_state(self, state):
... self._alive = bool(state)
... set_alive = partialmethod(set_state, True)
... set_dead = partialmethod(set_state, False)
...
>>> c = Cell()
>>> c.alive
False
>>> c.set_alive()
>>> c.alive
True