2017-01-23 10 views
6

Jestem nowym użytkownikiem Pythona i dekoratorów, więc przepraszam, jeśli wydaje się to być trywialnym pytaniem.Używanie pętli do dekoracji wielu importowanych funkcji w Pythonie

Próbuję zastosować dekoratorów do wielu importowanych funkcji za pomocą pętli w Pythonie, jak pokazano poniżej

from random import random, randint, choice 

def our_decorator(func): 
    def function_wrapper(*args, **kwargs): 
     print("Before calling " + func.__name__) 
     res = func(*args, **kwargs) 
     print(res) 
     print("After calling " + func.__name__) 
    return function_wrapper 

for f in [random, randint, choice]: 
    f = our_decorator(f) 

random() 
randint(3, 8) 
choice([4, 5, 6]) 

idealnie, spodziewam wyjście być w tej formie:

Before calling random 
<random_value> 
After calling random 
Before calling randint 
<random_integer> 
After calling randint 
Before calling choice 
<random_choice> 
After calling choice 

Ale Otrzymuję tylko wynik funkcji wyboru jako wynik.

<random_choice among 4,5 6> 

Dekorator nie została zastosowana do dowolnej funkcji i to również wygląda random() i randint (3,8) połączenia nie są coraz wykonywany.

Chciałbym wiedzieć, co tu jest nie tak i co można zrobić, aby ozdobić wiele zaimportowanych funkcji za pomocą pętli?

Dzięki za pomoc

Odpowiedz

5

Twoje ozdoby funkcji, a następnie wiążące nazwę f do każdego z nich po kolei. Więc po pętli f będzie równy ostatnio dekorowanej funkcji. Oryginalne nazwy oryginalnych funkcji pozostają nienaruszone.

Będziesz musiał pracować z nazwiskami, coś jak

gl = globals() 
for f_name in ['random', 'randint', 'choice']: 
    gl[f_name] = our_decorator(gl[f_name]) 

Ale chciałbym znacznie wolą po prostu zastosować dekorator do każdej funkcji z kolei ręcznie.

2

Można to zrobić poprzez ustawienie go na opakowaniu random jak:

import random 

for f in ['random','randint','choice']: 
    setattr(random,f,our_decorator(getattr(random,f))) 

Tu więc ustawić „atrybut” pakietu random. Zwróć też uwagę, że w pętli for podajesz ciągi.

a następnie zadzwonić z:

random.random() 
random.randint(3, 8) 
random.choice([4, 5, 6]) 

Niemniej jednak, to nie wygląda bardzo elegancko. Dekorator jest zwykle stosowany przy użyciu funkcji @ -syntax.

3

zgadzam się z Remco & Willem że używając @ składni w zwykły sposób, byłoby lepiej, chociaż podejście Willem za modyfikowania atrybutów importowanego modułu random jest chyba lepiej niż człowiek obsługi globals().Ale tu jest inna droga:

from random import random, randint, choice 

def our_decorator(func): 
    def function_wrapper(*args, **kwargs): 
     print("Before calling " + func.__name__) 
     res = func(*args, **kwargs) 
     print(res) 
     print("After calling " + func.__name__) 
    return function_wrapper 

random, randint, choice = [our_decorator(f) for f in (random, randint, choice)] 

random() 
randint(3, 8) 
choice([4, 5, 6]) 

wyjście

Before calling random 
0.8171920550436872 
After calling random 
Before calling randint 
8 
After calling randint 
Before calling choice 
4 
After calling choice 

W komentarzach, RemcoGerlich wskazuje, że technika Willem van Onsem za zdobienia atrybuty modułu random oznacza, że ​​każdy inny importowany Moduły, które wykorzystują udekorowane funkcje w random, również zostaną naruszone. Jest to całkowicie poprawne, jeśli drugi moduł używa standardowej instrukcji import random. Sądzę, że w pewnych okolicznościach takie zachowanie może być pożądane.

Istnieje jednak sposób, że jeśli masz kontrolę nad kodem w drugim module. Jeśli drugi moduł używa formularza from random import choice, otrzyma on nieskażoną wersję choice. Oto krótkie demo.

dectest.py

#!/usr/bin/env python3 

from random import choice 

def test(seq): 
    print('In test') 
    return choice(seq) 

import random 
import dectest 

def our_decorator(func): 
    def function_wrapper(*args, **kwargs): 
     print("Before calling " + func.__name__) 
     res = func(*args, **kwargs) 
     print(res) 
     print("After calling " + func.__name__) 
     return res 
    return function_wrapper 

f = 'choice' 
setattr(random, f, our_decorator(getattr(random, f))) 

a = [4, 5, 6, 7] 
print(random.choice(a)) 

print('dectest') 
print(dectest.test(a)) 

typowa produkcja

Before calling choice 
6 
After calling choice 
6 
dectest 
In test 
7 

Jeśli dectest.py wygląda następująco:

import random 

def test(seq): 
    print('In test') 
    return random.choice(seq) 

potem uzyskać zachowanie które Remco wspomina:

Before calling choice 
5 
After calling choice 
5 
dectest 
In test 
Before calling choice 
4 
After calling choice 
4 
+0

Ale faktycznie zmienia funkcje w module losowej zmienia również innych bibliotek, które dzieją się w użyciu ten moduł, który może być niebezpieczny. Globals() jest niezwykły i jest oznaką robienia rzeczy, które prawdopodobnie nie są potrzebne, ale nie jest szczególnie niebezpieczne. – RemcoGerlich

+0

O ile mi wiadomo, nie zmienia to funkcji * globalnie *: ustawia się tylko różne funkcje dla bieżącego pliku. –

+0

@WillemVanOnsem: tak, ale twoje rozwiązanie faktycznie zmienia funkcje w module losowym, inny kod też to zobaczy. – RemcoGerlich

Powiązane problemy