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...
这很有道理。
答案 0 :(得分:9)
您可以在实例上调用它,因为@classmethod
是decorator(它将函数作为参数并返回一个新函数)。
以下是来自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
的实例之前定义的原始函数)。