python @

前置知识:

面向切面编程:

以一个例子来理解面向切面编程,假如程序写好了之后,现在发现要针对所有业务操作添加一个日志,或者在前面加一道权限,传统的做法是,改造每个业务方法,在里面假如所需要的功能,但是这样势必会把代码弄乱,而且以后再扩展还是更乱,面向切面编程的思想是将这部分功能插入进去,具体的实现例子就是python的@,也就是python的装饰器

闭包:

维基百科中的解释:在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

代码理解:

def print_msg():
    tmp_message="this is a temporary message"
    def printer():
        print(tmp_message)
    return printer

return_function=print_msg()
return_function()

输出:
this is a temporary message

在这个例子中,tmp_message是一个局部变量,在执行了print_msg()后就不应该继续存在了。但是嵌套函数引用了这个变量,将这个变量封装在了嵌套函数中,这样就形成了一个闭包,其中自由变量是tmp_message,函数是printer(),tem_message和printer()两者形成的闭包保存了执行时的上下文,可以脱离原本的作用域独立存在。
对应与python中,修饰器下面的函数就可以理解为一个闭包。

python装饰器的工作原理:

假设用funA函数装饰器去装饰funB函数,如下所示:

def funA(func):
    func()
    return

@funA
def funB():
    print("This is funcB")
    return 

输出:
This is funcB

上述代码的执行过程完全等价于:

def funA(func):
    func()
    return


def funB():
    print("This is funcB")
    return

funB=funA(funB)

通过以上两段程序可以看出,使用函数装饰器A去装饰另一个函数B,其底层执行了如下两步操作:

  1. 将B作为参数传递给A函数
  2. 将A函数执行完成后的返回值反馈给B(funB=funA(funB))

再通过一个例子说明:

def funA(func):
    print("first test")
    func()
    print("second test")
    return "the return of funA"

@funA
def funB():
    print("This is funB")

print(funB)

输出:
first test
This is funB
second test
the return of funA

由以上可以看出,被装饰器(@函数)修饰的函数不再是原来的函数,而是被替换成一个新的东西(取决于装饰器的返回值),即如果装饰器函数的返回值为普通变量,那么被修饰的函数名就变成了变量名,同样,如果装饰器返回的是一个函数的名称,那么被修饰的函数名依然表示一个函数。

实际上,函数装饰器的核心就是通过装饰器函数,在不修改原函数的前提下,来对函数的功能进行合理的扩充。

带参数的函数装饰器:

当B函数没有参数的时候,可以直接将B作为A()的参数传入,当被修饰的函数本身带有参数的时候,可以有以下处理方式:

比较简单的解决方法是在函数装饰器中嵌套一个函数,该函数带有的参数个数和被装饰器修饰的函数相同:

def funA(func):
    def alternative(argument):
        print("This is alternative",argument)
    return alternative

@funA
def funB(argument):
    print("This is funB",argument)


funB("test")

输出:
This is alternative test

以上的程序和下面这个程序是等价的:

def funA(func):
    def alternative(argument):
        print("This is alternative",argument)
    return alternative


def funB(argument):
    print("This is funB",argument)

funB=funA(funB)
funB("test")

通过funB()函数被funA()修饰,funB就被赋值为alternative,这意味这虽然我们在程序中显示调用的是funB函数,但是实际执行的是装饰器内嵌套的alternative函数

如果当前程序中,有多个(大于等于两个)函数被同一个装饰器函数修饰,这些函数带有的参数并不相等,此时的解决方法是利用*args和**kwargs作为装饰器内部嵌套函数的参数。

def funA(func):
    def A(*args,**kwargs):
        func(*args,**kwargs)
    return A

@funA
def B(a1):
    print("this is B, param:",a1)

@funA
def C(a1,a2):
    print("this is C,param1:",a1,",param2:",a2)

输出:
this is B, param: test
this is C,param1: test1 ,param2: test2

参考:
https://www.zhihu.com/question/24863332
https://www.jianshu.com/p/ee82b941772a
http://c.biancheng.net/view/2270.html
https://www.jb51.net/article/158533.htm

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值