Python中无类方法有何用处?
我刚刚注意到,在Python中可以这样做:
def f(self):
print self.name
class A:
z=f
name="A"
class B:
z=f
name = "B"
…
print a.z()
>>> A
换句话说,f()
的行为就像是一个没有定义在任何类上的方法,但可以附加到某个类上。当然,如果它期待的对象上有一些方法或属性,但实际上并不存在,那就会出现运行时错误。
我想问的是:这样做有用吗?有什么目的?有没有什么情况可以解决问题?也许这是一种定义接口的方法?
3 个回答
有趣的事实:
在Python中,函数是一种类型:
type(f) # prints <type: function>
所以,函数是可以调用的实例,可以随处使用。
它可以让函数变得可移植,模拟即插即用的功能。
在开发过程中,这非常方便——可以尝试不同的逻辑(通过插入不同的函数)。
这提供了逻辑的抽象,因为我们可以用导入的函数替换类的方法。
不过,我对你怎么用它来模拟接口有点怀疑!
因为函数对象有一个叫做 __get__(...) 的方法,所以它被称为非数据描述符。
当你在类 A 中定义一个方法时,它其实只是 A.__dict__ 中的一个函数对象:
In [78]: class A(object):
....: def f1(self):
....: pass
....:
In [79]: A.__dict__["f1"]
Out[79]: <function f1 at 0x0D39A070>
但是当你通过实例或类来获取这个函数时,你得到的是绑定方法或未绑定方法。
In [80]: A.f1
Out[80]: <unbound method A.f1>
In [81]: A().f1
Out[81]: <bound method A.f1 of <__main__.A object at 0x0D2F1CD0>>
这就是方法的工作原理。所以,你可以在之后再添加方法:
In [82]: A.f2 = lambda self: id(self)
In [83]: A().f2()
Out[83]: 221189392
所以你的代码几乎和下面的代码一样:
class A:
def z(self):
print self.name
name="A"
请详细阅读 http://docs.python.org/howto/descriptor.html 中的内容。
是的,这种做法是有用的,确实有它的目的,但实际上很少需要这样做。如果你觉得在类定义后需要修改它们,最好先停下来想一想,这样做是否真的是最好的办法。
一种情况是“猴子补丁”。我在一个大型的Plone系统中做过这种事情,因为有些方法需要小改动,但没有简单的方法可以正常覆盖它的行为。在这种情况下,当你面对一个复杂的库时,这种方式可以让你轻松地添加新的或修改后的行为,而不需要去改动原来的库。
另一个让我想到的情况是,当你想要生成很多可以自动化的方法时,比如数据驱动的测试。
def createTest(testcase, somedata, index):
def test(self):
"Do something with somedata and assert a result"
test_name = "test_%d" % index
setattr(testcase, test_name, test)
for index, somedata in enumerate(somebigtable):
createTest(MyTestCase, somedata, index)
当MyTestCase是一个unittest.TestCase时,你可以有一个测试遍历所有数据,但它会在第一次失败时停止,这样你就得去找出是哪一行数据出错了。通过动态创建方法,所有的测试可以单独运行,测试名称会告诉你是哪一个失败了(上面代码的原始版本实际上构建了一个更有意义的名称,包含了一些数据和索引)。
你不能在类的主体内部做到这一点,因为在定义完成之前,没有办法引用类本身或它的字典。不过,你可以通过使用一个元类来做类似的事情,因为元类允许你在创建类之前修改类的字典,有时候这样做会更干净。
另外要注意的是,有些情况下这种做法是行不通的。有些__xxx__
特殊方法在类创建后不能被覆盖:原始定义会在类的__dict__
之外的某个地方保存,所以你之后所做的任何修改可能会被忽略。此外,如果在使用元类时,有时额外的函数不会像类定义中的属性那样受到元类的处理。