关于python:在classmethod中访问 __init__

Accessing __init__ vars in classmethod

我来自Java背景,所以我有点困惑。

考虑下面的代码段

1
2
3
4
5
6
7
8
9
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

这将引发一个错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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'

如果我评论@classmethod,这会很好。

我的理解(当然不正确):

当我执行a = A()时,在__init__中创建的所有变量(obj_var都可以通过这个a在传递给同一类的任何其他classmethod时访问。显然情况并非如此。

问题(s)

  • 为什么我不能访问class_method中的__init__vars,当方法中提到decorator @classmethod时,但是在移除decorator时,它工作正常?

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

  • 我可以用相同的方法使用@classmethod__init__vars吗?


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

您可能已经注意到,python方法的声明与独立函数一样,但是在类中。这是因为python方法实际上是独立的函数,恰好位于类中。selfcls的论点并不特殊。它只是函数的第一个参数。

在我们进一步讨论之前,我想指出的是,您似乎没有明确继承object。如果您使用的是python 2.x,那么在图的根目录中没有object,除非您明确地继承了它。这是一件坏事,在新代码中,您应该尽可能直接或间接地从object继承。从Python3中的object继承是合法的、无害的,但不必要。本讨论的其余部分假定您正在3.x中工作或已修复此问题。

当您使用foo.bar访问变量、函数、方法或任何其他类型的对象时,会调用一些称为"描述符协议"的钩子。你不需要知道这些细节就可以理解函数是如何工作的。你只需要知道:

  • 首先,如果有一个实例变量bar直接附加到foo上(而foo不是一个类),我们直接返回它。
  • 如果foo是一个类,而bar是在foo或它的一个超类中声明的@classmethod函数(**),那么在返回它之前,第一个参数设置为foo。否则,它将原封不动地返回。(**)如果返回了某些内容,我们将在此处停止。
  • 我们搜索foo的"方法分辨率顺序"。这包括foo的类(称为type(foo)的类)、那个类的超类,等等,直到我们到达object为止。在多重继承的情况下,这会变得更加复杂,但同样,您不需要知道这一点。
  • 从有一个的第一个类(称为Baz)中取bar变量。
  • 如果Baz.bar是一个规则的、未修饰的函数,则将其第一个参数设置为foo并返回它。
  • 否则,如果Baz.bar@classmethod函数,则将其第一个参数设置为type(foo)并返回它。
  • 否则,如果Baz.bar@staticmethod函数,或者根本不是函数(**),则返回原样。
  • 如您所见,如果方法声明为@classmethod,那么第一个参数始终是类,而不是实例,不管函数是如何调用的。这意味着您不能访问foo的实例变量,因为您不能访问foo本身。__init__()中设置的任何变量都是实例变量,因此在这里不可见。

    以下是我撒的谎:

    (*):python实际上先完成剩余的工作,然后返回到这个步骤。但这只对像@property这样的东西很重要,它实际上可以覆盖本地实例变量。常规方法不能。

    (**):这是一个谎言。在本例中,python还对@property函数以及其他实现某些特殊方法的函数进行特殊处理。

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