使用循环来装饰Python

时间:2017-01-23 08:25:02

标签: python decorator python-decorators

我是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>

装饰器尚未应用于任何函数,看起来像random()和randint(3,8)调用没有被执行。

我想知道,这里出了什么问题以及如何使用循环来装饰多个导入函数?

感谢您的帮助

3 个答案:

答案 0 :(得分:5)

您正在装饰这些功能,然后依次将名称f绑定到每个功能。所以在循环之后,f将等于最后一个修饰函数。原始功能的原始名称不受影响。

您必须使用名称,例如

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

但我很多更喜欢只是手动将装饰器应用到每个函数。

答案 1 :(得分:3)

我同意Remco&amp; Willem以通常的方式使用@语法会更好,尽管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指出Willem Van 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)

然后我们得到了Remco提到的行为:

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

答案 2 :(得分:2)

您可以通过为random包设置它来执行此操作,如:

import random

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

这里你设置了&#34;属性&#34; random包的内容。另请注意,在for循环中,您可以输入字符串。

然后拨打:

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

尽管如此,这看起来并不优雅。通常在函数本身使用@ - 语法来应用装饰器。