8

Chcę, aby różne funkcje były wykonywane tylko wtedy, gdy zalogowany użytkownik ma wymagany poziom uprawnień.Dekorator do ustawiania atrybutów funkcji

Aby moje życie było bardziej złożone, po prostu chcę użyć dekoratorów. Poniżej uwidoczniam ustawienie atrybutu permission dla funkcji "dekorowanych" - jak pokazano poniżej.

def permission(permission_required): 
    def wrapper(func): 
     def inner(*args, **kwargs): 
      setattr(func, 'permission_required', permission_required) 
      return func(*args, **kwargs) 
     return inner 
    return wrapper 

@permission('user') 
def do_x(arg1, arg2): 

    ... 

@permission('admin') 
def do_y(arg1, arg2): 
    ... 

Ale kiedy zrobić:

fn = do_x 
if logged_in_user.access_level == fn.permission_required: 
    ... 

otrzymuję błąd 'function' object has no attribute 'permission_required'

Czego mi brakuje?

+1

na marginesie: Jestem całkiem pewien, że chcesz używać [ 'functools.wraps'] (http://docs.python.org/2/library/functools.html#functools.wraps) tutaj. Nie po to, by rozwiązać problem bezpośrednio, ale dlatego, że debugowanie tego rodzaju kodu jest niemożliwe, gdy wszystkie funkcje kończą się na nazwie "wewnętrzna", biorąc "(* args, ** kwargs)', 'sprawdzając' niewłaściwe źródło, itd. – abarnert

Odpowiedz

14

Ustawiasz atrybut w wewnętrznej (opakowaniu) funkcji. Nie trzeba funkcję otoki wcale:

def permission(permission_required): 
    def decorator(func): 
     func.permission_required = permission_required 
     return func 
    return decorator 

Twój dekorator musi powrócić coś że będzie zastąpić pierwotną funkcję. Oryginalna funkcja sama w sobie (z dodanym atrybutem) zrobi to dobrze, ponieważ wszystko, co chcesz zrobić, to dodać do niej atrybut.

Jeśli trzeba jeszcze owijkę, a następnie ustawić atrybut na funkcję owijarki Zamiast:

def permission(permission_required): 
    def decorator(func): 
     def wrapper(*args, **kwargs): 
      # only use a wrapper if you need extra code to be run here 
      return func(*args, **kwargs) 
     wrapper.permission_required = permission_required 
     return wrapper 
    return decorator 

Wszakże wymieniasz owinięte funkcji z owijki zwracanej przez dekoratora, tak to jest obiekt, na który będziesz szukać atrybutu.

+0

Potrzebuję owijki, aby uwzględnić argumenty w funkcji dekorowanej. Użyłem twojego kodu i działało świetnie - ale kiedy dodałem opakowanie dla argumentów, błąd powrócił. – rikAtee

+1

@rikAtee: Nie potrzebujesz opakowania, aby uwzględnić argumenty w funkcji dekorowania. Pierwszy przykład tylko modyfikuje i zwraca funkcję; wciąż ma dokładnie te same argumenty, które robił przed dekoracją. – abarnert

+0

@rikAtee: Rzeczywiście, opakowanie * nie * jest wymagane, jeśli wszystko, co robisz, to ustawienie atrybutu. Dodaj tylko opakowanie, jeśli istnieje potrzeba opakowania (np. Dodaje dodatkowy kod do obsługi argumentów lub wartości zwracanych lub wykonuje dodatkowe czynności po wywołaniu funkcji). –

1

Twój dekorator powinien powrócić funkcję, która może zastąpić do_x lub do_y, nie zwraca wynik realizacji wynosi do_x lub do_y Można modity ozdobić jak poniżej:

def permission(permission_required): 
    def wrapper(func): 
     def inner(): 
      setattr(func, 'permission_required', permission_required) 
      return func 
     return inner() 
    return wrapper 

oczywiście masz inny krótki rozwiązanie :

def permission(permission_required): 
    def wrapper(func): 
     setattr(func, 'permission_required', permission_required) 
     return func 
    return wrapper 
+1

Twoja wewnętrzna funkcja nie ma * nic *, więc zastępujesz oryginał funkcją, która nie robi nic więcej niż ustawienie atrybutu na funkcji zawijanej, czyniąc ją * bezużyteczną *. –

Powiązane problemy