Итак, я играл с Python, отвечая на этот вопрос , и обнаружил, что это неверно:
o = object()
o.attr = 'hello'
из-за файла AttributeError: 'object' object has no attribute 'attr'
. Однако для любого класса, унаследованного от объекта, это действительно так:
class Sub(object):
pass
s = Sub()
s.attr = 'hello'
При печати s.attr
отображается "привет", как и ожидалось. Почему это так? Что в спецификации языка Python указывает, что вы не можете назначать атрибуты ванильным объектам?
python
attributes
language-design
Smashery
источник
источник
object
тип неизменен, и нельзя добавлять новые атрибуты? В этом есть смысл.object
экземпляров класса, а не дляobject
класса.Ответы:
Для поддержки произвольного назначения атрибутов объекту необходим
__dict__
: a dict, связанный с объектом, в котором могут храниться произвольные атрибуты. Иначе новые атрибуты некуда ставить .Экземпляр
object
вовсе не носить с собой__dict__
- если это так, перед ужасной круговой задачей зависимости (такdict
, как и большинство всего остального, наследуется отobject
;-), это было бы оседлать каждый объект в Python с Dict, что означало бы накладные расходы из многих байт на объект , который в настоящее время не имеет или не нуждается в Dict ( по существу, все объекты , которые не имеют произвольно назначаемые атрибуты не имеют или нужен Dict).Например, используя отличный
pympler
проект (вы можете получить его через svn отсюда ), мы можем провести некоторые измерения ...:>>> from pympler import asizeof >>> asizeof.asizeof({}) 144 >>> asizeof.asizeof(23) 16
Вы же не хотите, чтобы каждый
int
занимал 144 байта вместо 16, верно? -)Теперь, когда вы создаете класс (наследующий от чего угодно), все меняется ...:
>>> class dint(int): pass ... >>> asizeof.asizeof(dint(23)) 184
...
__dict__
будет теперь добавлены (плюс немного больше накладных расходов) - такdint
экземпляр может иметь произвольные атрибуты, но вы платите довольно стоимость пространства для этой гибкости.Так что, если вы хотите
int
s с одним дополнительным атрибутомfoobar
...? Это редкая необходимость, но Python предлагает специальный механизм для этой цели ...>>> class fint(int): ... __slots__ = 'foobar', ... def __init__(self, x): self.foobar=x+100 ... >>> asizeof.asizeof(fint(23)) 80
... не совсем , как крошечное как
int
, заметьте! (или даже дваint
s, one theself
и one theself.foobar
- второй можно переназначить), но, безусловно, намного лучше, чем adint
.Когда у класса есть
__slots__
специальный атрибут (последовательность строк), тоclass
оператор (точнее, метакласс по умолчаниюtype
) не снабжает каждый экземпляр этого класса__dict__
(и, следовательно, возможностью иметь произвольные атрибуты), а только конечным , жесткий набор «слотов» (в основном мест, каждая из которых может содержать одну ссылку на какой-либо объект) с заданными именами.Взамен утраченной гибкость, вы получите много байт на экземпляр (возможно , имеет смысл только если у вас есть несметное экземпляров gallivanting вокруг, но там есть случаи использование для этого).
источник
__slots__
не работают с типами переменной длиной , такими какstr
,tuple
и в Python-тожеint
.__dict__
ли его класс иметь__slot__
атрибут?Sub
этот__dict__
атрибут, а объект - нет, поскольку онSub
наследуется отobject
, как этот атрибут (и другие подобные__module__
) добавляется в наследование? Может быть, это новый вопрос__dict__
создается только в первый раз, когда он нужен, поэтому ситуация с затратами памяти не так проста, какasizeof
кажется на выходе. (asizeof
Не знаю , как избежать__dict__
материализации.) Вы можете увидеть Dict не получает материализовались , пока не нужны в этом примере , и вы можете увидеть один из путей кода , ответственных за__dict__
материализации здесь .Как говорили другие респонденты,
object
у a нет__dict__
.object
является базовым классом всех типов, включаяint
илиstr
. Таким образом, все, что предусмотрено,object
будет для них бременем. Даже такая простая вещь, как необязательный__dict__
, потребует дополнительного указателя для каждого значения; это приведет к потере дополнительных 4-8 байтов памяти для каждого объекта в системе для очень ограниченной полезности.Вместо создания экземпляра фиктивного класса в Python 3.3+ вы можете (и должны) использовать
types.SimpleNamespace
для этого.источник
Это просто из-за оптимизации.
Дикты относительно большие.
>>> import sys >>> sys.getsizeof((lambda:1).__dict__) 140
Большинство (возможно, все) классы, определенные в C, не имеют подсказок для оптимизации.
Если вы посмотрите исходный код, вы увидите, что существует множество проверок, чтобы увидеть, есть ли у объекта dict или нет.
источник
Итак, исследуя свой собственный вопрос, я обнаружил следующее о языке Python: вы можете наследовать от таких вещей, как int, и вы видите то же поведение:
>>> class MyInt(int): pass >>> x = MyInt() >>> print x 0 >>> x.hello = 4 >>> print x.hello 4 >>> x = x + 1 >>> print x 1 >>> print x.hello Traceback (most recent call last): File "<interactive input>", line 1, in <module> AttributeError: 'int' object has no attribute 'hello'
Я предполагаю, что ошибка в конце
__add__
связана с тем, что функция добавления возвращает int, поэтому мне пришлось бы переопределить такие функции , чтобы сохранить мои настраиваемые атрибуты. Но теперь все это имеет для меня смысл (я думаю), когда я думаю об «объекте» как об «int».источник
Это потому, что объект - это «тип», а не класс. В общем, все классы, которые определены в расширениях C (как и все встроенные типы данных, и прочее вроде массивов numpy), не позволяют добавлять произвольные атрибуты.
источник
__dict__
, по причинам, указанным Алексом Мартелли.https://docs.python.org/3/library/functions.html#object :
источник
Это (IMO) одно из фундаментальных ограничений Python - вы не можете повторно открывать классы. Я считаю, что настоящая проблема вызвана тем фактом, что классы, реализованные на C, не могут быть изменены во время выполнения ... подклассы, но не базовые классы.
источник