Python - 为什么我可以用实例调用类方法?

时间:2015-05-12 11:52:19

标签: python python-2.7 inheritance class-method

Python的新手并且做了一些阅读,我在自定义类类方法而不是实例方法中制作了一些方法。

所以我测试了我的代码,但是我没有改变一些方法调用来调用类中的方法而不是实例,但它们仍然有效:

class myClass:
   @classmethod:
   def foo(cls):
      print 'Class method foo called with %s.'%(cls)

   def bar(self):
      print 'Instance method bar called with %s.'%(self)

myClass.foo()
thing = myClass()
thing.foo()
thing.bar()

这会产生:

class method foo called with __main__.myClass.
class method foo called with __main__.myClass.
instance method bar called with <__main__.myClass instance at 0x389ba4>.

所以我想知道为什么我可以在实例(thing.foo)上调用类方法(foo),(虽然它是传递给方法的类)?这有点意义,因为&#39;事情&#39;是一个myClass&#39;但是我希望Python能够提出一个错误,说“foo”是一种类方法,并且不能在实例上调用#39;。

这只是“&#39;”事物继承的预期结果。 object从其超类继承foo方法?

如果我尝试通过类调用实例方法:

myClass.bar()

然后我得到:

TypeError: unbound method bar() must be called with myClass instance...

这很有道理。

2 个答案:

答案 0 :(得分:9)

您可以在实例上调用它,因为@classmethoddecorator(它将函数作为参数并返回一个新函数)。

以下是来自Python documentation

的一些相关信息
  

可以在类(例如C.f())或实例上调用它   (例如C()。f())。除了类之外,该实例被忽略。如果一个   为派生类调用类方法,派生类对象   作为隐含的第一个参数传递。

@classmethod here上也有很好的SO讨论。

答案 1 :(得分:1)

让我们从描述符协议的快速概述开始。如果类定义了__get__方法,则该类的实例是描述符。将描述符作为另一个对象的属性访问会产生__get__方法的返回值,而不是描述符本身。

function是一个描述符;这就是实例方法的实现方式。给定

class myClass:
    @classmethod:
    def foo(cls):
        print('Class method foo called with %s.' % (cls,))

    def bar(self):
        print 'Instance method bar called with %s.'%(self)

c = myClass()

表达式c.bar等效于

myClass.__dict__['bar'].__get__(c, myClass)

表达式myClass.bar等同于表达式

myClass.__dict__['bar'].__get__(None, myClass)

请注意,唯一的区别是作为第一个参数传递给__get__的对象。前者返回一个新的method对象,该对象在被调用时将c及其自己的参数传递给函数bar。这就是c.bar()C.bar(c)等价的原因。后者仅返回函数bar本身。

classmethod是一种提供__get__不同实现的类型。这意味着c.foo()myClass.foo()像以前一样调用__get__

# c.foo
myClass.__dict__['foo'].__get__(c, myClass)

# myClass.foo
myClass.__dict__['foo'].__get__(None, myClass)

现在,两个调用返回相同的method对象,并且此method对象在被调用时将myClass作为第一个参数传递给原始function对象。也就是说,c.foo()等效于myClass.foo(), 等效于x(myClass)(其中x是在修饰之前将名称foo绑定到classmethod的实例之前定义的原始函数)。