我是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)调用没有被执行。
我想知道,这里出了什么问题以及如何使用循环来装饰多个导入函数?
感谢您的帮助
答案 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])
尽管如此,这看起来并不优雅。通常在函数本身使用@
- 语法来应用装饰器。