闭包有哪些优缺点 (装饰器与闭包的区别)

闭包

在Python中什么是闭包呢?要想了解闭包首先来了解一下函数名的作用:

函数名存放的是函数所在空间的地址

②函数名()执行函数名所存放空间地址中的代码

③func01 = func02函数名可以像普通变量一样赋值,func01() 等价于 func02()

结论:函数可以像普通变量一样作为参数使用

函数被调用完之后,函数内定义的变量会被都销毁,但是有时候又想用到这些变量该怎么办呢?这时候就可以用闭包来解决这个问题。首先闭包的定义:

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包

从定义中可以看出闭包的构成条件有一下三点:

在函数嵌套(函数里面再定义函数)的前提下

②内部函数使用了外部函数的变量(还包括外部函数的参数)

③外部函数返回了内部函数

由此可以看出闭包的作用:

闭包不仅可以保存外部函数的变量还可以提高代码的复用性。

我们可以通过上面的定义写出一个标准的闭包格式:

 # 外部函数
 def test1(a):
     b = 10
     # 内部函数
     def test2():
         # 内部函数使用了外部函数的变量或者参数
         print(a, b)
     # 返回内部函数, 这里返回的内部函数就是闭包实例
     return test2

注意在闭包内修改外部变量就需要使用到nonlocal关键字

我们来看下面两段代码的对比就可以知道了:

代码1:

# 定义一个外部函数
def func_out(num1):

    # 定义一个内部函数
    def func_inner(num2):
        # 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1
        num1 = 10
        # 内部函数使用了外部函数的变量(num1)
        result = num1 + num2
        print("结果是:", result)

    print(num1)
    func_inner(1)
    print(num1)

    # 外部函数返回了内部函数,这里返回的内部函数就是闭包
    return func_inner

# 创建闭包实例
f = func_out(1)
# 执行闭包
f(2)

执行结果:

1
结果是: 11
1
结果是: 12

代码2:

# 定义一个外部函数
def func_out(num1):

    # 定义一个内部函数
    def func_inner(num2):
        # 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1
        nonlocal num1  # 告诉解释器,此处使用的是 外部变量a
        # 修改外部变量num1
        num1 = 10
        # 内部函数使用了外部函数的变量(num1)
        result = num1 + num2
        print("结果是:", result)

    print(num1)
    func_inner(1)
    print(num1)

    # 外部函数返回了内部函数,这里返回的内部函数就是闭包
    return func_inner

# 创建闭包实例
f = func_out(1)
# 执行闭包
f(2)

执行结果:

1
结果是: 11
10
结果是: 12

装饰器与闭包的区别,闭包和装饰器

装饰器

装饰器的作用:

在不改变原有函数的源代码的情况下,给函数增加新的功能

装饰器的功能特点:

①不修改已有函数的源代码

②给已有函数增加额外的功能

装饰器符合了 开发中的封闭开放原则

通用装饰器的语法格式:

# 装饰器的基本雏形
# def decorator(fn): # fn:目标函数.
#     def inner():
#         '''执行函数之前'''
#         fn() # 执行被装饰的函数
'''执行函数之后'''
    return inner

装饰器的语法糖用法: @装饰器名称,同样可以完成对已有函数的装饰操作。

不使用语法糖:

# 添加一个登录验证的功能
def check(fn):
    def inner():
        print("请先登录....")
        fn()
    return inner

def comment():
    print("发表评论")

# 使用装饰器来装饰函数
comment = check(comment)
comment()

使用语法糖:

# 添加一个登录验证的功能
def check(fn):
    print("装饰器函数执行了")
    def inner():
        print("请先登录....")
        fn()
    return inner

# 使用语法糖方式来装饰函数
@check
def comment():
    print("发表评论")

comment()

说明:

  • @check 等价于 comment = check(comment)
  • 装饰器的执行时间是加载模块时立即执行。
  • 多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程
  • 装饰器只能接收一个参数,并且还是函数类型。

装饰器只能接收一个参数,并且还是函数类型。那如果要写带有参数的装饰器如何写呢?

示例:

# 添加输出日志的功能
def logging(flag):

    def decorator(fn):
        def inner(num1, num2):
            if flag == "+":
                print("--正在努力加法计算--")
            elif flag == "-":
                print("--正在努力减法计算--")
            result = fn(num1, num2)
            return result
        return inner

    # 返回装饰器
    return decorator

# 使用装饰器装饰函数
@logging("+")
def add(a, b):
    result = a + b
    return result

@logging("-")
def sub(a, b):
    result = a - b
    return result

result = add(1, 2)
print(result)

result = sub(1, 2)
print(result)

装饰器还有一种特殊的用法就是类装饰器,就是通过定义一个类来装饰函数。

类装饰器示例代码:

class Check(object):
    def __init__(self, fn):
        # 初始化操作在此完成
        self.__fn = fn

    # 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用。
    def __call__(self, *args, **kwargs):
        # 添加装饰功能
        print("请先登陆...")
        self.__fn()

@Check
def comment():
    print("发表评论")

comment()

说明:

  • @Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
  • 要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用
  • call方法里进行对fn函数的装饰,可以添加额外的功能。

作者简介:Python菜鸟工程师,将在接下来的一段时间内与大家分享一些与Python相关的知识点。如若文中出现问题,各位大佬多多指点,互相学习。喜欢的关注一个吧!谢谢!

至此Python中的多任务和装饰器已经讲解完毕,需要XMIND的同学可以回复:多任务xmind 获取思维导图资料。

装饰器与闭包的区别,闭包和装饰器