B站|公众号:啥都会一点的研究生
相关阅读
这七个100%提高Python代码性能的技巧,一定要知道
整理了几个100%会踩的Python细节坑,提前防止脑血栓
整理了十个100%提高效率的Python编程技巧,更上一层楼
Python-列表,从基础到进阶用法大总结,进来查漏补缺
Python-元组,从基础到进阶用法大总结及与列表核心区别,进来查漏补缺
Python-字典,从基础到进阶用法大总结,进来查漏补缺
Python-集合,从基础到进阶大总结,进来查漏补缺
这些包括我在内都有的Python编程陋习,趁早改掉
Python可变类型的坑,不要再踩了
列表推导式,Python中最好的特性?可读性?
元组啊,不就是不可变的列表吗?
订阅专栏 ===> Python
装饰器可以在编写更少、更简单代码的基础上实现复杂的逻辑,并在任何地方都能调用
是体现“Python 之禅”的最佳Python特性
有许多很棒的内置Python装饰器使得效率蹭蹭上涨,只需使用一行代码就可以为现有函数或类添加复杂的功能
一起来看看吧!
1.@lru_cache:通过缓存加速程序
使用缓存技巧加速 Python 函数的最简单方法是使用@lru_cache
装饰器
这个装饰器可以用来缓存一个函数的结果,这样后续调用相同参数的函数就不会再执行了
它对于计算量大或使用相同参数频繁调用的函数特别有用
看一个直观的例子:
import time
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
start_time = time.perf_counter()
print(fibonacci(30))
end_time = time.perf_counter()
print(f"The execution time: {end_time - start_time:.8f} seconds")
# The execution time: 0.18129450 seconds
该例是经典的计算斐波那契数
计算fibonacci(30)
的时候很耗时,很多前面的Fibonacci数在递归过程中会重复计算多次
现在,使用@lru_cache
装饰器来加速它:
from functools import lru_cache
import time
@lru_cache(maxsize=None)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
start_time = time.perf_counter()
print(fibonacci(30))
end_time = time.perf_counter()
print(f"The execution time: {end_time - start_time:.8f} seconds")
# The execution time: 0.00002990 seconds
如上所示,使用@lru_cache
装饰器后,可以在0.00002990
秒内得到相同的结果,比之前的0.18129450
秒快了很多
@lru_cache
装饰器有一个maxsize
参数,指定存储在缓存中的最大结果数
当缓存已满并且需要存储新结果时,最近最久未使用的结果将从缓存中弹出
这称为最近最久未使用 (Least Recently Used,LRU) 策略
默认情况下,maxsize
设置为128
如果将其设置为None
,如示例,LRU 功能将被禁用,并且缓存可以无限增长
2. @total_ordering:类装饰器,填充缺失的比较方法
来自functools
模块的@total_ordering
装饰器,用于根据定义的方法为Python类生成缺少的比较方法
例如:
from functools import total_ordering
@total_ordering
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def __eq__(self, other):
return self.grade == other.grade
def __lt__(self, other):
return self.grade < other.grade
student1 = Student("Alice", 85)
student2 = Student("Bob", 75)
student3 = Student("Charlie", 85)
print(student1 < student2) # False
print(student1 > student2) # True
print(student1 == student3) # True
print(student1 <= student3) # True
print(student3 >= student2) # True
如上所示,在Student
类中没有定义__ge__
、__gt__
和 __le__
方法
但是,@total_ordering
的存在,让我们在不同实例之间的比较结果都是正确的,优点显而易见
- 可以使代码更清晰并节省时间,因为不需要写所有的比较方法
- 一些旧类可能没有定义足够的比较方法,
@total_ordering
装饰器确保使用安全
3. @contextmanager:制作定制的上下文管理器
Python提供上下文管理器机制来帮助正确的管理资源
大多数情况下,我们都知道使用with
语句:
with open("test.txt",'w') as f:
f.write("Yang is writing!")
如上,使用with
语句打开一个文件,写入后自动关闭
不需要显式的调用f.close()
函数来关闭文件
有时,需要为一些特殊的需求定义一个自定义的上下文管理器
在这种情况下,@contextmanager
装饰器该登场了
例如,下面的代码实现了一个简单的自定义上下文管理器,可以在文件打开或关闭时打印相应的信息
from contextlib import contextmanager
@contextmanager
def file_manager(filename, mode):
print("The file is opening...")
file = open(filename,mode)
yield file
print("The file is closing...")
file.close()
with file_manager('test.txt', 'w') as f:
f.write('Yang is writing!')
# The file is opening...
# The file is closing...
4. @property:为Python类设置getter和setter
getter和setter是面向对象编程(OOP)中的重要概念
对于类的每个实例变量,getter方法返回它的值,而setter方法设置或更新它的值
鉴于此,getter和setter也分别称为访问器和修改器
二者用于保护数据不被直接和意外地访问或修改
不同的OOP语言有不同的机制来定义getter和setter
在Python中,可以简单地使用@property
装饰器
class Student:
def __init__(self):
self._score = 0
@property
def score(self):
return self._score
@score.setter
def score(self, s):
if 0 <= s <= 100:
self._score = s
else:
raise ValueError('The score must be between 0 ~ 100!')
Yang = Student()
Yang.score=99
print(Yang.score)
# 99
Yang.score = 999
# ValueError: The score must be between 0 ~ 100!
如上所示,score
变量不能设置为 999,这是一个无意义的数字
因为使用@property
装饰器在setter函数中限制了它的可接受范围
5. @cached_property:将方法的结果缓存为属性
Python3.8为functool
模块引入了一个新的强大装饰器——@cached_property
可以将一个类的方法转换为一个属性,该属性的值计算一次,然后在实例的生命周期内作为普通属性缓存
from functools import cached_property
class Circle:
def __init__(self, radius):
self.radius = radius
@cached_property
def area(self):
return 3.14 * self.radius ** 2
circle = Circle(10)
print(circle.area)
# prints 314.0
print(circle.area)
# returns the cached result (314.0) directly
在上面的代码中,通过@cached_property
修饰了area
方法
所以没有对同一个不变实例的circle.area
进行重复计算
6. @classmethod:在Python类中定义类方法
在Python类中,有3种可能的方法类型:
- 实例方法:绑定到实例的方法。可以访问和修改实例数据。在类的实例上调用实例方法,可以通过
self
参数访问实例数据 - 类方法:绑定到类的方法。不能修改实例数据。在类本身上调用类方法,接收类作为第一个参数,通常命名为
cls
- 静态方法:未绑定到实例或类的方法
实例方法可以定义为普通的Python函数,只要第一个参数是self
但是,要定义一个类方法,需要使用@classmethod
装饰器
为了演示,以下示例定义了一个类方法,可用于通过radius获取Circle
实例:
class Circle:
def __init__(self, radius):
self.radius = radius
@classmethod
def from_diameter(cls, diameter):
return cls(diameter / 2)
@property
def diameter(self):
return self.radius * 2
@diameter.setter
def diameter(self, diameter):
self.radius = diameter / 2
c = Circle.from_diameter(8)
print(c.radius) # 4.0
print(c.diameter) # 8.0
7. @staticmethod:在Python类中定义静态方法
如前所述,静态方法不绑定到实例或类
被包含在一个类中只是因为在逻辑上属于那个类
静态方法通常用于执行一组相关任务(例如数学计算)的实用程序类中
通过将相关函数组织到类中的静态方法中,代码将变得更有条理且更易于理解
要定义一个静态方法,只需要使用@staticmethod
装饰器
看一个例子:
class Student:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
self.nickname = None
def set_nickname(self, name):
self.nickname = name
@staticmethod
def suitable_age(age):
return 6 <= age <= 70
print(Student.suitable_age(99)) # False
print(Student.suitable_age(27)) # True
print(Student('yang', 'zhou').suitable_age(27)) # True
8. @dataclass:用更少的代码定义特殊类
@dataclass
装饰器(Python 3.7引入)可以自动为一个类生成几个特殊的方法,如__init__
、__repr__
、__eq__
、__lt__
等
因此,可以节省大量编写这些基本方法的时间
如果一个类主要用于存储数据,那么@dataclass
装饰器是最需要被考虑的
下面的示例只定义了一个名为Point
的类的两个数据字段
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
point = Point(1.0, 2.0)
print(point)
# Point(x=1.0, y=2.0)
9. @atexit.register:注册一个在程序正常终止时执行的函数
来自atexit
模块的@register
装饰器可以在Python解释器退出时执行一个函数
这个装饰器对于执行最终任务非常有用,例如释放资源
例如:
import atexit
@atexit.register
def goodbye():
print("Bye bye!")
print("Hello Yang!")
输出是:
Hello Yang!
Bye bye!
如示例所示,由于使用@register
装饰器,终端打印了“Bye bye!”
即使没有显式调用goodbye
函数