在 Python 类中动态定义实例字段

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21517740/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 23:03:14  来源:igfitidea点击:

Dynamically defining instance fields in Python classes

python

提问by SeekingAlpha

I am new to Python having come from mainly Java programming.

我是 Python 新手,主要来自 Java 编程。

I am currently pondering over how classes in Python are instantiated.

我目前正在思考如何实例化 Python 中的类。

I understand that __init__(): is like the constructor in Java. However, sometimes python classes do not have an __init__()method which in this case I assume there is a default constructor just like in Java?

我明白__init__(): 就像 Java 中的构造函数。但是,有时 python 类没有一个__init__()方法,在这种情况下,我假设有一个默认构造函数,就像在 Java 中一样?

Another thing that makes the transition from Java to python slightly difficult is that in Java you have to define all the instance fields of the class with the type and sometimes an initial value. In python all of this just seems to disappear and developers can just define new fields on the fly.

另一个使从 Java 过渡到 Python 有点困难的事情是,在 Java 中,您必须使用类型定义类的所有实例字段,有时还需要定义初始值。在 python 中,所有这些似乎都消失了,开发人员可以动态地定义新字段。

For example I have come across a program like so:

例如,我遇到过这样的程序:

class A(Command.UICommand):
    FIELDS = [
        Field( 'runTimeStepSummary', BOOL_TYPE)
        ]

    def __init__(self, runTimeStepSummary=False):
        self.runTimeStepSummary = runTimeStepSummary

    """Other methods"""

    def execute(self, cont, result):
        self.timeStepSummaries = {}
        """ other code"""

The thing that confuses (and slightly irritates me) is that this A class does not have a field called timeStepSummaries yet how can a developer in the middle of a method just define a new field? or is my understanding incorrect?

令人困惑(并让我有点恼火)的是,这个 A 类没有名为 timeStepSummaries 的字段,但是处于方法中间的开发人员如何仅定义一个新字段?还是我的理解不正确?

So to be clear, my question is in Python can we dynamically define new fields to a class during runtime like in this example or is this timeStepSummaries variable an instance of a java like private variable?

所以要清楚,我的问题是在 Python 中,我们可以像这个例子一样在运行时动态地定义一个类的新字段,还是这个 timeStepSummaries 变量是一个 java 的实例,比如私有变量?

EDIT: I am using python 2.7

编辑:我正在使用 python 2.7

采纳答案by Matthew Trevor

I understand that __init__(): is like the constructor in Java.

我明白__init__(): 就像 Java 中的构造函数。

To be more precise, in Python __new__is the constructor method, __init__is the initializer. When you do SomeClass('foo', bar='baz'), the type.__call__method basically does:

更准确地说,在Python中__new__是构造器方法,__init__是初始化器。当你这样做时SomeClass('foo', bar='baz'),该type.__call__方法基本上是:

def __call__(cls, *args, **kwargs):
    instance = cls.__new__(*args, **kwargs)
    instance.__init__(*args, **kwargs)
    return instance

Generally, most classes will define an __init__if necessary, while __new__is more commonly used for immutable objects.

通常,大多数类会__init__在必要时定义一个,而__new__更常用于不可变对象。

However, sometimes python classes do not have an init() method which in this case I assume there is a default constructor just like in Java?

但是,有时 python 类没有init() 方法,在这种情况下,我假设有一个默认构造函数,就像在 Java 中一样?

I'm not sure about old-style classes, but this is the case for new-style ones:

我不确定旧式类,但新式类就是这种情况:

>>>> object.__init__
<slot wrapper '__init__' of 'object' objects>

If no explicit __init__is defined, the default will be called.

如果__init__未定义显式,则将调用默认值。

So to be clear, my question is in Python can we dynamically define new fields to a class during runtime like in this example

所以要清楚,我的问题是在 Python 中,我们可以像在这个例子中那样在运行时动态地为类定义新字段吗?

Yes.

是的。

>>> class A(object):
...     def __init__(self):
...         self.one_attribute = 'one'
...     def add_attr(self):
...         self.new_attribute = 'new'
...

>>> a = A()
>>> a.one_attribute
'one'
>>> a.new_attribute
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'new_attribute'
>>> a.add_attr()
>>> a.new_attribute
'new'

Attributes can be added to an instance at any time:

属性可以随时添加到实例中:

>>> a.third_attribute = 'three'
>>> a.third_attribute
'three'

However, it's possible to restrict the instance attributes that can be added through the class attribute __slots__:

但是,可以限制可以通过 class 属性添加的实例属性__slots__

>>> class B(object):
...     __slots__ = ['only_one_attribute']
...     def __init__(self):
...         self.only_one_attribute = 'one'
...     def add_attr(self):
...         self.another_attribute = 'two'
...

>>> b = B()
>>> b.add_attr()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in add_attr
AttributeError: 'B' object has no attribute 'another_attribute'

(It's probably important to note that __slots__is primarily intended as a memory optimization- by not requiring an object have a dictionary for storing attributes - rather than as a form of run-time modification prevention.)

(重要的是要注意,这__slots__主要是作为内存优化- 通过不要求对象具有用于存储属性的字典 - 而不是作为一种防止运行时修改的形式。)

回答by Eryk Sun

This answer pertains to new-style Python classes, which subclass object. New-style classes were added in 2.2, and they're the only kind of class available in PY3.

此答案与新式 Python 类有关,该类将object. 2.2 中添加了新样式的类,它们是 PY3 中唯一可用的类。

>>> print object.__doc__
The most base type

The class itself is an instance of a metaclass, which is usually type:

类本身是元类的一个实例,通常是type

>>> print type.__doc__
type(object) -> the object's type
type(name, bases, dict) -> a new type

Per the above docstring, you can instantiate the metaclass directly to create a class:

根据上面的文档字符串,您可以直接实例化元类以创建一个类:

>>> Test = type('Test', (object,), {'__doc__': 'Test class'})
>>> isinstance(Test, type)
True
>>> issubclass(Test, object)
True
>>> print Test.__doc__
Test class

Calling a class is handled by the metaclass __call__method, e.g. type.__call__. This in turn calls the class __new__constructor (typically inherited) with the call arguments in order to create an instance. Then it calls __init__, which may set instance attributes.

调用类由元类__call__方法处理,例如type.__call__。这反过来__new__使用调用参数调用类构造函数(通常是继承的)以创建实例。然后它调用__init__,它可以设置实例属性。

Most objects have a __dict__that allows setting and deleting attributes dynamically, such as self.value = 10or del self.value. It's generally bad form to modify an object's __dict__directly, and actually disallowed for a class (i.e. a class dict is wrapped to disable direct modification). If you need to access an attribute dynamically, use the built-in functionsgetattr, setattr, and delattr.

大多数对象都具有__dict__允许动态设置和删除属性的 ,例如self.value = 10del self.value__dict__直接修改对象通常是不好的形式,实际上不允许类(即类 dict 被包装以禁用直接修改)。如果您需要动态地访问属性,使用内置函数getattrsetattrdelattr

The data model defines the following special methods for customizing attribute access: __getattribute__, __getattr__, __setattr__, and __delattr__. A class can also define the descriptor protocol methods __get__, __set__, and __delete__to determine how its instances behave as attributes. Refer to the descriptor guide.

数据模型定义了以下特殊方法定制属性访问__getattribute____getattr____setattr__,和__delattr__。一个类也可以定义描述符协议方法__get____set__以及__delete__确定它的实例的行为作为属性。请参阅描述符指南

When looking up an attribute, object.__getattribute__first searches the object's class and base classes using the C3 method resolution orderof the class:

在查找属性时,object.__getattribute__首先使用类的C3 方法解析顺序搜索对象的类和基类:

>>> Test.__mro__
(<class '__main__.Test'>, <type 'object'>)

Note that a data descriptor defined in the class (e.g. a propertyor a memberfor a slot) takes precedence over the instance dict. On the other hand, a non-data descriptor (e.g. a function) or a non-descriptor class attribute can be shadowed by an instance attribute. For example:

请注意,类中定义的数据描述符(例如 aproperty或 amember用于插槽)优先于实例字典。另一方面,非数据描述符(例如函数)或非描述符类属性可以被实例属性遮蔽。例如:

>>> Test.x = property(lambda self: 10)
>>> inspect.isdatadescriptor(Test.x)
True
>>> t = Test()
>>> t.x
10
>>> t.__dict__['x'] = 0
>>> t.__dict__
{'x': 0}
>>> t.x
10

>>> Test.y = 'class string'
>>> inspect.isdatadescriptor(Test.y)
False
>>> t.y = 'instance string'
>>> t.y
'instance string'

Use superto proxy attribute access for the next class in the method resolution order. For example:

使用super以在方法解析顺序隔壁班的代理属性的访问。例如:

>>> class Test2(Test):
...     x = property(lambda self: 20)
... 
>>> t2 = Test2()
>>> t2.x
20
>>> super(Test2, t2).x
10

回答by kindall

Attributes of Python objects are generally stored in a dictionary, just like the ones you create with {}. Since you can add new items to a dictionary at any time, you can add attributes to an object at any time. And since any type of object can be stored in a dictionary without previous declaration of type, any type of object can be stored as an attribute of an object.

Python 对象的属性通常存储在字典中,就像使用{}. 由于您可以随时向字典添加新项目,因此您可以随时向对象添加属性。并且由于任何类型的对象都可以存储在字典中而无需事先声明类型,因此任何类型的对象都可以存储为对象的属性。

In short, my_object.abc = 42is (often) just a shorthand for my_object.__dict__["abc"] = 42.

简而言之, my_object.abc = 42(通常)只是my_object.__dict__["abc"] = 42.

It is possible to define objects without a __dict__by defining the __slots__attribute, or to override certain special methods and store attributes in some other way, though most of the time you shouldn't do that.

可以__dict__通过定义__slots__属性来定义没有 a的对象,或者覆盖某些特殊方法并以其他方式存储属性,尽管大多数时候您不应该这样做。