这个方法应该是一个类方法,为什么它不能访问变量?

Noo*_*tor 6 python instance-variables decorator class-method python-decorators

我来自java背景,所以我在这里有点困惑。

考虑下面的代码片段:

class A():
    def __init__(self, **kwargs):
        self.obj_var = "I am obj var"

    @classmethod
    def class_method(cls):
        print cls.obj_var   # this line is in question here
        cls.cls_obj = "I m class object"
        return cls.cls_obj
Run Code Online (Sandbox Code Playgroud)

这会引发错误:

In [30]: a = A()

In [31]: a.obj_var
Out[31]: 'I am obj var'

In [32]: a.class_method()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-32-3dcd9d512548> in <module>()
----> 1 a.class_method()

<ipython-input-29-9c0d341ad75f> in class_method(cls)
      8     @classmethod
      9     def class_method(cls):
---> 10         print cls.obj_var
     11         cls.cls_obj = "I m class object"
     12         return cls.cls_obj

AttributeError: class A has no attribute 'obj_var'
Run Code Online (Sandbox Code Playgroud)

如果我注释掉@classmethod,这会正常工作。

我的理解肯定这是不正确的):

当我这样做时a = A(),当传递给任何其他相同类时obj_var__init__可以通过 this 访问创建的所有变量()。显然情况并非如此。aclassmethod

问题)

  • 为什么当方法中提到装饰器时我无法访问__init__ vars ,但在删除装饰器时,它工作正常?class_method@classmethod

  • python在编译时如何在内部处理这个特定的类?

  • 有什么方法可以使用@classmethod__init__vars 相同的方法吗?

Kev*_*vin 5

让我们来谈谈 Python 的方法实际上是如何工作的。

您可能已经注意到 Python 方法的声明就像独立的函数一样,但是在一个类中。那是因为 Python 方法确实是碰巧在类中的独立函数。该self/cls说法并不特殊。这只是函数的第一个参数。

在我们进一步讨论之前,我想指出您似乎没有明确继承自object. 如果您在 Python 2.x 中工作,object除非您显式继承它,否则图的根部没有。 这是一件坏事,您应该object在新代码中尽可能直接或间接地继承。从objectPython 3 中继承是合法且无害的,但没有必要。本讨论的其余部分假设您在 3.x 中工作或已修复此问题。

当您使用 访问变量、函数、方法或任何其他类型的对象时foo.bar,会调用某些称为“描述符协议”的挂钩。您无需了解这些细节即可了解函数的工作原理。所有你需要知道的是:

  1. 首先,如果有一个bar直接附加的实例变量foo(并且foo不是一个类),我们就直接返回它。(*)
  2. 如果foo是一个类并且bar是在其超类@classmethodfoo或其中一个超类中声明的函数(**),则foo在我们返回它之前将第一个参数设置为。否则,它原样返回。(***) 如果我们返回了什么,我们就到此为止
  3. 我们搜索 的“方法解析顺序” foo。这包括foo的类(称为type(foo))、该类的超类,依此类推,直到我们到达object。在多重继承的情况下,这会变得有点复杂,但同样,您不需要知道这一点。
  4. bar从第一个类中获取变量(称为Baz)。
  5. 如果Baz.bar是一个常规的、未修饰的函数,则将它的第一个参数设置为foo并返回它。
  6. 否则,如果Baz.bar是一个@classmethod函数,将它的第一个参数设置为type(foo)并返回它。
  7. 否则,如果Baz.bar@staticmethod函数,或者根本不是函数(**),则原样返回。

如您所见,如果方法已声明@classmethod无论函数如何调用,第一个参数始终是类,而不是实例。这意味着您无权访问 的实例变量foo,因为您无权访问其foo自身。中设置的任何变量__init__()都是实例变量,因此它们在此处不可见。

以下是我撒谎的所有事情:

(*):Python 实际上会先完成剩下的工作,然后再回到这一步。但这仅对诸如 之类的东西很重要@property,它实际上可以覆盖本地实例变量。常规方法不能。

(**): 这是个谎言。@property在这种情况下,Python 还会对函数以及其他任何实现某些特殊方法的东西进行特殊处理。

(***):我也忽略了 Python 2.x 中的未绑定方法,因为除了某些非常奇怪的情况(当您尝试手动传递错误类型的对象作为第一个参数时),它们没有任何区别. 它们在 Python 3 中不存在。


小智 5

如果要从类方法访问实例变量,则需要创建该实例变量所在的类的实例。希望下面的代码应该工作。

class A():
def __init__(self, **kwargs):
    self.obj_var = "I am obj var"

@classmethod
def class_method(cls):
    self = cls() # Here you are creating an instance of the class (in this case it is class A)
    print(self.obj_var) 
    cls.cls_obj = "I m class object"
    return cls.cls_obj

 print(A.class_method())
Run Code Online (Sandbox Code Playgroud)