简体   繁体   English

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

[英]Python - why can I call a class method with an instance?

New to Python and having done some reading, I'm making some methods in my custom class class methods rather than instance methods.刚接触 Python 并阅读了一些资料后,我在自定义类的类方法中创建了一些方法,而不是实例方法。

So I tested my code but I hadn't changed some of the method calls to call the method in the class rather than the instance, but they still worked:所以我测试了我的代码,但我没有改变一些方法调用来调用类中的方法而不是实例,但它们仍然有效:

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()

This produces:这产生:

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

So what I'm wondering is why I can call a class method (foo) on an instance (thing.foo), (although it's the class that gets passed to the method)?所以我想知道的是为什么我可以在实例 (thing.foo) 上调用类方法 (foo),(尽管它是传递给方法的类)? It kind of makes sense, as 'thing' is a 'myClass', but I was expecting Python to give an error saying something along the lines of 'foo is a class method and can't be called on an instance'.这是有道理的,因为“东西”是一个“myClass”,但我希望 Python 给出一个错误,说“foo 是一个类方法,不能在实例上调用”。

Is this just an expected consequence of inheritance with the 'thing' object inheriting the foo method from its superclass?这是否只是继承的预期结果,“事物”对象从其超类继承了 foo 方法?

If I try to call the instance method via the class:如果我尝试通过类调用实例方法:

myClass.bar()

then I get:然后我得到:

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

which makes perfect sense.这是完全有道理的。

You can call it on an instance because @classmethod is a decorator (it takes a function as an argument and returns a new function).您可以在实例上调用它,因为@classmethod是一个装饰器(它接受一个函数作为参数并返回一个新函数)。

Here is some relavent information from the Python documentation这是 Python文档中的一些相关信息

It can be called either on the class (such as Cf()) or on an instance (such as C().f()).它可以在类(如 Cf())或实例(如 C().f())上调用。 The instance is ignored except for its class.除了它的类之外,该实例被忽略。 If a class method is called for a derived class, the derived class object is passed as the implied first argument.如果为派生类调用类方法,则派生类对象将作为隐含的第一个参数传递。

There's also quite a good SO discussion on @classmethod here .关于@classmethod here也有很好的 SO 讨论。

Let's start with a quick overview of the descriptor protocol.让我们从描述符协议的快速概览开始。 If a class defines a __get__ method, an instance of that class is a descriptor.如果类定义了__get__方法,则该类的实例是描述符。 Accessing a descriptor as the attribute of another object produces the return value of the __get__ method, not the descriptor itself.访问作为另一个对象的属性的描述符会产生__get__方法的返回值,而不是描述符本身。

A function is a descriptor;一个function是一个描述符; this is how instance methods are implemented.这就是实例方法的实现方式。 Given给定的

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()

the expression c.bar is equivalent to表达式c.bar等价于

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

while the expression myClass.bar is equivalent to the expression而表达式myClass.bar等价于表达式

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

Note the only difference is in the object passed as the first argument to __get__ .请注意,唯一的区别在于作为第一个参数传递给__get__的对象。 The former returns a new method object, which when called passes c and its own arguments on to the function bar .前者返回一个新的method对象,该对象在调用时将c和它自己的参数传递给函数bar This is why c.bar() and C.bar(c) are equivalent.这就是为什么c.bar()C.bar(c)是等价的。 The latter simply returns the function bar itself.后者只是返回函数bar本身。

classmethod is a type that provides a different implementation of __get__ . classmethod是一种提供__get__不同实现的__get__ This means that c.foo() and myClass.foo() call __get__ as before:这意味着c.foo()myClass.foo()像以前一样调用__get__

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

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

Now, however, both calls return the same method object, and this method object, when called, passes myClass as the first argument to the original function object.然而,现在两个调用都返回相同的method对象,并且这个method对象在调用时将myClass作为第一个参数传递给原始function对象。 That is, c.foo() is equivalent to myClass.foo() , which is equivalent to x(myClass) (where x is the original function defined before the decoration bound the name foo to an instance of classmethod ).也就是说, c.foo()等效于myClass.foo() ,它等效于x(myClass) (其中x是在修饰将名称foo绑定到classmethod的实例之前定义的原始函数)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM