0
点赞
收藏
分享

微信扫一扫

Python 装饰器

目录

补充说明:函数也是对象

对象其实就是一个属性的集合,函数里面各种代表了某种属性的变量或参数等等,类里面代表了某种函数对象或者某种属性的变量,函数对象里面可能有代表某种属性的变量,而其中又可能有函数对象,而函数对象里面可能又有代表某种属性的变量,而且又有某种函数对象…
这样子想下去,大概都要乱了。这里对象是应用到了各种编程语言 —— Java、C++、Python、JavaScript 等等

参考链接1:
参考链接 2:

如何理解函数作为Python 装饰器的参数

理解 Python 装饰器,首先要先知道 Python 的函数可以嵌套的,返回值也是一个非常绕的情况,那么下面就用例子来帮助快速理解装饰器的具体的情况。

#funA 作为装饰器函数
def funA(fn):
    #...
    fn() # 执行传入的fn参数
    #...
    return '...'
@funA
def funB():
    #...

实际上,上面程序完全等价于下面的程序:

def funA(function):
    # function() 执行前的上文
    function() # 执行传入的function参数,这里也就是 funB
    # function() 执行后的下文
    return '...'
def funB():
    #.
funB = funA(funB)   # 注意这里的是 funB 而不是 funB() 没有括号()的

funB作为对象传递给了 function 参数,而且在执行 function() 这个函数前后,都可以添加装饰用的程序,如运行前输出文字,打个招呼“Hello”,运行完 function() ,则输出“The programing is successful.” 等等。

理解装饰器的返回值

# test.py
#funA 作为装饰器函数
def funA(function):
    print("这是装饰的上文")
    print(function.__name__+"函数返回值: ",end='')
    function() # 执行传入的fn参数
    print("这里是装饰下文")
    return "装饰器函数的返回值,这里是一个字符串变量,而不是一个函数对象"
@funA
def funB():
    print("学习 Python")
    
print(funB)
#print(funB())

而这里的 return 返回值是一个字符串变量,那么返回值传递给了 funB ,这一过程其实就是等同于 funB = "字符串变量" ,而如果是return function ,也就是 funB = function 这一过程,那么就是将函数对象传递给一个函数对象。

装饰带参数的函数

下面先举一个简单的例子来理解如何装饰带参数的函数,在函数装饰器中嵌套一个函数,该函数带有的参数个数和被装饰器修饰的函数相同。例如:

# test.py
def funA(function):
    # 定义一个嵌套函数
    def real_function(args):
        print("真正执行的函数其实是",args)
    return real_function
@funA
def funB(args):
    print("funB():", a)  # 这里没有被执行到,因为funB = funA(function)
    
funB(funB.__name__)   # funB 的参数被传递给了 real_function的 args 参数

输出显示:

真正执行的函数其实是 real_function

理解 return real_function ,就要理解 pass 关键字
上面的类似如下所示:

def funA(function):
    # 定义一个嵌套函数
    def real_function(args):        # 等同于 pass
        print("真正执行的函数其实是",args)  
    return real_function          # 直接运行到 return 这里

装饰多参数的函数

那么如果需要装饰多个函数且这多个函数的参数有可能不一致的情况,就需要使用未知数量参数 * 和关键字参数 ** 传参了。

参考链接:https://blog.csdn.net/qq_42701659/article/details/123428242?spm=1001.2014.3001.5501

# test.py
def decorator(function):     # 装饰器函数
    # 定义一个嵌套函数
    def accept_args(*args,**kwargs):
        function(*args,**kwargs)
        
    return accept_args
    
@decorator
def function_A(args):
    print(args," function_A 的单个参数输出")
    
@decorator
def function_B(name,args):
    print(name,args)
function_A(function_A.__name__)
function_B(function_B.__name__," function_B 的两个参数输出")

输出结果:

accept_args  function_A 的单个参数输出
accept_args  function_B 的两个参数输出

你运行一番,就有可能发现运行 function_A()function_B() 其实都是在运行 accept_args(),看似是运行 function_A()function_B() ,是因为 accept_args 嵌套函数内有 function(*args,**kwargs) 等同于在运行 function_A()function_B()必须记住嵌套函数的参数,是通过夺取被装饰的函数参数来的。

关于函数装饰器可以嵌套的问题

@funA
@funB
@funC
def fun():
    #...
fun = funA( funB ( funC (fun) ) )

装饰器嵌套装饰器,代码演示:

# test.py
def funA(function):     # 装饰器函数
    # 定义一个嵌套函数
    def funaWrapper():
        print(" 3 执行 funA装饰器")
        function()
        
    return funaWrapper
        

def funB(function):
    def funbWrapper():
        print(" 2 执行 funB 装饰器")
        function()
    return funbWrapper

def funC(function):
    def funcWrapper():
        print(" 1 执行 funC 装饰器")
        function()
    return funcWrapper
    
@funA
@funB
@funC
def fun():
	print(" 0 执行 fun()")
    
print(fun())

输出显示:

 3 执行 funA装饰器
 2 执行 funB 装饰器
 1 执行 funC 装饰器
 0 执行 fun()
None

深入理解装饰器间的嵌套,代码演示:

# test.py
def funA(fun):     # 装饰器函数
    # 定义一个嵌套函数
    def funaWrapper():
        print(" 3 执行 funA装饰器")
        result = funbWrapper()
        return result
    return funaWrapper
        

def funB(fun):
    def funbWrapper():
        print(" 2 执行 funB 装饰器")
        result = funcWrapper()
        return result
    return funbWrapper

def funC(fun):
    def funcWrapper():
        print(" 1 执行 funC 装饰器")
        result = fun()
        return result
    return funcWrapper
    
@funA
@funB
@funC
def fun():
    print(" 0 执行 fun()")
    return "-1 fun 被装饰函数的结果返回值"
    
print(fun())
print(fun)
print("fun 被 funA 赋值了,相当于 fun = funaWrapper")

这其中最核心的且必须要理解的步骤是
fun = funA(fun)
fun = funB(fun)
fun = funC(fun)

@funC
def fun():  # 这里的 def fun()  相当于让解释器去调用 fun 函数对象给装饰器 funC(fun)
	pass
	

仅仅是传递了 fun 函数对象给 funC(fun) ,别被误导了,弄混了,那么又该如何理清装饰器间的嵌套逻辑,很简单,既然第一个装饰器是 funC ,那么就会得到 fun = funC(fun) ,之后就被第二个装饰器 funB 装饰,也就是

@funB
def fun():  # 这里是 funC 的 fun 传给装饰器 funB(fun)
	pass
	
@funA
def fun():  # 这里是 funB 的 fun 传给装饰器 funA(fun)
	pass
	

装饰器的运行逻辑:

装饰器的执行逻辑 1:

装饰器的执行逻辑 2:

装饰器函数利用上下文的程序来装饰被装饰的函数,其实就是将一些程序封装在一个嵌套的函数内,而这个嵌套的函数内的上下文程序可以包裹一个执行状态的被装饰函数() —— 加了括号的()函数名

代码演示如下:

错误 —— 报错

# test.py
def decorator(function):
	def wrapper():
		function()
		
@decorator
def test():
    print("test")
    
print(test())
#print(test)

错误 —— 报错

# test.py
def decorator(function):
	function()
	
@decorator
def test():
    print("test")
    
print(test)
#print(test())

下面返回的是函数对象,需要加括号 ()调用

正确 —— 成功运行

# test.py
def decorator(function):
	def wrapper():
		function()
	return wrapper
	
@decorator
def test():
    print("test")
    
print(test())

正确 —— 成功运行

# test.py
def decorator(function):
	function()
	return function
	
@decorator
def test():
    print("test")
    
print(test())

正确 —— 成功运行

# test.py
def decorator(function):
    function()
    def wrapper():
        function()
    return wrapper
    
@decorator
def test():
    print("test")
    
print(test())

下面返回的是整型、浮点、字符型变量,无需加括号 () 调用

正确 —— 成功运行

# test.py
def decorator(function):
	function()
	return 0
	
@decorator
def test():
    print("test")
    
print(test)

正确 —— 成功运行

# test.py
def decorator(function):
	function()
	return "字符串"
	
@decorator
def test():
    print("test")
    
print(test)

正确 —— 成功运行

# test.py
def decorator(function):
	function()
	return 1.00
	
@decorator
def test():
    print("test")
    
print(test)

装饰器的执行逻辑 3:

当在使用已经被装饰器装饰过的函数fun 时,例如 fun(100,200) ,以为是在使用调用 fun() 函数,那么就要注意这里的 fun() ,还是不是原来的 fun() ,有可能不是原来的 fun() 函数了,有可能是装饰器内的嵌套函数对象,比如 fun = wrapper

上面提到的和带参数的函数有着非常关键的联系,总结如下:

参考链接:http://c.biancheng.net/view/2270.html

举报

相关推荐

0 条评论