简体   繁体   English

类变量的行为,当它不在__init__时

[英]Behaviour of class variable when it's not in __init__

I have the following Class named MyClass : 我有以下名为MyClass的类:

 def __init__(self, config):
        self.response = Response(config)
        self.utility = Utility()
        self.listl = {}

It has the following method : 它有以下方法:

 def findSt(self, lv):      
        dvp = self.listl
        if lv not in dvp:
            raise Exception("ERROR: ....", lv)
        else:
             ...

In my main code i use another method (getvalues) of that same class to give the correct value to self.listl ie: 在我的主代码中,我使用同一个类的另一个方法(getvalues)为self.listl提供正确的值,即:

    myobject = MyClass(config)
    myobject.listl = myobject.getvalues()
    lr = []
    for lv in lvs:
        lr.append(myobject.findSt(lv))   

This works, although i'm not sure it's the proper way to do this. 这是有效的,虽然我不确定这是否是正确的方法。 The reason i'm asking is because if i do the following typo in the main code : 我问的原因是因为如果我在主代码中执行以下拼写错误:

myobject.listL = myobject.getvalues()

No error is raised ! 没有错误! Of course in that case myobject.listl doesn't have the proper runtime values so it doesn't work, but why can I create a variable that is not in __init__ ? 当然,在这种情况下, myobject.listl没有正确的运行myobject.listl ,因此它不起作用,但为什么我可以创建一个不在__init__的变量?

For the same reason that you can set myobject.listl -- you can set any attribute on any instance from anywhere*, attributes set in __init__ aren't special in any way. 出于同样的原因你可以设置myobject.listl - 你可以在任何地方设置任何实例的任何属性*, __init__中设置的属性在任何方面都不是特殊的。 The only thing special about __init__ is that it's called automatically when a new instance is made. 关于__init__唯一特别之处在于它是在创建新实例时自动调用的。

That doesn't mean you have to make use of that, it's usually better not to set such attributes from outside the class, I'd prefer to have a method like 这并不意味着你必须使用它,通常最好不要从类外部设置这样的属性,我更喜欢有类似的方法

def setvalues(self):
    self.listl = self.getvalues()

or so. 或者。 In general you need to be careful because you have a mutable attribute there, what if some outside code still holds a reference to the old self.listl? 一般来说,你需要小心,因为你有一个mutable属性,如果一些外部代码仍然拥有对旧self.listl的引用呢? Then that old list won't be changed by that code. 然后该代码不会更改旧列表。 This way it would be: 这样会是:

def setvalues(self):
    self.listl[:] = self.getvalues()

Doesn't re-set the listl attribute, but merely the values inside it. 不重新设置listl属性,而只是重新设置其中的值。 But that's a whole different discussion. 但这是一个完全不同的讨论。

*: there are a few exceptions, like with classes implemented in C, or classes with __slots__ defined, but those are rare exceptions. *:有一些例外,例如在C中实现的类,或者定义了__slots__类,但这些是罕见的例外。

User-defined class instances can add/remove attributes at runtime: 用户定义的类实例可以在运行时添加/删除属性:

>>> class A(object): pass
... 
>>> a = A()
>>> a.something = 1   #no error. Now a has a new something attibute
>>> 

While built-in types usually don't allow this: 虽然内置类型通常不允许这样:

>>> a = object()
>>> a.something = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'something'

In python every object has an associated dictionary that contains its attributes: __dict__ . 在python中,每个对象都有一个包含其属性的关联字典: __dict__ This dictionary is mutable . 这本词典是可变的 When you perform an assignment: 执行作业时:

instance.attribute = value

What actually happens is that you are assigning to the __dict__ of the object: 实际发生的是你分配给对象的__dict__

instance.__dict__['attribute'] = value

and in fact: 事实上:

>>> class A(object):
...     def __init__(self):
...             self.something = 1
... 
>>> a = A()
>>> a.__dict__
{'something': 1}
>>> a.other = 2
>>> a.__dict__
{'other': 2, 'something': 1}
>>> a.__dict__['other2'] = 3
>>> a.other2
3

__init__ is not special in this respect. __init__在这方面并不特别。 It's just a convention that all attributes are defined there, but when you put self.attribute = value inside __init__ you are doing exactly the same thing as the above code: creating a new attribute. 这只是一个约定 ,所有属性都在那里定义,但当你在__init__放置self.attribute = value ,你正在做与上面代码完全相同的事情:创建一个属性。 It is only the proper method to do initialization most of the time. 在大多数情况下进行初始化只是正确的方法。

Classes do not (usually) declare a predefined set of attributes of their instances. 类(通常) 不会声明其实例的预定义属性集。 It is possible with some meta-programming to add this kind of declaration (eg using class-decorators/metaclasses for example). 一些元编程可以添加这种声明(例如,使用类装饰器/元类)。

If you want to define the attributes of a class in advance you can use __slots__ : 如果要提前定义类的属性,可以使用__slots__

>>> class A(object):
...     __slots__ = ('something',)
... 
>>> a = A()
>>> a.something = 1
>>> a.other = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'other'

When you define __slots__ in a class then its instance do not have an associated __dict__ , and hence its not possible to add attributes at runtime 在类中定义__slots__ ,它的实例没有关联的__dict__ ,因此无法在运行时添加属性

>>> a.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute '__dict__'

Alternatively you can redefine the __setattr__ method to have finer control about what happens when you set an attribute. 或者,您可以重新定义__setattr__方法,以便更好地控制设置属性时发生的情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM