Python中的无类方法对任何东西都有用吗?

时间:2011-11-30 07:02:48

标签: python class methods

我刚注意到你可以用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 个答案:

答案 0 :(得分:6)

是的,它很有用,它确实有用,但是这也很少见。如果您认为需要在定义类之后对其进行修补,则应始终停止并考虑它是否真的是最好的方法。

一种情况是猴子修补。我已经在一个庞大的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时,您可以进行一次测试,遍历所有数据,但在第一次失败时停止,您不必尝试找出哪一行数据失败。通过动态创建方法,所有测试都单独运行,测试名称告诉你哪一个失败了(上面代码的原始实际构建了一个更有意义的名称,涉及一些数据和索引)。

您不能在类的主体内执行此操作,因为在定义完成之前无法引用类本身或其字典。但是,您可以使用元类进行类似的操作,因为它允许您在创建类本身之前修改类dict,有时这是一种更简洁的方式来执行相同的操作。

另一点需要注意的是,有些情况下这不起作用。在创建类之后,无法覆盖某些__xxx__特殊方法:原始定义将保存在类__dict__以外的某个位置,因此您之后所做的任何更改都可能会被忽略。此外,如果使用元类,有时额外的函数将无法获得元类赋予属性的任何处理作为类定义的一部分。

答案 1 :(得分:3)

因为函数对象具有__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

中的详细信息

答案 2 :(得分:2)

有趣的事实:

  1. python中的函数是一个类型:

    type(f)  # prints <type: function>
    
  2. 因此,function是一个可调用的实例,因此它可以插入任何地方。

  3. 它可用于使功能可移植并模拟即插即用功能。

  4. 这在开发过程中非常方便 - 尝试不同的逻辑(通过插入不同的功能)

  5. 这提供了对逻辑的抽象,因为我们可以用导入的函数替换类方法。

  6. 虽然,我怀疑你将如何使用它来模拟接口!