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 object, elle est valide:
class Sub(object):
pass
s = Sub()
s.attr = 'hello'
L'impression s.attr
affiche «bonjour» comme prévu. pourquoi est-ce le cas? Qu'est-ce qui dans la spécification du langage Python spécifie que vous ne pouvez pas attribuer d'attributs aux objets vanilla?
python
attributes
language-design
Smashery
la source
la source
object
type est immuable et de nouveaux attributs ne peuvent pas être ajoutés? Cela semble être le plus logique.object
des instances de classe, pas sur uneobject
classe.Réponses:
Pour prendre en charge l'attribution arbitraire d'attributs, un objet a besoin d'un
__dict__
: un dict associé à l'objet, où des attributs arbitraires peuvent être stockés. Sinon, il n'y a nulle part où mettre de nouveaux attributs.Une instance de
object
ne transporte pas un__dict__
- si c'était le cas, avant l'horrible problème de dépendance circulaire (puisquedict
, comme presque tout le reste, hérite deobject
;-), cela mettrait en selle chaque objet en Python avec un dict, ce qui signifierait une surcharge de nombreux octets par objet qui n'a actuellement pas ou n'a pas besoin d'un dict (essentiellement, tous les objets qui n'ont pas d'attributs assignables arbitrairement n'ont pas ou n'ont pas besoin d'un dict).Par exemple, en utilisant l'excellent
pympler
projet (vous pouvez l'obtenir via svn à partir d' ici ), nous pouvons faire quelques mesures ...:>>> from pympler import asizeof >>> asizeof.asizeof({}) 144 >>> asizeof.asizeof(23) 16
Vous ne voudriez pas que chacun
int
prenne 144 octets au lieu de seulement 16, non? -)Maintenant, quand vous créez une classe (héritant de quoi que ce soit), les choses changent ...:
>>> class dint(int): pass ... >>> asizeof.asizeof(dint(23)) 184
... le
__dict__
est maintenant ajouté (en plus, un peu plus de frais généraux) - unedint
instance peut donc avoir des attributs arbitraires, mais vous payez un coût d'espace assez élevé pour cette flexibilité.Et si vous vouliez des
int
s avec un seul attribut supplémentairefoobar
...? C'est un besoin rare, mais Python offre un mécanisme spécial à cet effet ...>>> class fint(int): ... __slots__ = 'foobar', ... def __init__(self, x): self.foobar=x+100 ... >>> asizeof.asizeof(fint(23)) 80
...ne pas tout à fait aussi petit comme
int
vous l' esprit! (ou même les deuxint
s, un leself
et un leself.foobar
- le second peut être réaffecté), mais sûrement beaucoup mieux qu'undint
.Lorsque la classe a le
__slots__
attribut spécial (une séquence de chaînes), alors l'class
instruction (plus précisément, la métaclasse par défaut,type
) n'équipe pas chaque instance de cette classe avec un__dict__
(et donc la possibilité d'avoir des attributs arbitraires), juste un fini , ensemble rigide de "slots" (essentiellement des endroits qui peuvent chacun contenir une référence à un objet) avec les noms donnés.En échange de la flexibilité perdue, vous gagnez beaucoup d'octets par instance (probablement significatif uniquement si vous avez des millions d'instances qui circulent, mais il existe des cas d'utilisation pour cela).
la source
__slots__
ne fonctionnent pas avec les types de longueur variable tels questr
,tuple
et en Python 3 aussiint
.__dict__
, sa classe doit-elle avoir un__slot__
attribut?Sub
l'__dict__
attribut et l'objet ne le font pas, étant donné qu'ilsSub
héritentobject
, comment cet attribut (et d'autres comme__module__
) est-il ajouté à l'héritage? Peut-être que cela pourrait être une nouvelle question__dict__
n'est créé que la première fois qu'il est nécessaire, donc la situation de coût de la mémoire n'est pas aussi simple que laasizeof
sortie le laisse paraître. (asizeof
ne sait pas comment éviter la__dict__
matérialisation.) Vous pouvez voir que le dict ne se matérialise pas tant qu'il n'est pas nécessaire dans cet exemple , et vous pouvez voir l'un des chemins de code responsables de la__dict__
matérialisation ici .Comme l'ont dit d'autres répondants, an
object
n'a pas de__dict__
.object
est la classe de base de tous les types, y comprisint
oustr
. Ainsi, tout ce qui leur est fourniobject
sera également un fardeau pour eux. Même quelque chose d'aussi simple qu'un optionnel__dict__
aurait besoin d'un pointeur supplémentaire pour chaque valeur; cela gaspillerait 4 à 8 octets supplémentaires de mémoire pour chaque objet du système, pour une utilité très limitée.Au lieu de faire une instance d'une classe factice, en Python 3.3+, vous pouvez (et devriez) utiliser
types.SimpleNamespace
pour cela.la source
C'est simplement dû à l'optimisation.
Les dictionnaires sont relativement volumineux.
>>> import sys >>> sys.getsizeof((lambda:1).__dict__) 140
La plupart (peut-être toutes) les classes définies en C n'ont pas de dict pour l'optimisation.
Si vous regardez le code source, vous verrez qu'il existe de nombreuses vérifications pour voir si l'objet a un dict ou non.
la source
Alors, en étudiant ma propre question, j'ai découvert ceci à propos du langage Python: vous pouvez hériter de choses comme int, et vous voyez le même comportement:
>>> 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'
Je suppose que l'erreur à la fin est due au fait que la fonction add renvoie un int, donc je devrais remplacer des fonctions comme
__add__
et telles afin de conserver mes attributs personnalisés. Mais tout cela a maintenant un sens pour moi (je pense), quand je pense à "objet" comme "int".la source
C'est parce que l'objet est un "type", pas une classe. En général, toutes les classes définies dans les extensions C (comme tous les types de données intégrés et des trucs comme les tableaux numpy) ne permettent pas l'ajout d'attributs arbitraires.
la source
__dict__
, pour les raisons données par Alex Martelli.https://docs.python.org/3/library/functions.html#object :
la source
C'est (IMO) l'une des limitations fondamentales de Python - vous ne pouvez pas rouvrir les classes. Je pense que le problème réel, cependant, est causé par le fait que les classes implémentées en C ne peuvent pas être modifiées au moment de l'exécution ... les sous-classes le peuvent, mais pas les classes de base.
la source