Pytanie Konstruktor metody klasy Python z własnymi argumentami?


Jak przekazać pole klasy do dekoratora w metodzie klasy jako argument? Co chcę zrobić, to coś takiego:

class Client(object):
    def __init__(self, url):
        self.url = url

    @check_authorization("some_attr", self.url)
    def get(self):
        do_work()

Narzeka, że ​​self nie istnieje dla przekazania self.url dekoratorowi. Czy istnieje sposób obejścia tego?


76
2017-07-30 23:30


pochodzenie


Czy jest to niestandardowy dekorator, nad którym masz kontrolę, lub który nie możesz zmienić? - Joel Cornett
To mój dekorator, więc mam nad nim całkowitą kontrolę - Mark
To jest wywoływane przed init, myślę, że jest problem ... - Joran Beasley
Problem polega na tym, że self nie istnieje w czasie definicji funkcji. Musisz przekształcić go w funkcję częściową. - Antimony


Odpowiedzi:


Tak. Zamiast podawać atrybut instance w czasie definicji klasy, sprawdź go w czasie wykonywania:

def check_authorization(f):
    def wrapper(*args):
        print args[0].url
        return f(*args)
    return wrapper

class Client(object):
    def __init__(self, url):
        self.url = url

    @check_authorization
    def get(self):
        print 'get'

>>> Client('http://www.google.com').get()
http://www.google.com
get

Dekorator przechwytuje argumenty metody; pierwszym argumentem jest instancja, więc odczytuje atrybut z tego. Możesz przekazać nazwę atrybutu jako ciąg do dekoratora i użyć getattr jeśli nie chcesz kodować nazwy atrybutu:

def check_authorization(attribute):
    def _check_authorization(f):
        def wrapper(self, *args):
            print getattr(self, attribute)
            return f(self, *args)
        return wrapper
    return _check_authorization

113
2017-07-30 23:38





from re import search
from functools import wraps

def is_match(_lambda, pattern):
    def wrapper(f):
        @wraps(f)
        def wrapped(self, *f_args, **f_kwargs):
            if callable(_lambda) and search(pattern, (_lambda(self) or '')): 
                f(self, *f_args, **f_kwargs)
        return wrapped
    return wrapper

class MyTest(object):

    def __init__(self):
        self.name = 'foo'
        self.surname = 'bar'

    @is_match(lambda x: x.name, 'foo')
    @is_match(lambda x: x.surname, 'foo')
    def my_rule(self):
        print 'my_rule : ok'

    @is_match(lambda x: x.name, 'foo')
    @is_match(lambda x: x.surname, 'bar')
    def my_rule2(self):
        print 'my_rule2 : ok'



test = MyTest()
test.my_rule()
test.my_rule2()

ouput:   my_rule2: ok


21
2018-05-03 11:52



Dziękuję bardzo! To powinna być zaakceptowana odpowiedź. Rozszarpałam włosy, próbując wymyślić dekoratorów metod klasowych. - benshepherd
@raphael W tej konfiguracji nie mogę uzyskać dostępu do _lambda lub wzorca. Jak mogę to naprawić? - Jonathan
@Raphael: Jak mogę zrobić to samo dla metody class, ponieważ tutaj wszystkie metody są metodami instancji. - Apurva Kunkulol


Bardziej zwięzły przykład może wyglądać następująco:

#/usr/bin/env python3
from functools import wraps

def wrapper(method):
    @wraps(method)
    def _impl(self, *method_args, **method_kwargs):
        return method(self, *method_args, **method_kwargs) + "!"
    return _impl

class Foo:
    @wrapper
    def bar(self, word):
        return word

f = Foo()
result = f.bar("kitty")
print(result)

Który wydrukuje:

kitty!

15
2018-04-29 18:13





Nie możesz. Nie ma self w ciele klasy, ponieważ nie istnieje żadne wystąpienie. Musisz przekazać to, powiedzmy, a str zawierające nazwę atrybutu do wyszukiwania na instancji, którą może następnie wykonać zwrócona funkcja, lub całkowicie inną metodę.


2
2017-07-30 23:38