使用“self”调用类方法?

Pra*_*gre 5 python class-method

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
Run Code Online (Sandbox Code Playgroud)

我发现很难理解上面两个类调用类方法的不同风格。一个人在调用时使用 self 和另一个类名,什么时候应该使用第一个,什么时候应该使用第二个?

好的解释会真正澄清@classmethod概念。

jua*_*aga 5

这里的核心问题实际上不是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
Run Code Online (Sandbox Code Playgroud)

现在,在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>})
Run Code Online (Sandbox Code Playgroud)

实例属性只是您使用以下命令显式分配给实例的属性:self.something = somethingsomething

>>> pprint(a.__dict__)
{'x': 42}
Run Code Online (Sandbox Code Playgroud)

然而,当你访问一个属性时,你可以认为Python首先检查__dict__实例的。如果没有找到,它就会检查该类的名称空间。如果仍然找不到,它会按照方法解析顺序(即继承)检查任何父类的名称空间。

因此,如果您这样做,类本身就是其他对象:

Hello.open('World')
Run Code Online (Sandbox Code Playgroud)

这会检查Hello对象名称空间并找到它。同样,如果您在实例上使用该方法,

self.open('World')
Run Code Online (Sandbox Code Playgroud)

它检查实例命名空间,没有找到它,然后检查类命名空间,然后找到。现在,如果您不希望延长课程时间,那么实际上没有什么区别。但如果你这样,语义就会改变。如果您从Hello, 派生并重写open而不是load,则将Hello.open始终调用超类open方法。但是,self.open版本会在类命名空间中找到它,并且从不检查父类。因此,根据您想要的行为,您可以使用其中任何一个,但通常如果您的类设计正确,那么self就是正确的方法。但也许不是。那么,给你一个具体的例子:

>>> class B(A):
...    a = 'bar'
...
>>> b = B()
>>> b.method()
foo
bar
42
>>>
Run Code Online (Sandbox Code Playgroud)