|
最近在回看Python的时候,简单的来记一下装饰器的实现
以下操作环境为Python3.6
Python的装饰器英文名叫Decorator,其中组成装饰器的最重要的两个技术:闭包(Closure)以及柯里化(Currying)
为何使用装饰器?
在函数式编程过程中,函数需要增加某项功能时,为了避免硬编码(直接修改原函数),保留原函数的核心功能,将其他非该函数的功能剥离出去,因此衍生出了装饰器
闭包(Closure)
闭包:嵌套函数中,内层函数引用了外层函数的自由变量,就形成了闭包
以上是闭包的定义,在实现闭包前需要知道Python是允许有嵌套函数的,嵌套函数就是Python允许在内部定义函数并将函数返回出去
def outer():
def inner():
return 'I am inner'
return inner
fn = outer() # outer函数返回的是inner,相当于 fn = inner,但是fn仅仅只是记住了inner的函数名(相当于指向了inner函数)
print(fn) # 直接使用fn并不会调用内部的inner
print(fn()) # 这行代码会将内部的inner函数进行调用,打印'I am inner'
闭包:嵌套函数中,内层函数引用了外层函数的自由变量,就形成了闭包
上面代码解释了Python的嵌套函数,接下来在说何为内层函数引用了外层函数的自由变量,在此之前,需要知道Python的作用域,即LEGB原则
LEGB:
Local:本地变量,调用时创建,结束时消亡
Enclosing:嵌套函数外部函数的命名空间(可有可无)
Global:全局变量
Build-in:内建变量,在解释器启动时加载的变量
x = 'global' # x为全局变量
def outer():
x = 'enclosing' # 内部有inner函数,因此这个就是enclosing,如果内部没有嵌套函数,那么这个就是local变量
def inner():
x = 'local' # x为本地变量
return x
return inner
闭包:嵌套函数中,内层函数引用了外层函数的自由变量,就形成了闭包
那么内层函数引用了外层函数的自由变量,意思就是内部的local变量调用了外面的变量
def outer():
x = 1
def inner():
return x
return inner
print(x) # 报错:outer函数未调用,x = 1 未生成
fn = outer()
print(x) # 报错:outer函数调用结束,x = 1 已消亡
print(fn()) # 闭包的特性返回了x变量
print(fn())
print(fn())
print(fn())
以上代码在调用fn()的时候返回了1,这就是闭包:嵌套函数中,内层函数引用了外层函数的自由变量,就形成了闭包
柯里化(Currying)
柯里化:是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术
定义比较难懂,其实就是 f(x, y) --> f(x)(y)
def foo(x, y):
return x + y
foo(2, 3)
def foo(x): # 柯里化
def _foo(y):
return x + y
return _foo
foo(2)(3)
改写顺序:foo的形参y去掉,return x + y 用一个函数嵌套(形参为y),然后返回该函数
装饰器(Decorator)实现
有了闭包以及柯里化技术之后就可以来实现Python的装饰器了,下面是演变过程
# 在不改变add函数的基础上,增加打印功能
def add(x, y):
return x + y
def logger(x, y):
print('function is {}, x is {}, y is {}'.format(fn.__name__, x, y))
ret = add(x, y) # 直接调用全局的add函数
return ret
print(logger(4, 5))
以上代码是在add函数的基础之上添加打印功能
# 将add函数传递到logger函数内部,完成闭包
def add(x, y):
return x + y
def logger(fn, x, y):
print('function is {}, x is {}, y is {}'.format(fn.__name__, x, y))
ret = fn(x, y)
return ret
print(logger(add, 4, 5)) # 调用了外层的自由变量add函数
add函数被传入至logger函数内部,用到了闭包的技术,logger函数记住了add函数
# 为了能在所有函数中都可以使用到装饰器,那么形参上就必须要做改变
def add(x, y):
return x + y
def avg(x, y, z):
return (x + y + z)/3
def foo(x, y, *args, a, b, **kwargs):
pass
def logger(fn, *args, **kwargs):
print('function is {}, args is {}, kwargs is {}'.format(fn.__name__, args, kwargs)) # 参数结构
ret = fn(*args, **kwargs)
return ret
print(logger(add, 4, 5))
print(logger(avg, 4, 5, 6))
print(logger(foo, 4, 5, a=1, b=2, c=3))
logger函数使用了*args, **kwargs,来收集不同函数的形参
# 将函数柯里化,把内部形参剥离出来,完成装饰器
def add(x, y):
return x + y
def avg(x, y, z):
return (x + y + z)/3
def foo(x, y, *args, a, b, **kwargs):
pass
def logger(fn):
def inner(*args, **kwargs):
print('function is {}, args is {}, kwargs is {}'.format(fn.__name__, args, kwargs))
ret = fn(*args, **kwargs)
return ret
return inner
add = logger(add)
print(add(4, 5))
avg = logger(avg)
print(avg(4, 5, 6))
foo = logger(foo)
print(foo(4, 5, a=1, b=2, c=3))
此操作需要在每个函数定义后,进行重新定义,Python提供了语法糖,利用@关键字来减少代码的冗余
# 利用Python提供的语法糖,减少代码的冗余
def logger(fn):
def inner(*args, **kwargs):
print('function is {}, args is {}, kwargs is {}'.format(fn.__name__, args, kwargs))
ret = fn(*args, **kwargs)
return ret
return inner
@logger # 相当于 add = logger(add)
def add(x, y):
return x + y
@logger # 相当于 avg = logger(avg)
def avg(x, y, z):
return (x + y + z)/3
@logger # 相当于 foo = logger(foo)
def foo(x, y, *args, a, b, **kwargs):
pass
print(add(4, 5))
print(avg(4, 5, 6))
print(foo(4, 5, a=1, b=2, c=3))
需要注意的是logger函数要提前定义
|
|