美文网首页工作生活
python闭包函数与装饰器函数

python闭包函数与装饰器函数

作者: Xyxtank | 来源:发表于2019-07-02 16:34 被阅读0次

    一、闭包函数

    什么是闭包:python是一种面向对象的编程语言,在python中一切皆对象,这样就使得变量所拥有的属性,函数也同样拥有。这样我们可以理解,在函数内创建一个函数的行为是完全合法的,而这种函数就叫内嵌函数。内嵌函数(可以理解为内部函数)可以在外部函数作用域内正常调用,在外部函数作用域外则会报错。如果内嵌函数(可以理解为内部函数)引用了外部函数定义的对象(可以是外层之外,但不是全局变量),那么此时的函数就叫闭包函数。

    总之,一句话。如果外部函数的变量,被内部函数所引用,这种方式就是闭包。

    比如:实现一个常规的累加函数,这是常规写法:

    def func():
        num1 = 1
        num2 = 2
        return num1 + num2
    print(func())
    
    3
    

    使用闭包函数的写法:

    def func(num1):
        def add(num2):
            return num1 + num2
        return add
    a = func(1)
    print(a(2))
    
    3
    

    在这里,func()相当于外部函数,add()相当于内部函数,外部函数(func)的变量num1被内部函数(add)引用,这种方式就是闭包。另外,需要注意的是外部函数返回的是内部函数名。写法可以固定为:

    def func1():
        def func2():
            ...
        return func2 #外部函数返回的是内部函数名
    

    对比以上两种写法,其实看不出闭包有什么优势,反而变得更加繁琐。现在再举个例子,加深对闭包优势的了解。

    比如,设计一个计数器:

    def counter():
        cnt = [0]
        def add_one():
            cnt[0] += 1
            return cnt[0]
        return add_one
    
    num1 = counter()
    print(num1())
    print(num1())
    print(num1())
    print(num1())
    
    1
    2
    3
    4
    

    其实用常规方法设计的函数也可以实现,但换一种思路,在实际生活中,需求是不断变化的,现在需要一种从10开始的计数器,难道我们又要重新开发一套程序吗?没有更好的办法了吗?现在闭包的优势就来了。

    def counter(FIRST=0):
        cnt = [FIRST]
        def add_one():
            cnt[0] += 1
            return cnt[0]
        return add_one
    
    num1 = counter() #从1开始的计数器
    num10 = counter(10)#从10开始的计数器
    print(num1())
    print(num1())
    print(num1())
    print(num10())
    print(num10())
    print(num10())
    
    1
    2
    3
    11
    12
    13
    

    在这里,我们只需要简单的更改少量的代码counter(10),就可以实现新的需求。

    在实际测试工作中,我们会经常测试程序的性能,其中一个主要的指标是看程序的运行时间,在此场景中,闭包函数就会经常被使用,它会大大提高工作的效率。比如:

    import time
    
    def cost_time(func):
        def wrapper():
            start_time = time.time()
            func()
            end_time = time.time()
            print(f'程序运行时间为:{end_time - start_time}')#f字符串格式化在pyhton3.7开始使用
        return wraper
    
    @cost_time #装饰器
    def my_func():
        time.sleep(2)
    
    my_func()
    
    程序运行时间为:2.0004215240478516
    

    这里引用了装饰器,一般闭包函数和装饰器是配套使用的,这会大大提高python程序的效率。下面介绍装饰器的用法:

    二、装饰器函数

    什么是装饰器:python装饰器(function decorators)就是用来扩展函数功能的一种函数,其目的就是不改变原函数名(或类名)的情况下,给函数增加新的功能。装饰器就是通过闭包函数来给原来函数增加功能。因为调用函数不美观,所以引用了语法糖,也就是在需要添加功能的函数前面加上@即可。

    需要注意,@修饰符的用法。

    • '@’符号用作函数修饰符,必须出现在函数定义的前一行。不允许和函数定义在同一行。什么意思?还是用上面的例子举例:
    @cost_time #'@’符号用作函数修饰符,必须出现在函数定义的前一行,这就是第一行。
    def my_func():#这是函数,应该在'@’函数修饰符的下一行。
        time.sleep(2)
    
    • '@’符号用作函数修饰符,必须模块或者类定义层内对函数进行修饰,不允许修饰一个类。
    • 一个修饰符就是一个函数,它将被修饰的函数作为参数,并返回修饰的同名函数或其他可调用的东西。
    @cost_time #装饰器
    def my_func():
        time.sleep(2)
    

    这里的@,我们称之为语法糖,@cost_time就相当于cost_time(my_func),只不过更加的简洁。

    1. 带一个参数的装饰器

    在之前计算程序运行时间的例子中,my_func()函数没有带参数,如果需要带参数呢?

    import time
    
    def cost_time(func):
        def wrapper(info):
            print("this is decorator")
            start_time = time.time()
            func(info)
            end_time = time.time()
            print(f'程序运行时间为:{end_time - start_time}')
        return wraper
    
    @cost_time
    def my_func(info):
        print(info)
        time.sleep(1)
    
    my_func("hello world")
    
    this is decorator
    hello world
    程序运行时间为:1.0005288124084473
    
    1. 带多个参数的装饰器

    在这里, my_func(info)带了info参数,如果要实现的程序需要多个参数怎么办呢?一般情况下,我们会把*args和**kwargs,,作为装饰器内部函数wrapper()的参数。

    def decorator(func):
        def wrapper(*args, **kwargs):
            func(*args, **kwargs)
        return wrapper
    
    1. 带自定义参数的装饰器

    装饰器可以接受原函数任意类型和数量的参数,还可以接受自定义的参数,比如我要想控制程序运行的次数。

    import time
    
    def repeat(num):
        def cost_time(func):
            def wrapper(*args,**kwargs):
                start_time = time.time()
                for i in range(num):
                    print("this is decorator")
                    func(*args,**kwargs)
                end_time = time.time()
                print(f'程序运行时间为:{end_time - start_time}')
            return wrapper
        return cost_time
    
    @repeat(3)
    def my_func(info):
        print(info)
        time.sleep(1)
    
    my_func("hello world")
    print(my_func.__name__)
    
    this is decorator
    hello world
    this is decorator
    hello world
    this is decorator
    hello world
    程序运行时间为:3.006178617477417
    wrapper#不再是my_func函数
    

    这里需要注意的是my_func(info)函数被装饰后,元信息发生了改变,print(my_func.name)显示的结果是wrapper函数,不再是原来的my_func函数。为了解决这个问题,可以使用内置的装饰器@functools.wrap,它会帮助保留原函数的元信息,其本质也就是将原函数的元信息,拷贝到对应的装饰器函数里。

    import time
    import functools
    
    def cost_time(func):
        @functools.wraps(func)#使用内置的装饰器@functools.wrap,它会帮助保留原函数的元信息
        def wraper(info):
            print("this is decorator")
            start_time = time.time()
            func(info)
            end_time = time.time()
            print(f'程序运行时间为:{end_time - start_time}')
        return wraper
    
    @cost_time
    def my_func(info):
        print(info)
        time.sleep(1)
    
    my_func("hello world")
    print(my_func.__name__)
    
    this is decorator
    hello world
    程序运行时间为:1.0009491443634033
    my_func#元信息保留了
    
    1. 装饰器的嵌套

    python也支持多个装饰器,比如:

    @decorator1
    @decorator2
    def hello(info):
        print(info)
    

    它等同于

    decorator1(decorator2(hello))
    

    执行的顺序是从左到右执行,比如:

    import functools
    
    def decorator1(func):
        @functools.wraps(func)
        def wrapper(*args,**kwargs):
            print("this is decorator1")
            func(*args,**kwargs)
        return wrapper
    
    def decorator2(func):
        @functools.wraps(func)
        def wrapper(*args,**kwargs):
            print("this is decorator2")
            func(*args,**kwargs)
        return wrapper
    
    @decorator1
    @decorator2
    def hello(info):
        print(info)
    
    hello("hello world!")
    
    this is decorator1
    this is decorator2
    hello world!
    

    先执行的是decorator1,然后再执行decorator2,最后执行hello函数。

    相关文章

      网友评论

        本文标题:python闭包函数与装饰器函数

        本文链接:https://www.haomeiwen.com/subject/smkthctx.html