decorators 参数_Decorators 02 — Decorators里面加点料,decorators with parameters

本文详细介绍了Python中带参数的装饰器的工作原理和实现方式,通过多个代码示例展示了如何创建和使用带参数的装饰器。文章强调了装饰器是Python中强大的工具,允许程序员修改函数或类的行为。文中提到了装饰器的基本语法格式,并解释了如何将待装饰的函数与装饰器参数结合。还讨论了如何通过偏函数`functools.partial`来实现更灵活的参数传递,使得装饰器更具有Pythonic风格。
摘要由CSDN通过智能技术生成

无意中发现几篇很nice的Python英文推文,思路、结构都清晰。在此,结合自己的理解,稍作翻译并进行内容讲解,虽然都是英文的,但是木有想象中的难的,小伙子加油哈!

本节知识点:(1)decorators with parameters的一般写法,(2)   decorators with parameters的Pythonic写法

强烈建议查看我之前的三篇推文

  • 一文读懂Python closure
  • 为何Python中的function被称为first class functions?
  • Decorator 01 当你意识到Python closure夹带的私货是function时,你就懂decorators了 

decorator原理依据

Python functions are First Class citizens which means that functions can be treated similar to objects.

  • Function can be assigned to a variable i.e they can be referenced.
  • Function can be passed as an argument to another function.
  • Function can be returned from a function.

译:这边的 First Class citizens  跟我们之前说的first class objectfirst class function一个意思,老外表述也很骚,也会用比喻,你懂的,嘿嘿嘿,还是那几个意思:Python中函数能够被贴label(贴标签或者说refer)、能够作为其他function的参数、能够作为其他function的返回值

decorators with parameters机理

We know Decorators are very powerful and useful tool in Python since it allows programmers to modify the behavior of function or class. In this article, we will learn about the Decorators with Parameters with help of multiple examples. Decorators with parameters is similar to normal decorators.

译:带参数的decorator和不带参数的差不太多,掌握好我之前所说的closure和decorator概念就ok,不要怕怕,小伙子

8be58963dc77e33ec45f64555011de73.gif

基本语法格式

@decorator(params)
def func_name():
    ''' Function implementation'''

等价于

def func_name():
    ''' Function implementation'''

func_name = (decorator(params))(func_name)
"""

notes: 还记得decorator的两种实现方式不,@ 语法糖 和  函数调用

As the execution starts from left to right decorator(params) is called which returns a function object fun_obj. Using the fun_obj the call fun_obj(fun_name) is made. Inside the inner function, required operations are performed and the actual function reference is returned which will be assigned to func_name. Now, func_name() can be used to call the function with decorator applied on it.

notes: 这些个英文可能把你看怕,其实很简单。

  • decorator(params) 调用以后吐出了个函数t fun_obj,这个函数t fun_obj 你还认识否,这个就是我们当年那个大哥,只不过大哥牛逼了,现在加了层盔甲,仅此而已
  • 后面又是那个故事:大哥吧待装饰的func偷偷塞在小弟的棉袄里,再把小弟吐出来给你;小弟接受和待装饰的函数一样的参数,然后该回结果回结果,同时多加几行代码实现下装饰功能。
# 装饰器  外壳
def outer_shell(*args, **kwargs): 
    
    # 装饰器 大哥 
    def decorators(func): 
        
        # 装饰器小弟
        def wrapper( *args, **kwargs ):
            pass
        
        return wrapper
    
    return decorators #this is the fun_obj mentioned in the above content 

@outer_shell(params) 
def func(): 
    """ 待装饰的函数"""
    pass

代码示例

带parameters的三层decorators

代码示例1、

# 装饰器外壳
def outer_shell(x, y): 

    # 装饰器  大哥 
    def decorator(func): 

        # 装饰器小弟
        def wrapper(*args, **kwargs): 
            print("I like Geeksforgeeks") 
            print("Summation of values - {}".format(x+y) ) 

            func(*args, **kwargs) 
            
        return wrapper 
    return decorators


@outer_shell(12,15)
def my_fun(*args,**kwargs): 
    """
    待装饰的函数
    """
    for arg in args: 
        print(arg, end=" ")
    print()
    
    for key,value in kwargs.items():
        print( "key: {} ---> value: {}".format(key, value)  )
    


my_fun('Geeks', 'for', 'Geeks',name="xiaole",age=18,status = "conding中") 

I like Geeksforgeeks
Summation of values - 27
Geeks for Geeks 
key: name ---> value: xiaole
key: age ---> value: 18
key: status ---> value: conding中

代码等价于

# 装饰器外壳
def outer_shell(x, y): 

    # 装饰器  大哥 
    def decorators(func): 

        # 装饰器小弟
        def wrapper(*args, **kwargs): 
            print("I like Geeksforgeeks") 
            print("Summation of values - {}".format(x+y) ) 

            func(*args, **kwargs) 
            
        return wrapper 
    return decorators



def my_fun(*args,**kwargs): 
    """
    待装饰的函数
    """
    for arg in args: 
        print(arg, end=" ")
    print()
    
    for key,value in kwargs.items():
        print( "key: {} ---> value: {}".format(key, value)  )
    


  # 调用这块不一样
outer_shell(12,15)(my_fun)('Geeks', 'for', 'Geeks',name="xiaole",age=18,status = "conding中") 

I like Geeksforgeeks
Summation of values - 27
Geeks for Geeks 
key: name ---> value: xiaole
key: age ---> value: 18
key: status ---> value: conding中

带parameters的三层decorators,且参数值存在默认值

代码示例2、

# 装饰器外壳
def outer_shell(x=12, y=15): 

    # 装饰器  大哥 
    def decorators(func): 

        # 装饰器小弟
        def wrapper(*args, **kwargs): 
            print("I like Geeksforgeeks") 
            print("Summation of values - {}".format(x+y) ) 

            func(*args, **kwargs) 
            
        return wrapper 
    return decorators


@outer_shell()
def my_fun(*args,**kwargs): 
    """
    待装饰的函数
    """
    for arg in args: 
        print(arg, end=" ")
    print()
    
    for key,value in kwargs.items():
        print( "key: {} ---> value: {}".format(key, value)  )
    


# 调用 
my_fun('Geeks', 'for', 'Geeks',name="xiaole",age=18,status = "conding中") 
I like Geeksforgeeks
Summation of values - 27
Geeks for Geeks 
key: name ---> value: xiaole
key: age ---> value: 18
key: status ---> value: conding中

看到了吧,我的 装饰器外壳 outer_shell 中还可以设置默认参数(本质上就是为函数进行默认参数设置,嘿嘿,没那么高深的)

但是这样子是不是存在一个问题,我们既然选取默认参数设置,然后我们在装饰的时候使用的是@outer_shell(),其实也没什么问题。不过,如果全部使用默认参数,我们使用@outer_shell是不是更加Pythonic 一些,对的,就是这个味道。

Pythonic的带parameters的三层decorators的写法

代码示例3、

带parameters的三层decorators,且参数值存在默认值,

  • 当我们全部使用默认参数的时候 ===》@outer_shell(12,17)
  • 当我们需要制定参数的时候===》@outer_shell

说啥子都没用,直接上代码,不BB

代码初级版本

from functools import partial


# 还是采用两层结构
# 装饰器  大哥 
def decorators(func=None,x=12,y=15): 
    if func is None:
        # print("在if func is None 语句中, x={},y={}".format(x,y))
        return partial(decorators, x=x,y=y )

    # 装饰器小弟
    def wrapper(*args, **kwargs): 
        # print("在wrapper  语句中  x={},y={}".format(x,y))
        print("I like Geeksforgeeks") 
        print("Summation of values - {}".format(x+y) ) 

        func(*args, **kwargs) 

    return wrapper 


def my_fun(*args,**kwargs): 
    """
    待装饰的函数
    """
    print( "*_-"*15 )
    for arg in args: 
        print(arg, end=" ")
    print()
    
    for key,value in kwargs.items():
        print( "key: {} ---> value: {}".format(key, value)  )
    print( "*_-"*15 )
    
# 装饰
my_fun =  decorators(  my_fun )
# 调用 
my_fun('Geeks', 'for', 'Geeks',name="xiaole",age=18,status = "conding中") 
I like Geeksforgeeks
Summation of values - 27
*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-
Geeks for Geeks 
key: name ---> value: xiaole
key: age ---> value: 18
key: status ---> value: conding中
*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-
# 装饰
my_fun =  decorators(  my_fun , x=123 )
# 调用 
my_fun('Geeks', 'for', 'Geeks',name="xiaole",age=18,status = "conding中") 
I like Geeksforgeeks
Summation of values - 138
*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-
Geeks for Geeks 
key: name ---> value: xiaole
key: age ---> value: 18
key: status ---> value: conding中
*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-
    # 装饰
my_fun =  decorators( x=123 )(  my_fun  )
# 调用 
my_fun('Geeks', 'for', 'Geeks',name="xiaole",age=18,status = "conding中") 
I like Geeksforgeeks
Summation of values - 138
*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-
Geeks for Geeks 
key: name ---> value: xiaole
key: age ---> value: 18
key: status ---> value: conding中
*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-

使用语法糖看着高级一点版本,其实都一样,嘿嘿嘿

from functools import partial


# 还是采用两层结构
# 装饰器  大哥 
def decorators(func=None,x=12,y=15): 
    if func is None:
        # print("在if func is None 语句中, x={},y={}".format(x,y))
        return partial(decorators, x=x,y=y )

    # 装饰器小弟
    def wrapper(*args, **kwargs): 
        # print("在wrapper  语句中  x={},y={}".format(x,y))
        print("I like Geeksforgeeks") 
        print("Summation of values - {}".format(x+y) ) 

        func(*args, **kwargs) 

    return wrapper 
    
# 装饰
@decorators
def my_fun(*args,**kwargs): 
    """
    待装饰的函数
    """
    print( "*_-"*15 )
    for arg in args: 
        print(arg, end=" ")
    print()
    
    for key,value in kwargs.items():
        print( "key: {} ---> value: {}".format(key, value)  )
    print( "*_-"*15 )
# 调用 
my_fun('Geeks', 'for', 'Geeks',name="xiaole",age=18,status = "conding中") 
I like Geeksforgeeks
Summation of values - 27
*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-
Geeks for Geeks 
key: name ---> value: xiaole
key: age ---> value: 18
key: status ---> value: conding中
*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-
# 装饰
@decorators(x=1111)
def my_fun(*args,**kwargs): 
    """
    待装饰的函数
    """
    print( "*_-"*15 )
    for arg in args: 
        print(arg, end=" ")
    print()
    
    for key,value in kwargs.items():
        print( "key: {} ---> value: {}".format(key, value)  )
    print( "*_-"*15 )
# 调用 
my_fun('Geeks', 'for', 'Geeks',name="xiaole",age=18,status = "conding中") 
I like Geeksforgeeks
Summation of values - 1126
*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-
Geeks for Geeks 
key: name ---> value: xiaole
key: age ---> value: 18
key: status ---> value: conding中
*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-*_-

不要急,稍作解释你就都懂了

  • 首先,两组代码,一组是用函数赋值、另一组是使用装饰器语法糖@的,二者没有什么本质的区别,知识前一个有助于理解,后一个写法更为简便

  • 其次,我们讲到之前使用默认参数的时候,我们使用@outer_shell()的形式去装饰被调用的函数显得一点都不Pythonic,那么怎么办呢,方法很多,我们这边写一种我掌握的方法。

    简单点,还是退回到两层结构,小弟层就是一个wrapper,没必要懂;改动一下大哥层decorators就ok,两处改动:

    • 输入参数分两块待装饰的函数func装饰器的参数,这边以x,y为例,更一般的,写成*args**kwargs也ok

    • 返回一个原装饰器函数的偏函数,这个偏函数的参数是锁死的,就用给定的值或者,预先设置的默认参数值

  • 最后,最重要的一点,怎么设计转向,带参数、不带参数,搞个if语句一切都解决,简单吧,嘿嘿嘿

简单阐述下工作原理

  • 情形1, 直接使用@decorators 或者my_fun = decorators( my_fun )

    此时等价于my_fun = decorators( my_fun , 默认参数设置),在if 判断中,func is None 不成立,所以后面的就直接走wrapper,我就不说啦,说烂啦

  • 情形2,设置全部参数或者设置部分参数,此时使用@decorators(x=1111)或者 my_fun = decorators( x=123 )(my_fun),看前一个你可能脑子一下子转过来,快看后面那个,你是不是就又反应过来啦:

    • 调用decorators( x=123 ),得的一个偏函数,偏函数的参数x,y都锁定啦(也就是装饰器的参数),x,y的值部分或者全部由用户指定;
    • 随后进入返回的偏函数(也就是参数x,y锁定的decorators函数),这时候我们后面进入执行 my_fun = decorators( x=默认值或者指定值, y=默认值或者指定值)(my_fun),在if 判断中,func is None 不成立,所以后面的就直接走wrapper,我就不说啦,说烂啦

简单吧

c375c203c14c2518ff699567cabac361.gif

附录:偏函数

偏函数不是高深的东西,就是个闭包closure,其返回值是一个function

偏函数用于固定多参数的function中的一些参数,从而简化调用

from functools import partial
def pow(x,n):
    print(  "{}的{}次方是{}".format(x, n, x**n) )
    

pow2=partial(pow ,n=2 )
pow5=partial(pow,n=5)

print( "{}的类型是{}".format( "pow2" , type(pow2) ) )
print( "{}的类型是{}".format( "pow5" , type(pow5) ) )

pow2(3)
pow5(2)
pow2的类型是'functools.partial'>
pow5的类型是'functools.partial'>
3的2次方是9
2的5次方是32

References

[1] GeeksforGeeks Decorators with parameters in Python   https://www.geeksforgeeks.org/decorators-with-parameters-in-python/?ref=lbp

Motto

日拱一卒,功不唐捐

长按下图  关注xiao乐
1c45e83d058d4ef21e3e9ec1a88d5ac1.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值