LINUX.ORG.RU

[python][wtf???][o_O] Странное поведение классов.

 


0

0

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

Понимаю, что сумбурно звучит, просто столкнулся тут с ситуацией. Есть базовый класс, в котором инициализируется экземпляр другого класса. И вдруг я обнаруживаю, что именно этот экземпляр является общим для всех экземпляров как самого базового класса, так и любых его наследников.

Вот пример, чтобы было проще понять, что я имею ввиду:

>>> class A(object):
...     num = 1
...
>>> class B(object):
...     a = A()
...
>>> class C(object):
...     a = A()
...
>>> class D(B): <--- Наследник B
...     pass
...
>>> a = A()
>>> b = B()
>>> c = C()
>>> d = D()
>>> a.num, b.a.num, c.a.num, d.a.num
(1, 1, 1, 1)
>>> a
<__main__.A object at 0x8ec5a8c>
>>> b.a
<__main__.A object at 0x8ec590c> <--- WTF??? o_О Адрес совпадает.
>>> c.a
<__main__.A object at 0x8ec59cc>
>>> d.a
<__main__.A object at 0x8ec590c> <--- WTF??? o_О Адрес совпадает.
>>> a.num, b.a.num, c.a.num, d.a.num
(1, 1, 1, 1)
>>> b.a.num = 666 <--- d.a.num не трогаем, обратите внимание
>>> a.num, b.a.num, c.a.num, d.a.num
(1, 666, 1, 666) <--- WTF??? o_О d.a.num изменился вместе с b.a.num.

То есть мне как-то казалось, что если я создал новый экземпляр класса D(), то и входящий в его состав экземпляр класса A() будет создан заново. Но вместо этого я наблюдаю, что этот самый экземпляр будучи раз созданным при определении класса B() просто переходит в свежесозданный экземпляр наследственного класса D(). Другими словами, ведут они себя примерно как статические поля в том же шарпе (про джаву не могу сказать, но там наверное также). А еще заметил, что конструктор вложенного класса вызывается в момент, когда я импортирую класс из модуля даже если я не создаю никаких экземпляров.

Вопрос. Что это за фигня или может так оно и было задумано, просто я дурак? Но в любом случае вопрос, что с этим делать и как добиться переинициализации всех полей при создании каждого нового экземпляра остается открытым. Объявлять все внутри __init__() пока что не хотелось бы.

★★☆
Ответ на: комментарий от YogSagot

>А зачем? Логику можете объяснить?

Потому что это не жаба. Поля собственно объекта создаются в конструкторах. На уровне класса создаются поля *класса*.

Absurd ★★★
()

> Объявлять все внутри __init__() пока что не хотелось бы.

В туторияле есть отдельный раздел про то что такое ООП в понимании питона(особенно class methods). Прочитай и вопросы отпадут. После этого не будешь путать данные(члены?) инстанса с данными класса.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

>В туторияле есть отдельный раздел про то что такое ООП в понимании питона(особенно class methods).

У меня сейчас временно нет интернета (да, да, да, за неуплату отключили), сайт с документацией питона у меня слит телепортом. Всего его облазал, чей-то не припомню там этого.

В общем, к чему это я. Ссылочкой не угостите?

YogSagot ★★☆
() автор топика
Ответ на: комментарий от YogSagot

> У меня сейчас временно нет интернета (да, да, да, за неуплату отключили)

атсрал?

zh
()
Ответ на: комментарий от YogSagot
class A:
   a = 1

Здесь a - это атрибут класса, а не экземпляра. К нему можно обратиться A.a:

>>> A.a
1

_Вдобавок_, значение атрибута класса неявно используется при инициализации одноименного атрибута экземпляра:

>>> t = A()
>>> t.a
1

При желании значение атрибута объекта можно изменить, но это не повлияет на значение атрибута класса:

>>> t.a = 2
>>> t.a
2
>>> A.a
1

Вопросы есть? :)

сайт с документацией питона у меня слит телепортом.

Вендузятнег детектед.

Ссылочкой не угостите?

Глава 9 туториала, «Classes».

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>_Вдобавок_, значение атрибута класса неявно используется при инициализации одноименного атрибута экземпляра

Нет, ничего не инициализируется. В данном случае у экземпляра вообще нет атрибута, он есть только у класса

>>> class A:
... a = 1
...
>>> t = A()
>>> t.__dict__
{}
>>> A.__dict__
{'a': 1, '__module__': '__main__', '__doc__': None}

Стандартный __getattribute__ ищет атрибут в __dict__ экземпляра и далее в родительских классах. Но в экземпляре ничего не инициализируется.

ntp
()
Ответ на: комментарий от ntp

>> _Вдобавок_, значение атрибута класса неявно используется при инициализации одноименного атрибута экземпляра

> Нет, ничего не инициализируется.

Почему-то говорят, что "class attribute defines a default value for instance variable". Не нравится тебе термин "инициализация" - ради ТНБ.

tailgunner ★★★★★
()

Так и задумано, нужен __init__. Без инита тоже нужно, но в других ситуациях ;-)

А вообще, ты только что придумал тест на дибильность при принятии питонщика на работу - возьмем на вооружение

michwill ★★★★★
()

Эй, ты чо. Это же статические переменные.

FeiWongReed
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.