python基础 - 闭包、装饰器

大家好,我是W

相信大家在学python基础、江狗(Django)的时候都遇到过闭包和装饰器的知识,但是可能功力不够好像用的不多。但是作为一个基础概念,我们还是要把他学扎实点。所以今天就来将闭包和装饰器,顺便理一下数据传输的流程。接下来的流程就是闭包、闭包的数据流程、装饰器、装饰器的数据流程、二者关系、实际使用场景及优点

闭包

闭包的定义

闭包就是能够读取其他函数内部变量的函数。只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数”

说起来可能有点抽象,用一个实例演示一下:

# 这样的结构叫做闭包
# 外层函数outter 定义了局部变量b=1
def outter():
    b = 1
	# 而内层函数inner能够使用outter的局部变量
    def inner():
        print(b)
	# 闭包的结构要求外层函数outter必须将inner的实例返回
    return inner
	# 而返回的inner作为内存地址赋值给接受outter的返回值的变量

闭包的作用

闭包的作用有两个,一是能够读取外部函数的局部变量。二是能够将这些局部变量保存在内部实例的内存中。

什么是闭包?闭包的作用,用法及优缺点

请看下面这个实例:

def outter():
# a = 1作为outter的局部变量 , 虽然inner函数可以对其打印 ,但是却没有修改的权限
a = 1

def inner():
	# inner函数将a声明为nonlocal,使得inner函数可以直接使用外层变量
    nonlocal a
    a += 2
    print(a)

return inner # 将inner直接返回

if __name__ == '__main__':
    inner = outter() # 接收到inner实例
    # 接下来连续调用inner 3次 查看其结果
	inner() # 3
    inner() # 5
    inner() # 7

如此看来inner函数可以将outter的a作为自己的局部变量,并且可以将a的状态保存下来。

闭包的数据流程

闭包的数据流程经过上面的几个例子已经很清晰了,现在在看一个简单地例子整理一下:

# 在outter中定义一个默认参数b=2
def outter(b=2):
a = 1

def inner():
	# inner可以直接读取外层函数的局部变量
    print(a + b) # 本例中打出4
    print(a) # 1

return inner


if __name__ == '__main__':
    inner = outter(3) # 实参3赋值给b ,并将outter的内层函数inner的引用赋值给main中的inner变量
    inner() # 此时的inner就是函数inner的引用,可以直接加括号调用函数

这就好像一个同心圆,外层outter为大圆,内层inner为小圆,使用outter后得到返回值赋值给inner变量,inner变量被调用时会从自己的内存中向大圆的内存获取信息从而实现自己的功能。

若我们不获取返回值直接调用inner函数则会报未定义错误:

def outter(b=2):
a = 1

def inner():
    print(a + b)
    print(a)

return inner


if __name__ == '__main__':  # NameError: name 'inner' is not defined
    inner()

装饰器

装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)。 — Python 函数装饰器

装饰器的实现需要依靠闭包的结构,所以闭包和装饰器的关系就是装饰器也是闭包。

装饰器的结构与闭包类似,只是外层函数接收的变量变成了函数引用:

# 闭包
def outter(fun):
a = 1

def inner():
    nonlocal a
    fun()

return inner

# 普通函数
def fun():
    print('fun print')

if __name__ == '__main__':
    inner = outter(fun)
    inner()

那么到底这个结构有什么作用呢?试想一下有这样的一个场景:你做的网站需要做一个黑名单验证,但是若整体修改登录函数的话好像比较麻烦,因为要改访问数据库的规则,要改验证逻辑。于是装饰器就可以解决这个问题,请看下面的实例:

def outter(fun):
	a = 1

    def inner():
        nonlocal a
        print('第%d处理' % (a))
        a += 1
        fun()

	return inner


def fun():
print('fun print')

if __name__ == '__main__':
inner = outter(fun)
inner()
inner()

大家可以试着运行一下这个代码,我们的fun函数就好像登录函数,但是由于新增的功能可能涉及的代码比较庞大不好修改,那么可以在下面直接包上这层装饰器,经过装饰器的处理我们只需要在主调函数上获得inner函数的实例就可以正常使用fun函数,并且每次使用fun函数之前都会经过inner函数的处理。

可能有的同学会问,那我还是要修改主调函数的代码啊,能不能不修改?可以的,只需要按照下面的格式在fun函数上加一个魔法糖就可以了。

def outter(fun):
    a = 1

    def inner():
        nonlocal a
        print('第%d处理' % (a))
        a += 1
        fun()

    return inner

# @加上闭包的名字就可以使用装饰器了
@outter
def fun():
    print('fun print')

if __name__ == '__main__':
    fun() # 在主调函数中并不需要做任何的修改,就好像无事发生一样
    fun()
    fun()

经过魔法糖加持就可以实现高效的重构代码了。

总结

ok写到这里其实第一段里讲的内容都讲到了,闭包的结构、数据流程、装饰器的结构、装饰器的实际应用场景、闭包与装饰器的关系,本来想写一个装饰器的应用场景的,但是着急去跑步,下次一定下次一定。本次的质量一般,大家见谅。