2.2 - 闭包和装饰器

2.2.1 闭包

   闭包的条件:

    1)在函数嵌套的前提下

    2)内部函数使用了外部函数的变量

    3)外部函数的返回值是内部函数的引用

def outer(a):
    b = 2

    def inner(c):
        return a + b + c

    return inner


f = outer(1)
print(f(3))

2.2.2 闭包的特点(内部函数引用的外部参数的赋值时间点)

  一般而言,当一个函数结束的时候,其变量及值会被释放;但在闭包的场景下,因为外部函数返回了内部函数的引用且内部函数用到了外部函数的变量,因此:【当外部函数执行到return语句的那一刻】,python会将此时内部函数用到外部变量的值存储到内部函数的 __closure__属性中。在外部函数执行到return之前,内部函数用到的外部变量并没有实际赋值,只是一个形参。

试分析以下案例的返回结果:

def outer():
    func = []

    for k in range(3):
        def inner():
            return k * k
        fun.append(inner)

    return func # func中的函数都是闭包,且在这个时候,才会开始对内部函数中的外部变量 k 赋 此时的实际值。这个时候,k的值是2  

f1, f2, f3 = outer()
print(f1()) # 4
print(f2()) # 4
print(f3()) # 4

 

2.2.3 函数装饰器

  1)无参的函数装饰器

  装饰器不改变函数原来的作用,而是对现有函数增加一些额外的功能。装饰器(注解)不是编码必须性,而是一种简洁的代码写法。

如下:给函数func增加 “输出函数执行时间” 功能。

 1 import time
 2 
 3 # 定义装饰器
 4 def timer(f):
 5     def inner(*args, **kwargs):
 6         start = time.time()
 7         f(*args, **kwargs)
 8         print(f'程序运行耗费了{time.time() - start}秒')
 9     return inner
10 
11 @timer
12 def func():
13     time.sleep(1.2) # 强制该函数停顿1.2秒
14 
15 
16 # 调用经过装饰器装饰的函数func
17 func()

  上述代码执行过程,在 4th 定义了一个函数timer接收另一个函数形参f,11th表示会将下面的函数定位到装饰器函数,此时装饰器函数并没有被调用只是声明一下,之后 12th 定义了被装饰的函数,定义完成之后会将被装饰函数作为实参传递给装饰器函数timer,timer最终返回 inner函数给调用方(即:被装饰函数)。至此,func函数已经被新增了功能,跳转到17th执行func函数的时候,会执行新增了功能的函数(即:inner)。【@timer 等价于 func = timer(func)

  2)有参数的函数装饰器

 1 import time
 2 
 3 # 定义装饰器
 4 def timer(name):
 5 
 6     def mid(f):
 7         def inner(*args, **kwargs):
 8             start = time.time()
 9             f(*args, **kwargs)
10             print(f'{name}, 程序运行耗费了{time.time() - start}秒')
11         return inner
12 
13     return mid
14 
15 @timer('主人')
16 def func():
17     time.sleep(1.2)  # 强制该函数停顿1.2秒
18 
19 # 调用经过装饰器装饰的函数func
20 func()

  有参数的装饰器相较于无参数的装饰器,不过是在外面额外套了一层函数,并且在 15th 位置的代码,由原先的只是声明一下没有实际执行变成了开始执行函数,执行完毕后返回的结果 等价于 原先的无参装饰器。

 

2.2.4 类装饰器

  1)无参的类装饰器

import time

class Timer:

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

    def __call__(self, *args, **kwargs):
        start = time.time()
        self.f(*args, **kwargs)
        print(f'程序运行耗费了{time.time() - start}秒')

@Timer
def fun(x):
    time.sleep(x)

fun(3)

  原理同上,函数接收函数形参f,类接收函数形参f;函数会返回一个带有装饰功能的函数,类也会返回一个带有装饰功能的函数。【当类像函数一样被调用的时候,会自动触发 __call__ 魔术方法】 

  2)有参的类装饰器

import time

class Timer:

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

    def __call__(self, f):
        def inner(*args, **kwargs):
            start = time.time()
            f(*args, **kwargs)
            print(f'{self.name}, 程序运行耗费了{time.time() - start}秒')
        return inner

@Timer('主人')
def func(x):
    time.sleep(x)

func(2)

 

2.2.5 多个装饰器时的执行顺序

@装饰器1
@装饰器2('主人')
@装饰器3
def func():
    ..........

  程序从上到下逐行顺序执行,有的装饰器只是声明一下如1和3,有的装饰器会真正调用如2(但是像这种真正调用的装饰器也是返回一个定义好的函数,和只是声明一下的装饰器形式上没有任何差别),直到遇到被装饰的函数,被装饰的函数定义完成之后会作为实参返回上一个装饰器3,装饰器3被触发之后也会将自身作为函数参数返回上一个装饰器2的返回参数,......,直到最上面的装饰器函数被调用执行返回一个 包含所有装饰功能的函数 给调用者,即:func。

 

2.2.6 内置装饰器函数

  @classmethod  装饰为类方法

  @staticmethod  装饰为静态方法

  @property  将类中的方法装饰为 只读的属性

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @property
    def adult_age(self):
        return 18

    def is_adult(self):
        if self.age >= self.adult_age:
            print(f'{self.name}, you are an adult')
        else:
            print(f'{self.name}, you are a minor')


p = Person('Danny', 17)
p.is_adult()
print(f'法定成年年龄是{p.adult_age}')

 

posted @ 2024-01-21 10:13  橘子葡萄火龙果  阅读(14)  评论(0)    收藏  举报