web-dev-qa-db-fra.com

Impossible de définir des attributs de la classe d'objet

Donc, je jouais avec Python en répondant cette question , et j'ai découvert que ce n'est pas valide:

o = object()
o.attr = 'hello'

en raison d'un AttributeError: 'object' object has no attribute 'attr'. Cependant, avec n'importe quelle classe héritée de l'objet, il est valide:

class Sub(object):
    pass

s = Sub()
s.attr = 'hello'

Impression s.attr Affiche 'Hello' comme prévu. pourquoi est-ce le cas? Quoi dans le Python Spécification de la langue spécifie-t-il que vous ne pouvez pas attribuer d'attributs aux objets vanilla?

83
Smashery

Pour soutenir l'attribution d'attribut arbitraire, un objet nécessite un __dict__: un dict associé à l'objet, où les attributs arbitraires peuvent être stockés. Sinon, il n'y a nulle part à Mettez Nouveaux attributs.

Une instance de object fait non transporter autour d'un __dict__ - Si cela le faisait, avant le problème horrible de dépendance circulaire (puisque dict, comme la plupart des autres, hérite de object ;-), ce serait Selle Tous objet in Python avec un dict, qui signifierait une dépendance de beaucoup octets par objet qui n'a pas ou nécessitent actuellement un dict (essentiellement , tous les objets qui n'ont pas d'attributs assignables arbitrairement n'ont ni n'ont besoin d'un dict).

Par exemple, en utilisant l'excellent projet pympler (vous pouvez l'obtenir via SVN de ici ), nous pouvons faire quelques mesures ...:

>>> from pympler import asizeof
>>> asizeof.asizeof({})
144
>>> asizeof.asizeof(23)
16

Vous ne voudriez pas que tous les int prennent 144 octets au lieu de seulement 16, non? -)

Maintenant, lorsque vous faites une classe (héritante de tout), les choses changent ...:

>>> class dint(int): pass
... 
>>> asizeof.asizeof(dint(23))
184

...les __dict__ est Maintenant ajouté (plus, un peu plus de frais généraux) - donc une instance dint peut avoir des attributs arbitraires, mais vous payez un coût assez d'espace pour cette flexibilité.

Alors, que si vous vouliez ints avec juste un attribut supplémentaire foobar...? C'est un besoin rare, mais Python propose un mécanisme spécial aux fins ...

>>> class fint(int):
...   __slots__ = 'foobar',
...   def __init__(self, x): self.foobar=x+100
... 
>>> asizeof.asizeof(fint(23))
80

... pas tout à fait aussi minuscule qu'un int, pensez-vous! (ou même les deux ints, un, le self et l'un des self.foobar - La seconde peut être réaffectée), mais sûrement beaucoup mieux qu'un dint.

Quand la classe a le __slots__ Attribut spécial (une séquence de chaînes), puis la déclaration class (plus précisément, la métaclasse par défaut, type) pas équiper chaque instance de cette classe avec un __dict__ (et donc la capacité d'avoir des attributs arbitraires), juste un ensemble fini et rigide de "emplacements" (essentiellement des endroits qui peuvent chacun maintenir une référence à un objet) avec les noms donnés.

En échange de la flexibilité perdue, vous obtenez beaucoup d'octets par exemple (probablement significatif que si vous avez des zillions d'instances, mais, là sont Utilisez des cas pour cela).

125
Alex Martelli

Comme les autres répondeurs l'ont dit, un object n'a pas de __dict__. object est la classe de base de tous les types , y compris int ou str. Ainsi, tout ce qui est fourni par object sera un fardeau pour eux aussi. Même quelque chose d'aussi simple qu'un facultatif__dict__ aurait besoin d'un pointeur supplémentaire pour chaque valeur; Cela perdrait 4 à 8 à 8 octets de mémoire pour chaque objet dans le système, pour un utilitaire très limité.


Au lieu de faire une instance d'une classe factice, In Python 3.3+, vous pouvez (et devrait) utiliser types.SimpleNamespace Pour cela.

17
Antti Haapala

C'est simplement dû à l'optimisation.

Les dict sont relativement grands.

>>> import sys
>>> sys.getsizeof((lambda:1).__dict__)
140

La plupart des classes (peut-être toutes) définies dans C n'ont pas de dict d'optimisation.

Si vous regardez le code source Vous verrez qu'il existe de nombreux chèques pour voir si l'objet a une dict ou non.

4
Unknown

C'est parce que l'objet est un "type", pas une classe. En règle générale, toutes les classes définies en extensions C (comme tous les types de données intégrées, et des produits tels que des tableaux numpus) n'autorisent pas l'ajout d'attributs arbitraires.

0
Ryan