📂Python
🔖Python
2024-09-06 09:18阅读: 7评论: 0推荐: 0

Python闭包和装饰器

copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
# 闭包和类可以用来管理函数的状态 # 1.itemgetter # 如果需要根据字典的键来排序,或者元组的某个特定位置值来排序,那么可以使用itemgetter # 相应的如果要根据对象的某个属性来排序,那么可以使用attrgetter from operator import itemgetter, attrgetter from typing import Any frequency = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)] frequency_sorted = sorted(frequency, key=itemgetter(1)) frequency_sorted = sorted(frequency, key=lambda x: x[1]) # 这两条代码等价 staff = [ {"name": "Bob", "age": 20}, {"name": "Adam", "age": 22}, {"name": "Jack", "age": 21}, ] staff_sorted_by_age = sorted(frequency, key=itemgetter('age')) # lambda x: x['age'] class Student: def __init__(self, name, age): self.name = name self.age = age students = [Student('Bob', 20), Student('Adam', 22), Student('Jack', 21)] students_sorted_by_age = sorted(students, key=attrgetter('age')) # lambda x: x.age # 点进itemgetter可以看到源码,这其实是个类,比起我实现的单纯的闭包,它可能更完善通用,下面是我的实现 def itemgetter(key): def g(obj): return obj[key] return g # 2.装饰器以timer为例 from time import perf_counter def timer(func): def wrapper(*args, **kwargs): start = perf_counter() result = func(*args, **kwargs) end = perf_counter() print(func.__name__, end - start) return result return wrapper @timer def fib(n): a, b = 0, 1 for i in range(n): a, b = b, a + b return a # @timer等价于timer(fib) # 若这是一个有参数的装饰器,那么@timer(args) 等价于timer(args)(fib) # 即,无论@xxx后面是啥,不论是是否call@后面的类或函数,最终都会执行xxx(func),xxx可以是lru_cache, timer, lru_cache(maxsize=10)等等 # 下面实现一个带参数的装饰器,这会多嵌套一层闭包 def repeat(n=1): """将func重复n次,默认1次""" def decorator(func): def wrapper(*args, **kwargs): for _ in range(n): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(3) # 重复3次 def fib(n): a, b = 0, 1 for i in range(n): a, b = b, a + b return a @repeat() # 即使使用默认值,也必须加括号,因为repeat需要被调用返回decorator,之后才能decorator(func)返回最后被装饰的函数 def fib(n): a, b = 0, 1 for i in range(n): a, b = b, a + b return a # 若想实现有参数就传,没参数就不用括号这样的实现,本质是函数类根据参数的个数来决定返回哪个函数 def repeat(func=None, *, n=1): # 使用*,之后的参数只能传递关键字参数 """将func重复n次,默认1次""" def decorator(func): def wrapper(*args, **kwargs): for _ in range(n): result = func(*args, **kwargs) return result return wrapper if func is None: # 说明是@repeat(n=3)的形式,需要返回decorator return decorator else: # 说明是@repeat的形式,此时被调用就是传入func完成最终装饰了 return decorator(func) # 其它补充 # @decorate其实就是语法糖而已,等价于func = decorate(func) # 养成习惯在定义装饰器时,在wrapper上加上@functools.wraps(func)装饰,以保证func的__name__等属性正确 # def decorator(func): # @functools.wraps(func) # def wrapper(*args, **kwargs): # return func(*args, **kwargs) # return wrapper # 3.类装饰器 # 装饰器本身只要是可调用对象就行,所以函数、类都可以作为装饰器 from functools import wraps class decorator_func: """ 函数替换装饰器,虽然这是类装饰器,但其实还是返回一个闭包函数 个人认为适合写带参数的装饰器,嵌套层级不会那么多 """ def __init__(self, n=1): self.n = n def __call__(self, func): @wraps(func) def wrapper(*args, **kwargs): for _ in range(self.n): result = func(*args, **kwargs) return result return wrapper from functools import update_wrapper class decorator_class: """ 实例替换装饰器,将可调用对象从函数换成了这个类,能更好的管理状态 个人认为适合写无参数的装饰器,嵌套层级没那么多 当然,等会会给个例子,让它有无参数都适用 """ def __init__(self, func): update_wrapper(self, func) # 和@functools.wraps(func)等价 self.func = func def __call__(self, *args, **kwargs): for _ in range(self.n): result = self.func(*args, **kwargs) return result # 个人写的有无参数通用的实例替换类装饰器 class MyDecorator: def __init__(self, func=None, *, n=1): self.n = n self.func = func def __call__(self, *args, **kwargs): if self.func is None: return self.rebuild(*args, **kwargs) else: return self.call(*args, **kwargs) def rebuild(self, func): self.func = func return self def call(self, *args, **kwargs): return self.func(*args, **kwargs) # 补充:看看functools.lru_cache的实现,它是通过类型判断第一个参数是func还是它的参数从而确定返回什么的

本文作者:faf4r

本文链接:https://www.cnblogs.com/faf4r/p/18399589

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   faf4r  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起