2017-01-23 8 views
6

Я новичок в Python и декораторах, поэтому извиняюсь, если это кажется тривиальным вопросом.Использование циклов для украшения нескольких импортированных функций в Python

Я пытаюсь применить декоратор к нескольким импортируемым функциям, используя цикл в Python, как показано ниже

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]) 

В идеале, я ожидаю, что выход будет в таком виде:

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

Но, Я получаю результат функции выбора как результат.

<random_choice among 4,5 6> 

декоратор не был применен к любой из функций, и это также выглядит как случайная() и randint (3,8) звонки не получают казнены.

Я хотел бы знать, что здесь не так, и что можно сделать, чтобы украсить несколько импортированных функций с помощью циклов?

Спасибо за помощь

ответ

5

Ваши функции украшают, а затем привязывают имя f к каждому из них по очереди. Поэтому после цикла f будет равен последней украшенной функции. Исходные имена для оригинальных функций не затрагиваются.

Вы должны работать с именами, что-то вроде

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

Но я бы много предпочитают просто применить декоратор к каждой функции, в свою очередь, вручную.

2

Вы можете сделать это, установив его для random пакета, как:

import random 

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

Здесь, таким образом, установить «атрибут» из random пакета. Также обратите внимание, что в вашем цикле for вы подаете строки.

, а затем вызвать с:

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

Тем не менее, это не выглядит очень элегантно. Декоратор обычно применяется, используя @ -syntax для самой функции.

3

Я согласен с Ремко & Willem, что использование @ синтаксис обычным способом было бы лучше, хотя подход Виллема модификации атрибутов импортируемого random модуля, вероятно, лучше, чем человек обработки globals().Но вот еще один способ:

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]) 

выход

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

В комментариях, RemcoGerlich указывает на то, что техника Виллема Ван Onsem о декорировании атрибуты random модуля означает, что любой другой импортирован модули, которые используют декорированные функции в random, также будут затронуты. Это совершенно правильно, если в другом модуле используется стандартный оператор import random. Я предполагаю, что в некоторых случаях такое поведение может быть действительно желательным.

Однако есть способ, что если у вас есть контроль над кодом в другом модуле. Если в другом модуле используется форма from random import choice, тогда он получит незакодированную версию choice. Вот короткая демонстрация.

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)) 

Типичный выход

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

Если dectest.py выглядит следующим образом:

import random 

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

то получит поведение, которое РЕМКО упоминает:

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

Но на самом деле изменения функции в случайном модуле также изменяет другие библиотеки, которые происходят использовать этот модуль, который может быть опасным. Globals() необычен и является признаком того, что делает вещи, которые, вероятно, не нужны, но это не особенно опасно. – RemcoGerlich

+0

Насколько я знаю, это не изменяет функцию * глобально *: вы устанавливаете только разные функции для текущего файла. –

+0

@WillemVanOnsem: да, но это решение фактически меняет функции в случайном модуле, другой код тоже это увидит. – RemcoGerlich

 Смежные вопросы

  • Нет связанных вопросов^_^