python是否允许我在运行时将动态变量传递给装饰器? [英] Does python allow me to pass dynamic variables to a decorator at runtime?

查看:278
本文介绍了python是否允许我在运行时将动态变量传递给装饰器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在工作中集成一个非常旧的系统和一个较新的系统。我能做的最好的事情就是利用系统利用的RSS消防站类型的提要。目的是使用此RSS feed来使其他系统在某些人执行操作时执行某些操作。



我的想法是将装饰器包装在某些功能周围,以检查用户(RSS feed中提供的用户ID)在新系统中是否具有权限。



我当前的解决方案具有很多类似这样的功能,这些功能基于以下位置的 action 字段进行调用提要:

  actions_dict = {
...
'action1':function1
}

actions_dict [RSSFEED ['action_taken']](RSSFEED ['user_id'])

def function1(user_id):
如果has_permissions(user_id):
#执行此功能

我要创建 has_permissions 装饰器,它使用 user_id ,这样我就可以删除每个我的冗余 has_permissions 支票功能。

  @has_permissions(user_id)
def function1():
#执行此功能

不幸的是,我不确定如何编写这样的装饰器。我看到的所有教程都有带有硬编码值的 @has_permissions()行,但就我而言,它需要在运行时传递,并且每次函数都不同



如何实现此功能?

解决方案

在您的问题,您同时命名了 user_id 的支票和通缉的装饰器 has_permissions 的名称,因此我以一个名称更清楚的示例为例:让我们做一个装饰器,当颜色(字符串)为'green'时,调用底层(装饰的)函数。



Python装饰器是函数工厂



装饰器本身( if_green 在下面的示例中)是一个函数。它需要一个要修饰的函数作为参数(在我的示例中为 function )并返回一个函数(在函数中为 run_function_if_green 例)。通常,返回的函数会在某个时刻调用传递的函数,从而用它可能在其执行之前或之后执行的其他操作(或二者兼而有之)修饰它。



当然,它可能只是有条件地运行它,如您所需要的那样:

  def if_green(function):
def run_function_if_green( color,* args,** kwargs):
如果color =='green':
返回函数(* args,** kwargs)
return run_function_if_green


@if_green
def print_if_green():
print('什么颜色很漂亮!')


print_if_green('red')#没有任何反应
print_if_green('green')#=>多么漂亮的颜色!

用装饰器装饰函数会发生什么(就像我对所做的那样) 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(感叹号):
print(感叹号,``这是一个不错的颜色!'') b
$ b exclaim_if_green('red','Yay')#再说一次,没什么
exclaim_if_green('green','Wow')#=>哇,真漂亮!

if_green 装饰函数的结果是一个新的第一个参数被添加到其签名之前,该签名对于原始函数是不可见的(因为 run_function_if_green 不会转发它)。由于您可以自由地实现装饰器返回的函数,因此它也可以使用更少,更多或不同的参数来调用原始函数,在将其传递给原始函数之前对其进行任何必需的转换,或者执行其他疯狂的工作。 / p>

概念,概念,概念



了解装饰器需要对Python语言的各种其他概念有知识和了解。 (其中大多数不是特定于Python的,但是可能仍然不了解它们。)



为简便起见(此答案就足够长了) ,我已跳过或遮盖了其中大多数。要通过(我认为)所有相关的速度获得更全面的速度,请咨询例如了解12个简单步骤中的Python装饰器!


I am attempting to integrate a very old system and a newer system at work. The best I can do is to utilize an RSS firehouse type feed the system utilizes. The goal is to use this RSS feed to make the other system perform certain actions when certain people do things.

My idea is to wrap a decorator around certain functions to check if the user (a user ID provided in the RSS feed) has permissions in the new system.

My current solution has a lot of functions that look like this, which are called based on an action field in the feed:

actions_dict = {
    ...
    'action1': function1
}

actions_dict[RSSFEED['action_taken']](RSSFEED['user_id'])

def function1(user_id):
    if has_permissions(user_id):
         # Do this function

I want to create a has_permissions decorator that takes the user_id so that I can remove this redundant has_permissions check in each of my functions.

@has_permissions(user_id)
def function1():
    # Do this function

Unfortunately, I am not sure how to write such a decorator. All the tutorials I see have the @has_permissions() line with a hardcoded value, but in my case it needs to be passed at runtime and will be different each time the function is called.

How can I achieve this functionality?

解决方案

In your question, you've named both, the check of the user_id, as well as the wanted decorator has_permissions, so I'm going with an example where names are more clear: Let's make a decorator that calls the underlying (decorated) function when the color (a string) is 'green'.

Python decorators are function factories

The decorator itself (if_green in my example below) is a function. It takes a function to be decorated as argument (named function in my example) and returns a function (run_function_if_green in the example). Usually, the returned function calls the passed function at some point, thereby "decorating" it with other actions it might run before or after it, or both.

Of course, it might only conditionally run it, as you seem to need:

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!

What happens when you decorate a function with the decorator (as I did with print_if_green, here), is that the decorator (the function factory, if_green in my example) gets called with the original function (print_if_green as you see it in the code above). As is its nature, it returns a different function. Python then replaces the original function with the one returned by the decorator.

So in the subsequent calls, it's the returned function (run_function_if_green with the original print_if_green as function) that gets called as print_if_green and which conditionally calls further to that original print_if_green.

Functions factories can produce functions that take arguments

The call to the decorator (if_green) only happens once for each decorated function, not every time the decorated functions are called. But as the function returned by the decorator that one time permanently replaces the original function, it gets called instead of the original function every time that original function is invoked. And it can take arguments, if we allow it.

I've given it an argument color, which it uses itself to decide whether to call the decorated function. Further, I've given it the idiomatic vararg arguments, which it uses to call the wrapped function (if it calls it), so that I'm allowed to decorate functions taking an arbitrary number of positional and keyword arguments:

@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!

The result of decorating a function with if_green is that a new first argument gets prepended to its signature, which will be invisible to the original function (as run_function_if_green doesn't forward it). As you are free in how you implement the function returned by the decorator, it could also call the original function with less, more or different arguments, do any required transformation on them before passing them to the original function or do other crazy stuff.

Concepts, concepts, concepts

Understanding decorators requires knowledge and understanding of various other concepts of the Python language. (Most of which aren't specific to Python, but one might still not be aware of them.)

For brevity's sake (this answer is long enough as it is), I've skipped or glossed over most of them. For a more comprehensive speedrun through (I think) all relevant ones, consult e.g. Understanding Python Decorators in 12 Easy Steps!.

这篇关于python是否允许我在运行时将动态变量传递给装饰器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆