In uno dei miei progetti, ho anche bisogno di fare una cosa in particolare, vale a dire che anche l'oggetto sottostante dovrebbe realmente eseguire il metodo che è stato reimplementato nel decoratore. In realtà è abbastanza facile da fare se sai dove indirizzarlo.
Il caso di utilizzo è:
- Ho un oggetto X con metodi A e B.
- Creo una classe decoratore Y che sostituisce A.
- Se si crea un'istanza Y (X) e si chiama A, verrà utilizzata la A decorata come previsto.
- Se B chiama A, quindi se istanzia Y (X) e chiama B sul decoratore, la chiamata dall'interno di B quindi passa alla vecchia A dell'oggetto originale che non era desiderabile. Voglio che anche la vecchia B chiami la nuova A.
E 'possibile raggiungere questo comportamento come questo:
import inspect
import six # for handling 2-3 compatibility
class MyBaseDecorator(object):
def __init__(self, decorated):
self.decorated = decorated
def __getattr__(self, attr):
value = getattr(self.decorated, attr)
if inspect.ismethod(value):
function = six.get_method_function(value)
value = function.__get__(self, type(self))
return value
class SomeObject(object):
def a(self):
pass
def b(self):
pass
class MyDecorator(MyBaseDecorator):
def a(self):
pass
decorated = MyDecorator(SomeObject())
Questo non può funzionare out of the box, come ho digitato tutto il resto a parte il metodo di getattr dalla parte superiore della testa.
Il codice cerca l'attributo richiesto nell'oggetto decorato e se è un metodo (non funziona per le proprietà ora, ma la modifica per supportarle non dovrebbe essere troppo difficile), il codice quindi attira l'effettivo funzione fuori dal metodo e usando l'invocazione dell'interfaccia del descrittore "ricollega" la funzione come metodo, ma sul decoratore. Quindi viene restituito e molto probabilmente eseguito.
L'effetto di questo è che se b
chiama sempre a
sull'oggetto originale, quindi quando l'oggetto è decorato e c'è una chiamata di metodo proveniente dal decoratore, il decoratore si assicura che tutti i metodi a cui si accede siano vincolati al decoratore invece, quindi cercare le cose usando il decoratore e non l'oggetto originale, quindi i metodi specificati nel decoratore hanno la precedenza.
P.S .: Sì, lo so che assomiglia molto all'eredità, ma questo nel senso della composizione di più oggetti.
fonte
2017-12-06 15:38:50
Puoi fare un esempio in cui semplicemente la sottoclasse non funzionerebbe? Puoi anche sottoclassi dinamicamente - questo modello sembra una soluzione alternativa per le lingue che non possono farlo o che non supportano più inheritage. –
Voglio decorare oggetti in fase di esecuzione. Voglio applicare diversi decoratori a un oggetto ed essere in grado di rimuoverli di nuovo. La sottoclasse non può cambiare un'istanza dopo che è stata creata, o può? –
Se avete la classe 'A' e cambiate' A', cioè aggiungendo un nuovo metodo, 'A.foo = lambda self: self' questo si rifletterà su tutte le istanze di A .. perché * tutto * è determinato in fase di esecuzione. Ottimo modo per produrre codice assolutamente non gestibile. –