2016-02-02 65 views
15

Mam klasę, która dekoruje niektóre metody za pomocą dekoratora z innej biblioteki. Konkretnie, klasy podklas zasobów zasobniki kolby, dekoruje metody http z httpauth.HTTPBasicAuth().login_required(), a niektóre sensowne ustawienia domyślne w usłudze modelu.Czy istnieje sposób podpowiedzi na pominięcie dekoracji w metodzie podklasy?

W przypadku większości podklas chcę zastosować dekorator; dlatego wolałbym go usunąć niż dodać do podklas.

Moją myślą jest mieć prywatną metodę, która wykonuje operacje i publiczną metodę, która jest dekorowana. Efektów dekoracji można uniknąć, zastępując metodę publiczną, aby wywołać prywatną, a nie dekorując to zastąpienie. Wyśmiewany przykład poniżej.

Jestem ciekawy, czy istnieje lepszy sposób na zrobienie tego. Czy jest skrót do "anulowania dekoratorów" w python, który daje ten efekt?

Czy możesz polecić lepsze podejście?

Kilka innych pytań zawiera odpowiednie odpowiedzi, np. Is there a way to get the function a decorator has wrapped?. Ale moje pytanie dotyczy szerszego projektu - interesuje mnie pytoniczny sposób, aby uruchomić operacje w dekorowanych metodach bez efektów dekoracyjnych. Na przykład. mój przykład jest taki, ale mogą istnieć inne.

def auth_required(fn): 
    def new_fn(*args, **kwargs): 
     print('Auth required for this resource...') 
     fn(*args, **kwargs) 
    return new_fn 

class Resource: 
    name = None 

    @auth_required 
    def get(self): 
     self._get() 

    def _get(self): 
     print('Getting %s' %self.name) 

class Eggs(Resource): 
    name = 'Eggs' 

class Spam(Resource): 
    name = 'Spam' 

    def get(self): 
     self._get() 
     # super(Spam, self)._get() 

eggs = Eggs() 
spam = Spam() 

eggs.get() 
# Auth required for this resource... 
# Getting Eggs 

spam.get() 
# Getting Spam 
+2

Możliwy duplikat [Czy istnieje sposób, aby uzyskać funkcję, którą dekorator zawinął?] (Http://stackoverflow.com/questions/1545178/is- tam-a-sposób-by-uzyskać-funkcję-a-dekorator-owinął) – Oin

Odpowiedz

9

Flask-HTTPAuth wykorzystuje functools.wraps w login_required dekoratora:

def login_required(self, f): 
    @wraps(f) 
    def decorated(*args, **kwargs): 
     ... 

Od Python 3.2, jak to nazywa update_wrapper, można uzyskać dostęp do pierwotnej funkcji poprzez __wrapped__:

Aby umożliwić dostęp do pierwotnej funkcji do introspekcji i innych celów (np. z pominięciem dekoratora buforowania, takiego jak lru_cache()), ta funkcja automatycznie dds atrybutu __wrapped__ do opakowania , które odnosi się do pakowanej funkcji.

Jeśli piszesz własnych dekoratorów, jak w przykładzie, można również użyć @wraps aby uzyskać taką samą funkcjonalność (jak również utrzymanie docstrings, itd.).

Zobacz także Is there a way to get the function a decorator has wrapped?

+0

To działa ładnie - choć może potrzebować alternatywy, gdy jest więcej dekoracji. Odpowiedź zaakceptowana i pytanie zaktualizowano przy użyciu –

+0

@JothamApaloo, o ile wszystkie są '@ wraps', możesz po prostu kontynuować, aż dotrzesz do czegoś, co nie ma tego atrybutu! Pamiętaj, że nie powinieneś umieszczać odpowiedzi w pytaniu. – jonrsharpe

+0

Dzięki @jonrsharpe. Zastanawiałem się nad tym, czy anulować tylko "@ with_required", ale pozwolić innym pozostać. Zajmę się tym, kiedy zajdzie taka potrzeba, i może zaktualizuję to. Ponadto chciałem umieścić odpowiedź w komentarzu, ale nie sformatowano poprawnie. Czy protokół jest po prostu dodawany jako odpowiedź? Myślę, że to oczywiste, ponieważ jest to "odpowiedź", znoś się i sprawdź :) –

2

Innym powszechnym rozwiązaniem jest mieć funkcja urządzone zachować kopię pierwotnej funkcji, które mogą być dostępne:

def auth_required(fn): 
    def new_fn(*args, **kwargs): 
     print('Auth required for this resource...') 
     fn(*args, **kwargs) 
    new_fn.original_fn = fn 
    return new_fn 

Teraz dla każdej funkcji, która została ozdobiona, można uzyskać dostęp do jego original_fn atrybut, aby uzyskać uchwyt do oryginalnej, nieuporządkowanej funkcji.

W takim przypadku można zdefiniować typ dyspozytora, który wykonuje zwykłe wywołania funkcji (gdy użytkownik jest zadowolony z zachowania dekoratora) lub wykonuje połączenia z numerem thing.original_fn, jeśli wolą uniknąć zachowania dekoratora.

Twoja proponowana metoda jest również ważnym sposobem jej struktury, a to, czy moja sugestia jest "lepsza", zależy od reszty kodu, z którym masz do czynienia, kto musi go przeczytać, i innych rodzajów kompromisów. .

0

Jestem ciekaw, czy istnieje lepszy sposób to zrobić. Czy istnieje skrót "anulowanie dekoratorów" w pythonie, który daje ten efekt?

Użyj biblioteki undecorated. Przeszukuje wszystkie dekoratory i zwraca tylko oryginalną funkcję. Dokumenty powinny być zrozumiałe, w zasadzie wystarczy zadzwonić: undecorated(your_decorated_function)