闭包函数和装饰器

1.闭包函数:

1.1 什么是闭包?

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

1.2 闭包的构成
  1. 在函数嵌套(函数里面再定义函数)的前提下
  2. 内部函数使用了外部函数的变量(还包括外部函数的参数)
  3. 外部函数返回了内部函数

定义内层函数对外层函数非全局变量的引用,就叫做闭包函数

​ 1.闭包会一直存在内存当中,不会因为函数执行结束而被释放。

在这里插入图片描述

1.3 闭包的作用
1. 保护数据安全、保护数据干净性

闭包的目的:要接受被装饰的函数和被装饰函数需要的参数

1.4 闭包基本模型:
def wrapper():
    name = 'xiongsheng'
    def inner():
        print(name)
    inner()
wrapper()
======================
xiongsheng

判断是否为闭包函数:

可以通过print(函数名.__closure__ )看是否有<cell xxxx xxx >类似信息打印,如果有就说明该函数为闭包函数。
name = 'xiongsheng'
def wrapper():
    def inner():
        print(name)
    inner()
    print(inner.__closure__)  # None   说明该函数不是闭包函数
wrapper()

===========================
None             

name = 'xiongsheng'
def wrapper(x):
    x = name
    def inner():
        print(x)
    inner()
    print(inner.__closure__)  # <cell at 0x0000000000130C48: str object at 0x0000000001E8A260>  有类似的打印信息说明该函数为闭包函数
wrapper(name)

=================================================
xiongsheng
(<cell at 0x02B77490: str object at 0x00FF73A0>,)
1.5 应用场景:
获取网页内容
from urllib.request import urlopen
def zhua(url):
    #url='www.baidu.com'
    def get():
        return  urlopen(url).read()
    return get
f=zhua('www.baidu.com')
f()
1.6 闭包的使用
# 外部函数
def config_name(name):
    # 内部函数
    def inner(info):
        print(name + ": " + info)

    return inner


tom = config_name("Tom")  # 创建tom闭包实例对象
jerry = config_name("jerry")  # 创建jerry闭包实例对象

print(id(tom))
print(id(jerry))  # 内存地址不同, 相当于创建了2个不同的闭包,

tom("你好! jerry")
tom("你好, 在吗?")  # 如果执行tom闭包,因为已经保存了name参数,那么以后在输入的时候都是,tom说:xxx
jerry("不在, 不和你玩!")

在这里插入图片描述

1.7 修改闭包内使用的外部变量
# 1.函数嵌套
def func_out():  # 外部函数的定义
    num = 10  # 定义一个变量

    def inner():  # 内部函数的定义
        nonlocal num  # 在闭包内部修改外部函数的变量
        num = 40
        result = num + 10  # 2.内部函数必须使用外部函数的变量
        print(result)

    print("修改前的外部变量:", num)
    inner()
    print("修改后的外部变量:", num)
    # print("打印结果:", result)
    # 外部函数返回内部函数的地址
    # 3. 外部函数要返回内部函数,这个使用了外部函数变量的内部函数称为闭包
    return inner  # 注意:没有括号,加上括号,就是函数调用


# 获取闭包的对象
# 这个func_new 就是闭包
# func_new函数执行的结果等于inner函数执行的结果
func_new = func_out()
# 执行闭包
func_new()

在这里插入图片描述

总结:一个函数内部如果需要某个名字关系,有两种方法:
  1、通过传参的方式
  2、通过闭包的方式,在该函数外层包一层作用域(即再定义一个函数),将需要的名字关系包在外部函数中

2. 装饰器:

2.1 装饰器的定义

定义给已有函数增加额外功能的函数,它本质上就是一个闭包函数

装饰器的功能特点:

1.不修改已有函数的源代码
2.不修改已有函数的调用方式
3.给已有函数增加额外的功能

**功能:**在遵循原则的前提,为被装饰对象添加上新功能。

2.2 装饰器的基本模型
def wrapper(f):
    def inner(*args,**kwargs):
        """被装饰函数执行之前的操作"""

        ret = f(*args,**kwargs)
        """被装饰函数执行之后的操作"""
        return ret
    return inner
2.3 装饰器的示例代码
# 添加一个登录验证的功能
def check(fn):
    def inner():
        print("请先登录....")
        fn()
    return inner


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

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

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

在这里插入图片描述

代码说明:

1.闭包函数有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器。
2.写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。
2.4 装饰器的语法糖写法

语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰
在这里插入图片描述
说明:

@check 等价于 comment = check(comment)
装饰器的执行时间是加载模块时立即执行。

总结
装饰器本质上就是一个闭包函数,它可以对已有函数进行额外的功能扩展。
装饰器的语法格式:

# 装饰器
 def decorator(fn): # fn:被装饰的目标函数.
     def inner():
         '''执行函数之前'''
         fn() # 执行被装饰的目标函数
         '''执行函数之后'''
     return inner
2.5 通用装饰器的使用

1.装饰带有参数的函数

# 添加输出日志的功能
def logging(fn):
    def inner(num1, num2):
        print("--正在努力计算--")
        fn(num1, num2)

    return inner


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


sum_num(1, 2)

2.装饰带有返回值的函数

# 添加输出日志的功能
def logging(fn):
    def inner(num1, num2):
        print("--正在努力计算--")
        result = fn(num1, num2)
        return result
    return inner


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


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

3.装饰带有不定长参数的函数

# 添加输出日志的功能
def logging(fn):
    def inner(*args, **kwargs):
        print("--正在努力计算--")
        fn(*args, **kwargs)

    return inner


# 使用语法糖装饰函数
@logging
def sum_num(*args, **kwargs):
    result = 0
    for value in args:
        result += value

    for value in kwargs.values():
        result += value

    print(result)

sum_num(1, 2, a=10)

4.通用装饰器

# 添加输出日志的功能
def logging(fn):
    def inner(*args, **kwargs):
        print("--正在努力计算--")
        result = fn(*args, **kwargs)
        return result

    return inner


# 使用语法糖装饰函数
@logging
def sum_num(*args, **kwargs):
    result = 0
    for value in args:
        result += value

    for value in kwargs.values():
        result += value

    return result


@logging
def subtraction(a, b):
    result = a - b
    print(result)

result = sum_num(1, 2, a=10)
print(result)

subtraction(4, 2)

6.通用装饰器的语法格式:

# 通用装饰器
def logging(fn):
  def inner(*args, **kwargs):
      print("--正在努力计算--")
      result = fn(*args, **kwargs)
      return result

  return inner
2.6 多个装饰器的使用
def make_div(func):
    """对被装饰的函数的返回值 div标签"""
    def inner():
        return "<div>" + func() + "</div>"
    return inner


def make_p(func):
    """对被装饰的函数的返回值 p标签"""
    def inner():
        return "<p>" + func() + "</p>"
    return inner


# 装饰过程: 1 content = make_p(content) 2 content = make_div(content)
# content = make_div(make_p(content))
@make_div
@make_p
def content():
    return "人生苦短"

result = content()

print(result)

在这里插入图片描述
代码说明:

多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程
小结
多个装饰器可以对函数进行多个功能的装饰,装饰顺序是由内到外的进行装饰

2.7 带有参数的装饰器

在装饰器外面再包裹上一个函数,让最外面的函数接收参数,返回的是装饰器,因为@符号后面必须是装饰器实例。

# 添加输出日志的功能
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)

在这里插入图片描述

2.8 类装饰器的使用
class Check(object):
    def __init__(self, fn):
        # 初始化操作在此完成
        self.__fn = fn

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


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


if __name__ == '__main__':
    comment()

在这里插入图片描述
代码说明:

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

小结
想要让类的实例对象能够像函数一样进行调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable)
类装饰器装饰函数功能在call方法里面进行添加

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiongsheng666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值