Utilizzo di proprietà definite a livello di istanza non per classe
-
06-07-2019 - |
Domanda
Quello che sto cercando di ottenere è qualcosa del genere:
class object:
def __init__(self):
WidthVariable(self)
print self.width
#Imagine I did this 60frames/1second later
print self.width
#output:
>>0
>>25
Cosa voglio che succeda (come sopra): quando viene creato WidthVariable
- una classe - aggiunge la variabile width
all'oggetto istanza . Questa variabile si comporta come una normale proprietà, ma in questo caso particolare è di sola lettura (è impostata solo la variabile fget
). Inoltre, fget
chiama una funzione definita in WidthVariable
che decide quale larghezza
restituire.
Tuttavia, non ho idea di come farlo! L'ho provato usando le normali proprietà ma ho scoperto che funzionano solo su classi e non per istanza - tieni presente che il codice che uso dovrebbe essere il più simile possibile al precedente (cioè solo il codice all'interno del __init__
di WidthVariable
dovrebbe impostare la variabile width
, da nessun'altra parte). Inoltre, self.width
non può essere funzionale, perché non so come chiamarlo self.width ()
, voglio self.width
(perché continua con il resto del design che ho).
Per chiarire, il codice completo sarebbe qualcosa del genere:
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
Soluzione
Dal momento che, come dice @Jonathan, i descrittori (comprese le proprietà) sono per classe, non per istanza, l'unico modo per ottenere descrittori per istanza diversi è quello di far individuare ciascuna istanza alla propria classe. È piuttosto superficiale e facile per quanto riguarda la metaprogrammazione; -) ...:
class Individualistic(object_or_whatever_bases):
def __init__(self, whatever_args):
self.__class__ = type('GottaBeMe', (self.__class__, object), {})
# keep rocking...!-)
Sto anche aggiungendo oggetto
esplicitamente perché è necessario (in Python 2. *
, e dici che è quello che stai usando !!!) per creare classi di nuovo tipo. Non usare più classi legacy, non si comportano correttamente rispetto alle proprietà e molto altro (e per la retrocompatibilità non possono - in Python 3, le classi legacy sono state finalmente annientate, quindi OGNI classe è di nuovo stile senza l'obbligo di ereditare più esplicitamente dall'oggetto!).
Ora, qualsiasi descrittore inserito in self .__ class __.__ dict__
influenzerà solo questa istanza, nessuna altra. C'è un po 'di overhead (ogni classe GottaBeMe
e quindi ogni istanza ha il suo __dict__
, ecc.), Ma niente di troppo terribile.
Ora, tutto ciò che serve per soddisfare la richiesta originale è cambiare:
class WidthVariable:
def __init__(self, object)
object.width = property(self.get_width)
(anche rinominando oggetto
arg in modo ragionevole per evitare di calpestare un built-in e rendere la classe di nuovo stile perché dovresti SEMPRE rendere SEMPRE un nuovo stile di classe ;-), a:
class WidthVariable(object):
def __init__(self, obj)
obj.__class__.width = property(self.get_width)
Niente di troppo oscuro, come puoi vedere! -)
Altri suggerimenti
Non capisco il modo in cui hai costruito il tuo esempio, né capisco cosa intendi per "proprietà normali". funziona solo "su classi". Ecco come creare una proprietà di sola lettura:
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
Ora credo di capire la tua domanda e credo anche che tu sia per sfortuna .
Per le classi di nuovo stile, implicito invocazioni di metodi speciali sono garantito solo per funzionare correttamente se definito sul tipo di un oggetto, non in il dizionario dell'istanza dell'oggetto.
I descrittori (utilizzati per implementare le proprietà) devono apparire nella classe __dict__
e non possono apparire nella istanza __dict__
. In altre parole, Python non è Ruby!
Attendo felicemente la correzione da un pio metaprogrammatore di Python, ma penso di avere ragione.
Non hai capito bene cosa vuoi? ma suppongo che tu voglia personalizzare obj.width per ogni istanza Ecco un modo semplice usando le proprietà semplici, la proprietà width restituisce il valore restituito da un callback per istanza
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
Se il callback non può essere passato, possiamo assegnarlo a WidthVariable che restituisce larghezza in base all'istanza
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