Mit definierten Eigenschaften bei einer pro-Instanzebene nicht pro-Klasse
-
06-07-2019 - |
Frage
Was ich versuche zu erreichen ist so etwas wie folgt aus:
class object:
def __init__(self):
WidthVariable(self)
print self.width
#Imagine I did this 60frames/1second later
print self.width
#output:
>>0
>>25
Was ich will, geschieht (wie oben): Wenn WidthVariable
- eine Klasse - erstellt wird, fügt die Variable width
auf das Objekt Beispiel: . Diese Variable verhält sich wie eine normale Eigenschaft, aber in diesem speziellen Fall ist es schreibgeschützt ist (nur die fget
Variable gesetzt ist). Darüber hinaus ruft die fget
eine Funktion in WidthVariable
definiert, was width
zurückzukehren entscheidet.
Allerdings habe ich keine Ahnung, wie dies zu tun! Ich versuchte es normal Eigenschaften verwenden, aber ich fand, dass sie nur auf Klassen arbeiten und nicht pro Instanz - Bitte beachten Sie, dass der Code, den ich verwenden soll im __init__
von WidthVariable
den oben (dh nur Code wie möglich ähnlich sein soll, den width
Variable gesetzt, nirgendwo sonst). Auch self.width
kann nicht funktionieren, weil ich nicht das tun, was es wie self.width()
zu nennen, ich will self.width
(weil es mit dem Rest des Designs hält ich habe).
Um zu klären, der vollständige Code würde wie folgt sein:
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
Lösung
Da, wie @ Jonathan sagt, Deskriptoren (einschließlich Eigenschaften) sind pro-Klasse, nicht pro Instanz, die einzige Möglichkeit, verschiedene pro Instanz Deskriptoren zu bekommen, ist jede Instanz zu haben, seine eigene Klasse individualisieren. Das ist ziemlich flach und leicht, so weit wie metaprogramming geht; -) ...:
class Individualistic(object_or_whatever_bases):
def __init__(self, whatever_args):
self.__class__ = type('GottaBeMe', (self.__class__, object), {})
# keep rocking...!-)
Ich füge auch object
ausdrücklich, weil es gebraucht wird (in Python 2.*
, und Sie tun sagen, das ist, was Sie verwenden !!!) Klassen zu machen Neuart. Verwenden Sie niemals Legacy-Klassen mehr, sie verhalten sich nicht korrekt in Bezug auf Eigenschaften und vieles andere (und für die Abwärtskompatibilität können sie nicht - in Python 3, Legacy-Klassen schließlich vernichtet worden, so dass jede Klasse ist neu Stil ohne die Forderung ausdrücklich von Objekt erbt mehr!).
Nun, jeder Beschreiber, die nur in self.__class__.__dict__
gesetzt wird beeinflussen diese eine Instanz, keine andere. Es ist ein wenig Overhead (jede GottaBeMe
Klasse und damit jede Instanz hat seinen eigenen __dict__
, usw.), aber nichts zu schrecklich.
Nun, alles, was gebraucht die ursprüngliche Anforderung zu erfüllen, ist zu ändern:
class WidthVariable:
def __init__(self, object)
object.width = property(self.get_width)
(Umbenennung auch die object
arg vernünftig mit Füßen treten zu vermeiden, ein eingebauter, und macht die Klasse im neuen Stil, weil man immer jede Klasse im neuen Stil machen sollte ;-), zu:
class WidthVariable(object):
def __init__(self, obj)
obj.__class__.width = property(self.get_width)
Nichts zu schwarz-magicky, wie Sie sehen können! -)
Andere Tipps
Ich verstehe nicht, wie Sie Ihr Beispiel aufgebaut hat, noch verstehe ich, was Sie meinen über „normale Eigenschaften“ nur arbeiten „auf Klassen“. Hier ist, wie Sie eine schreibgeschützte Eigenschaft erstellen:
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
Ich glaube, ich jetzt verstehe Ihre Frage, und ich glaube, auch du bist kein Glück .
Für neuen Stil Klassen, impliziter Anrufungen spezieller Methoden sind nur garantiert richtig arbeiten, wenn definiert auf den Typ eines Objekts, nicht in die Instanz Wörterbuch des Objekts.
Descriptors (die Eigenschaften zu implementieren werden verwendet, um) in der erscheinen Klasse __dict__
und kann nicht erscheinen in der Instanz __dict__
. Mit anderen Worten, ist Python Ruby nicht!
ich glücklich erwarten Korrektur von einem gottesfürchtigen Python metaprogrammer, aber ich glaube, ich bin recht.
Nicht ganz klar, was Sie wollen? aber ich nehme an, Sie wollen obj.width für jede Instanz angepasst werden Hier ist eine einfache Möglichkeit, durch Verwendung von Klar Eigenschaften, Breite Eigenschaft gibt Wert durch eine Instanz pro Rückruf zurückgegeben
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
Wenn Rückruf nicht übergeben werden, können wir es WidthVariable zuweisen können, die Breite liefert basierend auf Instanz
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