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)
- 如果需要实例方法,为什么要使用classmethod?
- 您所称的"__init__vars"是实例属性。类方法被称为类方法的原因是它只能访问类,不能访问实例。你为什么要用classmethod?
- @Brenbarn:我把__init__作为一个构造函数,一旦设置好,值就可以在类方法中的任何地方使用。
- @Noobeditor:你为什么用classmethod?你认为classmethod的目的是什么?听起来您应该阅读Python教程来了解如何在Python中使用类。
- @用户2357112:听起来可能很蠢……但是classmethod和instance方法有什么区别?通过object访问方法是实例,做A(). class_method()是S类方法吗?
- @ BrBrnn:我确实经历了文档伴侣,那总是第1步,我要做的是从Java shell中出来,在Java EDCOX1中,10个EDCOX1,11个EDCOX1,12个,差不多,但是显然不在Python……这就是我困惑的原因……有意义吗?
- @Noobeditor:这些或多或少是相同的,但同样,您没有以任何方式解释为什么您使用classmethod。您似乎认为类方法只是您在类中定义的任何方法,但事实并非如此。你可能想看看这个问题和这个问题。如果要访问实例变量,请不要使用classmethod。差不多就是这样。
- Python类方法类似于Java静态方法
- @大卫:不,他们不是。Java静态方法不参与多态性。它们不能重写或被其他方法(静态方法或其他方法)重写,并且不能在运行时进行多态调用,而不需要进行大量的反射。python没有这些限制。
- @我说凯文"类似",不是"同一个"。记住,asker是一个新手,他认为classmethod是一个实例方法。就一个简单而有用的类比而言,我的评论是合理的。新手没有机会理解你刚才说的话。虽然我知道你所说的一切,你认为提问者能理解吗?想想学习过程。你的回答对提问者来说完全是错误的。
让我们来谈谈Python的方法实际上是如何工作的。
您可能已经注意到,python方法的声明与独立函数一样,但是在类中。这是因为python方法实际上是独立的函数,恰好位于类中。self和cls的论点并不特殊。它只是函数的第一个参数。
在我们进一步讨论之前,我想指出的是,您似乎没有明确继承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中。