La confusion est probablement dans l'instruction obj.var += 1
.
Cela revient à obj.var = obj.var + 1
. Le côté droit ne peut pas trouver var
sur l'objet, donc il délègue à la classe. Cependant, le côté gauche stocke le résultat sur l'objet. À partir de ce moment-là, en recherchant var
sur l'objet, la délégation vers l'attribut de la classe ne se fera plus.
>>> class Test():
>>> var = 1
>>> Test.__dict__
mappingproxy({'__module__': '__main__',
'var': 1,
'__dict__': ,
'__weakref__': ,
'__doc__': None})
>>> obj = Test()
>>> obj.__dict__
{}
>>> obj.var += 1
>>> obj.__dict__
{'var': 2}
Si vous supprimez l'attribut de l'objet, l'attribut de la classe est de nouveau exposé :
>>> Test.var += 5
>>> obj.var
2
>>> del obj.var
>>> obj.var
6
Extrait pertinent de la documentation:
Une instance de classe possède un espace de noms implémenté sous forme de dictionnaire qui est le premier endroit où les références aux attributs sont recherchées. Lorsqu'un attribut n'est pas trouvé là-bas et que la classe de l'instance a un attribut avec ce nom, la recherche se poursuit avec les attributs de la classe.
Concernant votre question suivante, voici une façon de faire ce que vous voulez en utilisant descripteurs de données, bien que je ne pense pas que ce soit très pythonique. En particulier, il est inhabituel d'avoir des descripteurs de données sur des classes (et cela peut causer des problèmes si vous n'êtes pas vigilant).
class VarDescriptorMetaclass(type):
"""Métaclasse pour permettre aux setters de descripteurs de données de fonctionner sur les types."""
def __setattr__(self, name, value):
setter = getattr(self.__dict__.get(name), '__set__')
return setter(None, value) if setter else super().__setattr__(name, value)
class VarDescriptor(object):
"""Classe de descripteur de données pour prendre en charge la mise à jour de la propriété à la fois via la classe et l'instance."""
def __init__(self, valeur_initiale):
self.value = initial_value
def __get__(self, obj, objtype):
if obj and hasattr(obj, '_value'):
return self.value + obj._value
return self.value
def __set__(self, obj, value):
if obj:
obj._value = value - self.value
else:
self.value = value
def __delete__(self, obj):
if obj and hasattr(obj, '_value'):
del obj._value
class Test(metaclass=VarDescriptorMetaclass):
var = VarDescriptor(initial_value=1)
Cela semble faire ce que vous voulez :
>>> obj = Test()
>>> obj.var
1
>>> obj.var += 1
>>> obj.var
2
>>> Test.var += 5
>>> Test.var
6
>>> obj.var
7