Поля класса 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