无意中发现几篇很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 object、first 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,不要怕怕,小伙子
基本语法格式
@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,我就不说啦,说烂啦
简单吧
附录:偏函数
偏函数不是高深的东西,就是个闭包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
日拱一卒,功不唐捐