class Hello:
def load(self):
self.open('World')
@classmethod
def open(cls,print_string):
print 'Hello ' + print_string
class Hello:
def load(self):
Hello.open('World')
@classmethod
def open(cls,print_string):
print 'Hello ' + print_string
我发现从上面两类不同风格的调用类方法很难理解。一个人在调用时使用self和另一个类名,何时应该首先使用,何时应该使用第二个?
好的解释将真正阐明@classmethod
概念。
答案 0 :(得分:1)
这里的问题真的不是classmethod
,而是Python处理属性的方式。有两种不同的属性,类级属性(在其他语言中有时称为"静态"属性)和实例属性。
考虑:
>>> class A:
... a = 'foo'
... def __init__(self):
... self.x = 42
... def method(self):
... print(A.a)
... print(self.a)
... print(self.x)
...
>>> a = A()
>>> a.method()
foo
foo
42
现在,在Python中,类级属性(包括方法本身)属于类命名空间,可通过__dict__
属性访问:
>>> from pprint import pprint
>>> pprint(A.__dict__)
mappingproxy({'__dict__': <attribute '__dict__' of 'A' objects>,
'__doc__': None,
'__init__': <function A.__init__ at 0x101589d90>,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'a': 'foo',
'method': <function A.method at 0x10863a620>})
实例属性仅使用self.something = somethingsomething
明确指定给实例的:
>>> pprint(a.__dict__)
{'x': 42}
但是,当您访问属性时,您可以认为Python 首先检查实例的__dict__
。如果找不到它,则会检查类的命名空间。如果仍然找不到它,它会以方法解析顺序(即继承)检查任何父类的名称空间。
所以,如果你这样做,那么类本身只是其他对象:
Hello.open('World')
这将检查Hello
对象命名空间并找到它。同样,如果您在实例上使用该方法,
self.open('World')
它检查实例名称空间,找不到它,然后检查类名称空间并执行。现在,如果你不希望扩展课程,那么真的没有区别。但是如果你做那么语义将会改变。如果您派生自Hello
,并覆盖open
而不是load
,则Hello.open
将始终调用超类open
方法。但是,self.open
版本将在类命名空间中找到它,并且永远不会检查父类。因此,根据您想要的行为,您可以使用其中任何一种,但通常如果您的类设计正确,则self
是可行的方法。但也许不是。所以,给你一个具体的例子:
>>> class B(A):
... a = 'bar'
...
>>> b = B()
>>> b.method()
foo
bar
42
>>>