python闭包和装饰器

DAY 9. 闭包和装饰器

9.1 闭包

闭包就是内部函数对外部函数作用域内变量的引用

可以看出

  • 闭包是针对函数的,还有两个函数,内部函数和外部函数
  • 闭包是为了让内部函数引用外部函数作用域内的变量的

我们先写两个函数

def fun1():
    print("我是fun1")

    def fun2():
        print("我是fun2")

这样fun2就作为fun1的内部函数,此时在函数外部是无法调用Fun2的,因为

  1. fun2实际上相当于fun1的一个属性(方法),作用域是fun1的块作用域,全局作用域中无法找到,
  2. 函数内属性的生命周期是在函数运行期间,在fun1中只是定义了fun2,并没有调用它

为了让fun2跳出fun1的生命周期,我们需要返回fun2,这样在外部获取到的fun1的返回值就是fun2,这样调用fun1的返回值就是调用了fun2,如:

def fun1():
    print("我是fun1")
    def fun2():
        print("我是fun2")
    return fun2

var = fun1()
var()
# 我是fun1
# 我是fun2

当然,这还不是一个闭包,闭包是引用了自由变量的函数,所谓自由变量可以理解为局部变量,如果fun2调用了fun1中的变量,那么fun2就是一个闭包了。如

def fun1(var1):
    def fun2():
        print(f"var1 = {var1}")
    return fun2

var = fun1(1)
var()  # var1 = 1
闭包的作用

闭包私有化了变量,实现了数据的封装,类似于面向对象

def fun1(obj):
    def fun2():
        obj[0] += 1
        print(obj)
    return fun2


if __name__ == '__main__':
    mylist = [i for i in range(5)]
    var = fun1(mylist)
    var()
    var()
    var()
    # [1, 1, 2, 3, 4]
    # [2, 1, 2, 3, 4]
    # [3, 1, 2, 3, 4]

9.2 装饰器

闭包在python中有一个重要的用途就是装饰器,装饰器接受被装饰的函数作为参数并执行一次调用,装饰器的本质还是一个闭包

def func1(func):
    def func2():
        print("func2")
        return func()
    return func2


@func1
def Demo():
    print("Demo")


if __name__ == '__main__':
    Demo()
    # func2
    # Demo
  • 首先,@func1是一颗语法糖,等价于func1(Demo)()
  • 外部函数必须能接收一个参数,也只能接受一个参数,如果有多个参数,必须再套一个函数,因为在使用@语法糖时,会自动把被修饰函数作为参数传递给装饰器
  • 内部函数必须返回被装饰函数的调用

运行流程:

  1. 把被修饰函数作为参数传递给装饰器,这时函数返回的是闭包函数func2
  2. 隐式地调用func2,相当于func2(),执行函数体,输出func2,这时函数返回值是func(),返回的直接是被修饰函数的调用,相当于直接执行被修饰函数,输出Demo

相当于:

def func1(func):
    def func2():
        print("func2")
        return func()
    return func2


# @func1
def Demo():
    print("Demo")


if __name__ == '__main__':
    # s = Demo()
    # 先把被修饰函数作为参数传递给修饰器,这里的s就是func2
    s = func1(Demo)
    # 调用闭包函数
    s()
    print(s)

    # func2
    # Demo
    # <function func1.<locals>.func2 at 0x00000117F163AD90>

9.2.1 装饰器带参数
def func1(num):
    def func2(func):
        def func3():
            if num >10:
                print("大于10")
            else:
                print("小于10")
            return func()
        return func3
    return func2


@func1(num=12)
def Demo():
    print("Demo")


if __name__ == '__main__':
    Demo()b

执行流程

  1. 将装饰器的参数传递给第一层函数,并返回第二层函数func2
  2. 将被修饰函数作为参数传递给第二层函数func2,隐式调用func2,返回闭包函数
  3. 执行闭包函数,并返回被修饰函数的调用(执行被修饰函数)
9.2.2 被修饰函数带参数

如果被修饰函数带有参数,需要把参数传递给内层闭包函数,返回被修饰函数的调用时记得加参数

def func1(func):
    def func2(arg):
        arg += 1
        # 记得加参数
        return func(arg)
    return func2

@func1
def Demo(arg):
    print(arg)

if __name__ == '__main__':
    Demo(11)  # 12
9.2.3 例
  1. 求斐波那契数列任意一项的值
import time

def code_time(func):
    '''
    修饰器,用来打印函数运行时间
    :param func: 被修饰函数
    :return: func
    '''
    start_time = time.time()
    def closer(*args,**kwargs):
        result = func(*args,**kwargs)
        codeTime = time.time() - start_time
        print(f"This code runs at:{codeTime}")
        return result
    return closer

def _Fibonacci(n):
    if n <= 1:
        return 1
    else:
        return _Fibonacci(n-1) + _Fibonacci(n-2)

@code_time
def Fibonacci(n):
    return _Fibonacci(n)


if __name__ == '__main__':
    var = Fibonacci(40)
    print(var)
    # This code runs at:61.738335609436035
    # 165580141

发现代码效率非常低,输出第四十个值需要一分多钟,这是应为每计算一个值,需要计算前两个值,这里有很多重复的,如

            10
            |
    |-----------------|
    9                 8
|--------|       |--------|
8        7       7        6

7,8被重复计算多次

所以需要把已经计算过的储存起来,计算之前先判断有没有计算过,没计算过再计算,修改程序为:

import time

def code_time(func):
    '''
    修饰器,用来打印函数运行时间
    :param func:
    :return:
    '''
    start_time = time.time()
    def closer(*args,**kwargs):
        result = func(*args,**kwargs)
        codeTime = time.time() - start_time
        print(f"This code runs at:{codeTime}")
        return result
    return closer
resultList = {0:1,1:1}
def _Fibonacci(n):
    if n <= 1:
        return 1
    else:
        if n-1 in resultList:
            a = resultList[n-1]
        else:
            a = _Fibonacci(n-1)
            resultList[n-1] = a
        if n-2 in resultList:
            b = resultList[n-2]
        else:
            b = _Fibonacci(n-2)
            resultList[n-2] = b
        return a + b

@code_time
def Fibonacci(n):
    return _Fibonacci(n)


if __name__ == '__main__':
    var = Fibonacci(40)
    print(var)

    # This code runs at:0.0
    # 165580141

速度快了很多,但重复的代码是不能忍受的,使用修饰器重新一下:

import time


def code_time(func):
    start_time = time.time()

    def closer(*args, **kwargs):
        result = func(*args, **kwargs)
        codeTime = time.time() - start_time
        print(f"This code runs at:{codeTime}")
        return result

    return closer


def modify(func):
    catch = {0: 1, 1: 1}

    def closer(*args):
        if args not in catch:
            catch[args] = func(*args)
        return catch[args]
    return closer


@modify
def _Fibonacci(n):
    if n <= 1:
        return 1
    else:
        return _Fibonacci(n - 1) + _Fibonacci(n - 2)


@code_time
def Fibonacci(n):
    return _Fibonacci(n)


if __name__ == '__main__':
    var = Fibonacci(40)
    print(var)

有20节楼梯,一次可以走1,2,3,4级,总共有多少种走法

from my_python_package import code_time


def Modify(c = None):
    if c == None:
        c = {}
    def modify(func):
        catch = c
        def closer(*args):
            if args[0] not in catch:
                catch[args[0]] = func(*args)
            return catch[args[0]]
        return closer
    return modify

@Modify()
def _Stairs(num, steps):
    count = 0
    if num == 0:
        count = 1
    elif num > 0:
        for step in steps:
            count += _Stairs(num-step,steps)
    return count

@code_time
def Stairs(num,steps):
   count = _Stairs(num,steps)
   return count

if __name__ == '__main__':
    num = 20
    steps = [step for step in range(1,5)]
    count = Stairs(num, steps)
    print(count)

    # Stairs runs at: 0.0 s
    # 283953

9.3 总结

  • 闭包:内部函数调用了外部函数作用域内的变量

    • 针对函数
    • 要有自由变量(私有变量)
    • 要点:内部函数要跳出外部函数的生命周期,需要外部函数把他return出来
  • 装饰器:

    • 基础:闭包
    • 作用:不修改原来代码的基础上拓展原函数功能
    • 用处:修改API功能,AOP编程
    • 要点:@语法糖,函数执行顺序
  • 参考链接

Python高级编程技巧(进阶)(已完结)

Python的闭包与装饰器

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
闭包是一个函数对象,它引用了一些在其定义时外部环境的变量。在Python闭包常常用来创建装饰器装饰器其实就是一个闭包,它接受一个函数作为参数,并返回一个替代版函数闭包允许我们在不修改原始函数的情况下,添加一些额外的功能或逻辑。 一个典型的装饰器的例子可以是这样的:在一个函数外面定义一个装饰器函数,然后通过在要装饰的函数之前添加@装饰器名称的语法糖,来应用装饰器闭包装饰器的实现机制是类似的,都是通过嵌套函数的方式来实现的。在闭包内部函数引用外部函数变量。而在装饰器装饰器函数接受一个函数作为参数,并返回一个内部函数内部函数可以访问外部函数变量。 在闭包装饰器的实现过程,都需要注意作用域的规则。在闭包内部函数可以访问外部函数的局部变量,而在装饰器装饰器函数可以访问被装饰函数变量闭包装饰器提供了一种灵活的方式来扩展函数的功能,使得我们可以在不修改原始函数的情况下,添加一些额外的逻辑。它们是Python非常强大而且常用的特性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [python闭包装饰器解释](https://blog.csdn.net/qq_39567748/article/details/99596644)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [python高级之装饰器](https://blog.csdn.net/qq_35396496/article/details/109147229)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Junebao

如果你碰巧财力雄厚的话...

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

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

打赏作者

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

抵扣说明:

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

余额充值