Поля класса 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-частной переменной?

EDIT: я использую python 2.7

Ответ 1

Я понимаю, что __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__ в первую очередь предназначен как оптимизация памяти - не требуя, чтобы у объекта был словарь для хранения атрибутов, а не как форма предотвращения изменения времени выполнения.)

Ответ 2

Атрибуты объектов Python обычно хранятся в словаре, как и те, которые вы создаете с помощью {}. Поскольку вы можете добавлять новые слова в словарь в любое время, вы можете добавлять атрибуты к объекту в любое время. И так как любой тип объекта может быть сохранен в словаре без предыдущего объявления типа, любой тип объекта может быть сохранен как атрибут объекта.

Короче говоря, my_object.abc = 42 является (часто) просто сокращением для my_object.__dict__["abc"] = 42.

Можно определить объекты без __dict__, указав атрибут __slots__ или переопределить некоторые специальные методы и сохранить атрибуты каким-либо другим способом, хотя большую часть времени вы не должны этого делать.

Ответ 3

Этот ответ относится к классам Python нового стиля, подклассу object. Классы нового стиля были добавлены в 2.2, и они являются единственным видом класса, доступным в PY3.

>>> print object.__doc__
The most base type

Сам класс является экземпляром метакласса, который обычно type:

>>> print type.__doc__
type(object) -> the object 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 = 10 или del self.value. Обычно это плохая форма для непосредственного изменения объекта __dict__ и фактически запрещена для класса (т.е. Класс dict завернут, чтобы отключить прямую модификацию). Если вам нужно динамически обращаться к атрибуту, используйте встроенные функции getattr, setattr и delattr.

Модель данных определяет следующие специальные методы для настройки доступа к атрибутам: __getattribute__, __getattr__, __setattr__ и __delattr__. Класс также может определять методы протокола дескриптора __get__, __set__ и __delete__, чтобы определить, как его экземпляры ведут себя как атрибуты. Обратитесь к описателю дескриптора.

При поиске атрибута object.__getattribute__ сначала ищет класс объекта и базовые классы, используя порядок разрешения метода C3 класса:

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

Обратите внимание, что дескриптор данных, определенный в классе (например, a property или member для слота), имеет приоритет над экземпляром 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