Utilisation de propriétés définies à un niveau par instance et non par classe
-
06-07-2019 - |
Question
Ce que j'essaie de réaliser ressemble à ceci:
class object:
def __init__(self):
WidthVariable(self)
print self.width
#Imagine I did this 60frames/1second later
print self.width
#output:
>>0
>>25
Ce que je souhaite (comme ci-dessus): lorsque WidthVariable
- une classe - est créée, il ajoute la variable width
à l'objet instance . . Cette variable agit comme une propriété normale, mais dans ce cas particulier, elle est en lecture seule (seule la variable fget
est définie). De plus, fget
appelle une fonction définie dans WidthVariable
, qui détermine le width
à renvoyer.
Cependant, je ne sais pas comment faire cela! Je l’ai essayé avec des propriétés normales, mais j’ai constaté qu’elles ne fonctionnent que sur des classes et non par instance. Veuillez noter que le code que j’utilise doit être le plus semblable possible à celui ci-dessus (c’est-à-dire uniquement dans le code __ init __
de WidthVariable
doit définir la variable width
, nulle part ailleurs). De plus, self.width
ne peut pas fonctionner, car je ne sais pas comment l'appeler comme self.width ()
, je veux self.width
. (car il reste avec le reste de la conception que j'ai).
Pour clarifier, le code complet ressemblerait à ceci:
class MyObject:
def __init__(self)
WidthVariable(self)
print self.width
class WidthVariable:
def __init__(self, object)
object.width = property(self.get_width)
def get_width(self):
value = #do_stuff
return value #The Value
#output:
>>25 #Whatever the Value was
La solution
Etant donné que, comme @Jonathan le dit, les descripteurs (y compris les propriétés) sont par classe et non par instance, le seul moyen d'obtenir différents descripteurs par instance est de laisser chaque instance individualiser sa propre classe. C'est assez superficiel et facile en métaprogrammation ;-) ...:
class Individualistic(object_or_whatever_bases):
def __init__(self, whatever_args):
self.__class__ = type('GottaBeMe', (self.__class__, object), {})
# keep rocking...!-)
J'ajoute aussi explicitement objet
parce que c'est nécessaire (en Python 2. *
, et vous dites que c'est ce que vous utilisez !!!) pour faire classes new-type. N'utilisez plus jamais les classes héritées, elles ne se comportent pas correctement en ce qui concerne les propriétés et bien d'autres choses (et pour des raisons de compatibilité ascendante, elles ne le peuvent pas - en Python 3, les classes héritées ont finalement été annihilées; l'exigence de ne plus hériter explicitement d'objet!).
Désormais, tout descripteur placé dans self .__ class __.__ dict __
n’affectera que cette instance, pas une autre. Il y a un peu de surcharge (chaque classe GottaBeMe
et donc chaque instance a son propre __ dict __
, etc.), mais rien de trop terrible.
Maintenant, tout ce dont vous avez besoin pour satisfaire la demande initiale est de changer:
class WidthVariable:
def __init__(self, object)
object.width = property(self.get_width)
(renommer également l'objet
) est judicieux pour éviter de piétiner un élément intégré et de donner à la classe un nouveau style, car vous devriez TOUJOURS faire en sorte que CHAQUE classe soit nouveau: style ;-), à:
class WidthVariable(object):
def __init__(self, obj)
obj.__class__.width = property(self.get_width)
Rien de trop noir-magique, comme vous pouvez le voir! -)
Autres conseils
Je ne comprends pas comment vous avez construit votre exemple, pas plus que ce que vous voulez dire par "propriétés normales". ne travaille que "sur des cours". Voici comment créer une propriété en lecture seule:
class Foo(object):
# create read-only property "rop"
rop = property(lambda self: self._x)
def __init__(self):
self._x = 0
def tick(self):
self._x += 1
f = Foo()
print f.rop # prints 0
f.tick()
f.tick()
print f.rop # prints 2
f.rop = 4 # this will raise AtributeError
Je crois que je comprends maintenant votre question et que vous êtes également sans chance .
Pour les classes de style nouveau, implicite les invocations de méthodes spéciales sont garanti que de fonctionner correctement si défini sur le type d'un objet, pas dans le dictionnaire d'instance de l'objet & # 8217; s.
Les descripteurs (utilisés pour implémenter des propriétés) doivent figurer dans le __ dict __
de la classe et ne peuvent pas apparaître dans le __ dict__ de la classe.
. En d'autres termes, Python n'est pas Ruby!
J'attends avec joie la correction d'un métaprogrammeur pieux de Python, mais je pense avoir raison.
Vous ne savez pas très bien ce que vous voulez? mais je suppose que vous voulez que obj.width soit personnalisé pour chaque instance Voici un moyen simple d’utiliser des propriétés simples. La propriété width renvoie la valeur renvoyée par un rappel par instance
class MyClass(object):
def __init__(self, callback):
self.callback = callback
def get_width(self):
return self.callback()
width = property(get_width)
def w1(): return 0
def w2(): return 25
o1 = MyClass(w1)
o2 = MyClass(w2)
print o1.width
print o2.width
Si le rappel ne peut pas être passé, nous pouvons l'affecter à WidthVariable qui renvoie la largeur en fonction de l'instance
class MyClass(object):
def __init__(self):
self.callback = WidthVariable(self)
def get_width(self):
return self.callback()
width = property(get_width)
class WidthVariable(object):
def __init__(self, obj):
self.obj = obj
def __call__(self):
return hash(self.obj)
o1 = MyClass()
o2 = MyClass()
print o1.width
print o2.width