Python - 为什么我可以用实例调用 class 方法?
Python - why can I call a class method with an instance?
Python 的新手并阅读了一些资料,我正在自定义 class class 方法而不是实例方法中制作一些方法。
所以我测试了我的代码,但我没有更改一些方法调用来调用 class 中的方法而不是实例,但它们仍然有效:
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) 上调用 class 方法 (foo),(尽管它是 class 被传递给方法)?这是有道理的,因为 'thing' 是一个 'myClass',但我期待 Python 给出一个错误,说一些类似于“foo 是一个 class 方法和无法在实例上调用'。
这是否只是继承的预期结果,'thing' 对象从其 superclass 继承了 foo 方法?
如果我尝试通过 class:
调用实例方法
myClass.bar()
然后我得到:
TypeError: unbound method bar() must be called with myClass instance...
这很有道理。
您可以在实例上调用它,因为 @classmethod
是一个 decorator(它接受一个函数作为参数,returns 接受一个新函数)。
这里是Pythondocumentation
的一些相关信息
It can be called either on the class (such as C.f()) or on an instance
(such as 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.
@classmethod
here 上也有很好的 SO 讨论。
让我们从描述符协议的快速概述开始。如果 class 定义了 __get__
方法,则该 class 的实例是一个描述符。将描述符作为另一个对象的属性进行访问会产生 __get__
方法的 return 值,而不是描述符本身。
一个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__
的对象。前者 return 是一个新的 method
对象,调用时将 c
和它自己的参数传递给函数 bar
。这就是 c.bar()
和 C.bar(c)
等价的原因。后者只是 return 函数 bar
本身。
classmethod
是一种提供 __get__
的 不同 实现的类型。这意味着 c.foo()
和 myClass.foo()
像以前一样调用 __get__
:
# c.foo
myClass.__dict__['foo'].__get__(c, myClass)
# myClass.foo
myClass.__dict__['foo'].__get__(None, myClass)
然而,现在 两者 调用 return 同一个 method
对象,而这个 method
对象在调用时传递 myClass
作为原始 function
对象的第一个参数。也就是说,c.foo()
等同于 myClass.foo()
,即
等同于 x(myClass)
(其中 x
是在装饰将名称 foo
绑定到 classmethod
的实例之前定义的原始函数)。
Python 的新手并阅读了一些资料,我正在自定义 class class 方法而不是实例方法中制作一些方法。
所以我测试了我的代码,但我没有更改一些方法调用来调用 class 中的方法而不是实例,但它们仍然有效:
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) 上调用 class 方法 (foo),(尽管它是 class 被传递给方法)?这是有道理的,因为 'thing' 是一个 'myClass',但我期待 Python 给出一个错误,说一些类似于“foo 是一个 class 方法和无法在实例上调用'。
这是否只是继承的预期结果,'thing' 对象从其 superclass 继承了 foo 方法?
如果我尝试通过 class:
调用实例方法myClass.bar()
然后我得到:
TypeError: unbound method bar() must be called with myClass instance...
这很有道理。
您可以在实例上调用它,因为 @classmethod
是一个 decorator(它接受一个函数作为参数,returns 接受一个新函数)。
这里是Pythondocumentation
的一些相关信息It can be called either on the class (such as C.f()) or on an instance (such as 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.
@classmethod
here 上也有很好的 SO 讨论。
让我们从描述符协议的快速概述开始。如果 class 定义了 __get__
方法,则该 class 的实例是一个描述符。将描述符作为另一个对象的属性进行访问会产生 __get__
方法的 return 值,而不是描述符本身。
一个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__
的对象。前者 return 是一个新的 method
对象,调用时将 c
和它自己的参数传递给函数 bar
。这就是 c.bar()
和 C.bar(c)
等价的原因。后者只是 return 函数 bar
本身。
classmethod
是一种提供 __get__
的 不同 实现的类型。这意味着 c.foo()
和 myClass.foo()
像以前一样调用 __get__
:
# c.foo
myClass.__dict__['foo'].__get__(c, myClass)
# myClass.foo
myClass.__dict__['foo'].__get__(None, myClass)
然而,现在 两者 调用 return 同一个 method
对象,而这个 method
对象在调用时传递 myClass
作为原始 function
对象的第一个参数。也就是说,c.foo()
等同于 myClass.foo()
,即
等同于 x(myClass)
(其中 x
是在装饰将名称 foo
绑定到 classmethod
的实例之前定义的原始函数)。