闭包与装饰器

闭包与装饰器


  1"""
  2Author: wangy325
  3Date: 2024-07-22 00:25:10
  4Description: 闭包(closure)与装饰器(注解 decorator)
  5"""
  6from contextlib import contextmanager
  7
  8
  9# 闭包 closure
 10# 闭包是一个特殊的函数
 11# 闭包有一个'内嵌'函数
 12# 闭包返回这个内嵌函数
 13# 内置函数可以访问外部的函数的变量
 14
 15# 以下用闭包解一个一元一次方程 a*x + b = y
 16
 17
 18def liner(a, b):
 19    def coordinate(x):
 20        return a * x + b
 21
 22    return coordinate
 23
 24
 25# 3x + 4 = ?
 26f = liner(3, 4)
 27# 3 * 2 + 4 = ?
 28print(f(2))
 29# 所以闭包就等价于
 30g = (liner(3, 4)(x) for x in range(2, 3))
 31for e in g:
 32    print(e)
 33
 34# ############# #
 35#     装饰器     #
 36# ############# #
 37'''
 38装饰器是python的一种语法糖
 39使用@开头, 类似于Java的注解
 40装饰器的作用, 很强
 41可以改变方法的行为, 而不直接修改方法的代码
 42可以实现类似AOP的功能
 43'''
 44
 45
 46# 装饰器应用场景:
 47# • 日志记录: 在函数执行之前和之后添加日志记录。
 48# • 计时器:  记录函数执行时间。
 49# • 权限控制:  检查用户权限,只有满足条件才能执行函数。
 50# • 缓存:  缓存函数的返回值,避免重复计算。
 51# • 异常处理:  在函数执行过程中捕获并处理异常。
 52
 53
 54#  装饰器的基本写法
 55def timer(func):
 56    def wrapper(*args, **kwargs):
 57        start = time.perf_counter()
 58        print(f'函数{func.__name__}开始执行, 开始时间: {start}')
 59        rel = func(*args, **kwargs)
 60        time.sleep(1)
 61        end = time.perf_counter()
 62        print(f'函数{func.__name__}执行结束, 运行耗时: {end - start}')
 63        return rel
 64
 65    return wrapper
 66
 67
 68# 可以看到, 装饰器其实使用了闭包
 69
 70
 71@timer
 72def add(x, y):
 73    return x + y
 74
 75
 76i = add(1, 3)
 77
 78# 结合上面闭包的概念, 实际上使用装饰器相当于调用闭包:
 79# 把函数的运行推迟
 80# 而在运行前,后做一些事情
 81j = timer(lambda x, y: x + y)(1, 3)
 82
 83
 84print(f"i:{i}, j:{j}")
 85
 86
 87# ##
 88# 装饰器当然可以传递参数
 89# 使用了多层闭包
 90# ##
 91def log(level):
 92    def inner(func):
 93        def wrapper(*args, **kwargs):
 94            print(f'[{level}]: fun {func.__name__} start...')
 95            rel = func(*args, **kwargs)
 96            print(f'[{level}]: fun {func.__name__} done... ')
 97            return rel
 98
 99        return wrapper
100
101    return inner
102
103
104@log("INFO")
105def cal(x, y):
106    return x * y
107
108
109cal(3, 6)
110
111
112#
113# 或者, 更加灵活地处理装饰器的参数
114# 装饰器参数作为装饰器的业务逻辑
115#
116def decorator(profile=False, logger=False):
117    def inner(func):
118        def wrapper(*args, **kwargs):
119            if profile:
120                start_time = time.time()
121            result = func(*args, **kwargs)
122            if profile:
123                end_time = time.time()
124                print(f"{func.__name__} took {end_time - start_time} seconds")
125            if logger:
126                print(f"calling {func.__name__} with args: {args}, kwargs: {kwargs}")
127            return result
128
129        return wrapper
130
131    return inner
132
133
134@decorator(profile=True, logger=True)
135def say_hi():
136    time.sleep(1)
137    print("Hi")
138
139
140say_hi()
141
142
143# ############# #
144#   with语句    #
145# ############# #
146
147# with语句除了自动关闭文件/资源之外
148# 还可以用作'上下文管理器类'
149# https://docs.python.org/zh-cn/3/reference/compound_stmts.html#the-with-statement
150# 
151
152class MyContextManager:
153    def __enter__(self):
154        print('进入上下文管理器类')
155        return self
156
157    def __exit__(self, ext_type, ext_value, exc_tb):
158        print('退出上下文管理器')
159
160
161with MyContextManager() as m:
162    print('在上下文管理器中执行操作')
163
164
165@contextmanager
166def my_context_manager():
167    print("进入上下文管理器")
168    try:
169        yield "上下文管理器中的值"
170    finally:
171        print("退出上下文管理器")
172
173
174with my_context_manager() as value:
175    print(f"在上下文管理器中获取值:{value}")