python中的装饰器及闭包

160 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

@TOC

一、装饰器基本概念

在学习装饰器前需要学习闭包的概念,可参考文章: python中闭包和nonlocal声明. 装饰器就是闭包的一种延申,比闭包更加简洁方便。

#使用闭包调用
def write(func):  #闭包
	def inner():
    	print("write a number")
    	func()
	return inner
	
def f1():
	print("I write a 1")
def f2():
	print("I write a 2")
	
f = write(f1)
f()
>>>
write a number
I write a 1

如果使用装饰器则会简单很多

def write(func):  #闭包
	def inner():
    	print("write a number")
    	func()
	return inner

@write  	#执行的是闭包调用里的:f = write(f1)
def f1():
	print("I write a 1")
def f2():
	print("I write a 2")

f1()  #可以直接使用f1()来达到闭包的效果
>>>
write a number
I write a 1

正如例子中的注解所说, @write 就是代替了闭包调用的一步,使代码更加简洁。

二、重叠装饰器

重叠装饰器就是在被装饰的函数前有两个装饰器,先运行的是离函数最近的那个装饰器,但是闭包中的内部函数运行的顺序正好相反。

def write_1(func):  #闭包
	print("runing write_1")
	def inner():
    	print("write a number,write_1")
    	func()
	return inner

def write_2(func):  #闭包
	print("runing write_2")
	def inner():
    	print("write a number,write_2")
    	func()
	return inner
@write_2
@write_1
def f1():
	print("I write a 1")
>>>
runing write_1
runing write_2
write a number,write_2  #注意顺序
write a number,write_1
I write a 1
I write a 2

三、参数化装饰器

参数化装饰器的意思是:在函数有参数时,闭包中的函数也应该有参数。并且必要时使用:(*参数名)->任意数量的参数。

def write_1(func):  #闭包
	print("runing write_1")
	def inner(*args):
    	print("write a number,write_1")
    	return func(*args)
	return inner
	
@write_1
def f1(a,b):
	return a+b
	
r = f1(10,11)
print(r)
>>>
runing write_1
write a number,write_1
21

四、标准库装饰器

python中内置了一部分装饰器,有property、classmethod等,可到python库查阅。

python中闭包和nonlocal声明

一、闭包

通俗来讲,闭包可以理解成就是一种函数。如何创建闭包?

1、创建一个外部函数
2、创建一个内部函数 (函数嵌套)
3、内部函数使用外部函数的参数,外部函数返回内部函数名

例:

使用闭包实现两数之和:
def founcOut(num1):
	def founcIn(num2):
    	return num2 + num1
	return founcIn
	
f = founcOut(100)
r = f(100)
print(f,'\n',r)
>>>
<function founcOut.<locals>.founcIn at 0x00000173C422A160> 
200

由例子可以看出基本的闭包结构,值得注意的是founcOut返回的是founcIn()内部函数,并将返回赋值给f(在python中函数也是对象)。此时f就是函数founcIn(),所以再调用f并赋值num2=100,从而算出两数之和。

闭包还有其他一些应用,并且是装饰器的基础

==注:闭包的运行机制和详细原理可参考《Python学习手册》《流畅的PYTHON》==

二、nonlocal声明

nonlocal和global的作用很相似。继续上面例子,如果我想在内部函数中修改参数num1的值,系统会报错(具体参考上述两本书)。所以在内部函数中使用nonlocal声明即可修改外部函数的参数。

def founcOut(num1):
	def founcIn(num2):
    	nonlocal num1
    	num1 += 100
    	return num2 + num1
	return founcIn
	
f = founcOut(100)
r = f(100)
print(f,'\n',r)
>>>
<function founcOut.<locals>.founcIn at 0x00000173C422A160>
300