闭包函数、装饰器

闭包
  1、在外层函数中嵌套定义了一个内层函数
  2、外层函数的返回值,是内层嵌套函数的函数名,不带()
  3、内层函数对外层有非全局变量的引用

def func(a):
    print('start', a)
    b = 100

    def wrapper():  # 内层函数,在外层函数return时使用,内部手动调用意义不大
        print('wrapper', a, b)  # 内层函数有对外层函数非全局变量的引用,包括外层函数内部定义的变量或传参

    print('end')
    return wrapper  # 切记此处返回的是wrapper不带括号,带括号就是自动调用内部函数了


func(99)  # 直接调用打印的是start 99和end,没有触发调用内层函数
print(func(99))  # <function func.<locals>.wrapper at 0x0000022C9A9DA430>,func(99)就是wrapper函数
res = func(99)
print(res())  # res()即为wrapper(),wrapper没有返回值,所以打印None;也可以写为func(99)()

装饰器

# 引入装饰器
def work():
    print('work')


def fun(aa):
    def func1():
        print(aa)
        aa()

    return func1


fun(work)  # 把work函数传给fun函数并调用func()函数,传的是work不是work()
print(fun(work))
# <function fun.<locals>.func1 at 0x0000024FE921A700>,内层函数的地址
res1 = fun(work)
res1()  # 以下两行为res1()时打印的结果
# <function work at 0x0000024FE921A5E0>
# work

# 以下代码块效果等同于上方引入装饰器代码块
def fun_1(aa):
    def func1_1():
        print(aa)
        aa()

    return func1_1

# @fun_1释义:调用fun_1函数并把下方的work_1函数当做参数传进去,即为fun_1(work_1)
# 然后把fun_1(work_1)的返回值赋值给了work_1,使用work_1()就是调用函数
@fun_1
def work_1():
    print('work')


print(work_1)
# <function fun_1.<locals>.func1_1 at 0x000002526F84A700>,内层函数的地址
print(work_1())
# <function work_1 at 0x000001CDB4D0A670>  work_1函数的内存地址
# work
# None  work_1函数没返回值,所以打印None

装饰器的作用
  在不修改原功能函数内部代码的基础上,可以给原功能代码扩展新的功能
装饰器的开放封闭原则(面向对象原则的核心)
  软件实体应该是可扩展的,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。
装饰器的实现:
  1、通过闭包实现装饰器
  2、通过类实现装饰器(通过__call__方法实现)

# -----闭包实现装饰器
def rerun(func):  # func为接收被装饰的函数
    def wrapper(*args, **kwargs):
        for i in range(4):  # 函数重运行,最多重运行3次
            try:
                res = func(*args, **kwargs)
            except AssertionError as e:
                print(i)
                if i == 3:
                    raise e
            else:
                print('通过')
                return res

    return wrapper


@rerun  # aa = rerun(aa) 装饰器原理:调用rerun并把aa函数当做参数传入,把结果赋值给aa
def aa():
    a = input('请输入:')
    assert a == '1'


print(aa)  # <function rerun.<locals>.wrapper at 0x00000157987FA550> 闭包的内层函数
# aa()  # 调用


# -----通过装饰器实现函数运行时间的计算,并给出一个指定值,运行时间超过这个值的函数就打印出来
import time


# 三层函数,装饰器传参、最内层函数传参
def num(number: int = 2):  # 装饰器需要传参,默认值为2
    def time_(func):
        def wrapper(*args, **kwargs):  # 使用不定长参数接收被装饰的函数的传参
            s_time = time.time()
            res = func(*args, **kwargs)
            e_time = time.time()
            if e_time - s_time > number:
                print(func, e_time - s_time)
            return res

        return wrapper

    return time_


@num(2)  # 装饰器需要传参时,无论是否有默认值,都要带上后面的();2传给第一层函数
def tes_aa(a, b):  # tes_aa = num(2)(test1_aa)(11, 22)
    for i in range(3):
        time.sleep(1)
    return a + b


print(tes_aa(11, 22))  # 33

# -----编写装饰器,为多个函数加上登录认证的功能,校验登录状态
def login():
    login_status['token'] = True
    print('登录账号密码函数的内部逻辑')

login_status = {'token': False}  # 使用全局变量字典格式保存登录状态

def login_stake(func):
    def wrapper(*args, **kwargs):
        if not login_status['token']:
            login()
        return func(*args, **kwargs)

    return wrapper



# -----通过装饰器去装饰类或者有返回值的函数,装饰器内层函数一定要将类或者函数的调用结果返回

def zhuangshiqi(func):
    def wrapper(*args, **kwargs):
        print('装饰之前')
        res = func(*args, **kwargs)
        print('装饰之后')
        return res

    return wrapper


@zhuangshiqi  # MyTe = zhuangshiqi(MyTe)
class MyTe:
    def yy(self):
        print('被装饰的类的内部函数')


myt = MyTe()
# 如果装饰器内层函数没有将类调用结果返回,打印的是None,这样无法创建对象,有返回值时myt是类创建的一个对象
# 此时myt这个对象不能加()调用,如需myt()调用,需使用__call__函数
print(myt)  # <__main__.MyTe object at 0x00000242C231CD30>
myt.yy()  # 被装饰的类的内部函数
print(myt.yy())  # 被装饰的类的内部函数  None


# -----非闭包实现的装饰器去装饰类
def yyy_01(self):  # 此函数会被装饰为某个类的内部函数,所以需要一个self参数
    print('yyy')


def myddt(cls):
    setattr(cls, 'yyy1', yyy_01)
    setattr(cls, 'yyy2', 'daxigua')
    return cls

@myddt
class MyDdt:
    def work(self):
        print('work')

mydt = MyDdt()
print(mydt)  # <__main__.MyDdt object at 0x000001BC7A81CBE0>
print(mydt.yyy2)  # daxigua
mydt.yyy1()  # yyy
# -----内置函数callable()判断一个对象是否可调用,可调用就返回True,否则就返回False
# 此处的调用是指 该对象是否可以加()去调用
def work1():
    pass

work2 = [11,22]

print(callable(work1))  # True
print(callable(work2))  # False
# work2()  # TypeError: 'list' object is not callable  直接这样调用会报错

# 只要类里面实现了__call__方法,那么该类创建的对象就能像函数一样在后面加()去调用
class MyTest:

    def __call__(self, *args, **kwargs):
        pass

mytest = MyTest()
print(mytest())  # 此时__call__没有写返回值所以为None,也可以往__call__里传参


# 通过类去实现装饰器
class MyDecorator:

    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print('调用之前扩展功能')
        res = self.func()  # 调用传进来的原功能函数
        print('调用之后扩展功能')
        return res


@MyDecorator  # work3 = MyDecorator(work3)
def work3():
    print('原函数')

print(work3)  # <__main__.MyDecorator object at 0x0000026964EDD970> MyDecorator类创建的对象
work3()  # 调用对象,实际是去调用的__call__方法

同时使用多个装饰器时,执行装饰器顺序为从上到下执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值