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

时间:2014-02-02 23:34:55

标签: python

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

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

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

使从Java转换到python稍微困难的另一个原因是,在Java中,您必须使用类型定义类的所有实例字段,有时还要定义初始值。在python中,所有这些似乎都消失了,开发人员可以动态定义新的字段。

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

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"""

令人困惑的事情(并且稍微让我感到恼火)是这个A类没有名为timeStepSummaries的字段但是方法中间的开发人员如何才能定义新字段?或者我的理解不正确?

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

编辑:我正在使用python 2.7

3 个答案:

答案 0 :(得分:23)

  

我理解__init__():就像Java中的构造函数。

更准确地说,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

通常,如果需要,大多数类将定义__init__,而__new__更常用于不可变对象。

  

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

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

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

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

  

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

>>> 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'

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

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

但是,可以限制可以通过类属性__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'

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

答案 1 :(得分:4)

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

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

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

答案 2 :(得分:0)

这个答案适用于新式Python类,它是object的子类。 2.2中添加了新式类,它们是PY3中唯一可用的类。

>>> print object.__doc__
The most base type

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

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

根据上述docstring,您可以直接实例化元类以创建类:

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

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

大多数对象都有__dict__,可以动态设置和删除属性,例如self.value = 10del self.value。通常不好的形式是直接修改对象的__dict__,并且实际上不允许类(即包括类dict以禁用直接修改)。如果您需要动态访问属性,请使用built-in functions getattrsetattrdelattr

数据模型为customizing attribute access定义了以下特殊方法:__getattribute____getattr____setattr____delattr__。类还可以定义描述符协议方法__get____set____delete__,以确定其实例作为属性的行为方式。请参阅descriptor guide

查找属性时,object.__getattribute__首先使用类的C3 method resolution order搜索对象的类和基类:

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

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

>>> 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'

使用super代理方法解析顺序中下一个类的属性访问权限。例如:

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