我正在尝试在工作中集成一个非常旧的系统和一个较新的系统。我能做的最好的事情就是利用系统使用的RSS消防站类型的饲料。目标是使用此RSS源使其他系统在某些人执行操作时执行某些操作。
我的想法是围绕某些函数包装装饰器,以检查用户(RSS提要中提供的用户ID)是否在新系统中具有权限。
我当前的解决方案有很多看起来像这样的函数,这些函数根据Feed中的action
字段调用:
actions_dict = {
...
'action1': function1
}
actions_dict[RSSFEED['action_taken']](RSSFEED['user_id'])
def function1(user_id):
if has_permissions(user_id):
# Do this function
我想创建一个带有has_permissions
的{{1}}装饰器,这样我就可以删除每个函数中的冗余user_id
检查。
has_permissions
不幸的是,我不知道如何编写这样的装饰器。我看到的所有教程都有@has_permissions(user_id)
def function1():
# Do this function
行,带有硬编码值,但在我的情况下,它需要在运行时传递,并且每次调用函数时都会有所不同。
如何实现此功能?
答案 0 :(得分:6)
在你的问题中,你已经同时命名了user_id
的检查,以及想要的装饰器has_permissions
,所以我将举一个名称更清晰的例子:让我们来看看当颜色(字符串)为'green'
时,创建一个调用底层(装饰)函数的装饰器。
装饰器本身(以下例子中的if_green
)是一个函数。它需要一个函数作为参数进行修饰(在我的示例中名为function
)并返回一个函数(示例中为run_function_if_green
)。通常,返回的函数在某个时刻调用传递的函数,从而使用它之前或之后可能运行的其他操作“装饰”它,或者两者兼而有之。
当然,它可能只是有条件地运行它,因为你似乎需要:
def if_green(function):
def run_function_if_green(color, *args, **kwargs):
if color == 'green':
return function(*args, **kwargs)
return run_function_if_green
@if_green
def print_if_green():
print('what a nice color!')
print_if_green('red') # nothing happens
print_if_green('green') # => what a nice color!
使用装饰器装饰一个函数时会发生什么(就像我在这里使用print_if_green
一样),装饰器(我的例子中的函数工厂,if_green
)被调用函数(print_if_green
正如您在上面的代码中看到的那样)。就其本质而言,它返回不同的功能。 Python然后将原始函数替换为装饰器返回的。
因此,在后续调用中,返回的函数(run_function_if_green
,原始print_if_green
为function
)被调用为print_if_green
,并且有条件地调用该函数原print_if_green
。
对装饰器(if_green
)的调用仅针对每个修饰函数发生一次,而不是每次调用修饰函数时都会发生。但是当装饰器返回时,一次永久替换原始函数,每次调用原始函数时都会调用它来代替原始函数。如果我们允许的话,它可以采取论据。
我给它一个参数color
,它用它来决定是否调用修饰函数。此外,我已经给它了惯用的vararg参数,它用来调用包装函数(如果它调用它),这样我就可以使用任意数量的位置和关键字参数来装饰函数:
@if_green
def exclaim_if_green(exclamation):
print(exclamation, 'that IS a nice color!')
exclaim_if_green('red', 'Yay') # again, nothing
exclaim_if_green('green', 'Wow') # => Wow that IS a nice color!
使用if_green
装饰函数的结果是新的第一个参数被添加到其签名之前,该签名对原始函数是不可见的(因为run_function_if_green
不转发它)。由于您可以自由地实现装饰器返回的函数,它还可以使用更少,更多或不同的参数调用原始函数,在将它们传递给原始函数之前对它们执行任何所需的转换,或者执行其他疯狂的操作。 / p>
理解装饰器需要知识和理解Python语言的各种其他概念。 (其中大多数并非特定于Python,但人们可能仍然不了解它们。)
为了简洁起见(这个答案足够长),我已经跳过或掩盖了大部分内容。对于更全面的快速运行(我认为)所有相关的快速运行,请参考例如Understanding Python Decorators in 12 Easy Steps!
答案 1 :(得分:2)
装饰器(参数,包装函数)的输入在python中是相当静态的。无法像你要求的那样动态传递参数。如果用户id可以在装饰器函数内的运行时从某处提取,那么你可以实现你想要的......
例如,在Django中,像@login_required
这样的东西期望它们包装的函数有request
作为第一个参数,Request
个对象具有user
属性他们可以利用。另一个更丑陋的选择是拥有某种可以从当前用户获取的全局对象(参见thread local storage)。
答案 2 :(得分:1)
首先让我们创建一个可以在执行函数之前执行权限检查的装饰器:
import functools
def check_permissions(user_id):
def decorator(f):
@functools.wraps(f)
def wrapper(*args, **kw):
if has_permissions(user_id):
return f(*args, **kw)
else:
# what do you want to do if there aren't permissions?
...
return wrapper
return decorator
现在,当从字典中提取动作时,使用装饰器将其包装起来以创建一个执行自动权限检查的新callable:
checked_action = check_permissions(RSSFEED['user_id'])(
actions_dict[RSSFEED['action_taken']])
现在,当您致电checked_action
时,它将首先检查与user_id
相对应的权限,然后再执行基础操作。