几个被淘汰的Python库,请不要再用!( 二 )


DataclassesPython 3.7 的一个重要补充是 dataclasses 包,它是 namedtuple 的替代品 。
你可能想知道为什么需要替换 namedtuple?以下是你应该考虑切换到数据类的一些原因:

  • 1、它可以是可变的
  • 2、默认提供 repr、eq、init、hash 魔术方法,
  • 3、允许指定默认值,
  • 4、支持继承 。此外,数据类还支持 frozen 和 slots(从 3.10 开始)属性以提供与命名元组的特征奇偶校验 。
切换真的不应该太难,因为你只需要更改定义:
# 老方式:# from collections import namedtuplefrom typing import NamedTupleimport sysUser = NamedTuple("User", [("name", str), ("surname", str), ("password", bytes)])u = User("John", "Doe", b'tfeL+uD...xd2')print(f"Size: {sys.getsizeof(u)}")# Size: 64# 新方式:from dataclasses import dataclass@dataclass()class User:name: strsurname: strpassword: bytesu = User("John", "Doe", b'tfeL+uD...xd2')print(u)# User(name='John', surname='Doe', password=b'tfeL+uD...xd2')print(f"Size: {sys.getsizeof(u)}, {sys.getsizeof(u) + sys.getsizeof(vars(u))}")# Size: 48, 152在上面的代码中,我们还包含了大小比较,因为这是 namedtuple 和数据类之间的较大差异之一,如上所见,命名元组的大小要小得多,这是由于数据类使用 dict 来表示属性 。
至于速度比较,除非你计划创建数百万个实例,否则属性的访问时间应该基本相同,或者不够重要:
import timeitsetup = '''from typing import NamedTupleUser = NamedTuple("User", [("name", str), ("surname", str), ("password", bytes)])u = User("John", "Doe", b'')'''print(f"Access speed: {min(timeit.repeat('u.name', setup=setup, number=10000000))}")# Access speed: 0.16838401100540068setup = '''from dataclasses import dataclass@dataclass(slots=True)class User:name: strsurname: strpassword: bytesu = User("John", "Doe", b'')'''print(f"Access speed: {min(timeit.repeat('u.name', setup=setup, number=10000000))}")# Access speed: 0.17728697300481144如果以上内容说服了你打算切换到数据类,请尽快尝试吧
相反,如果你不想切换并且出于某种原因真的想使用命名元组,那么你至少应该使用键入模块而不是collections中的 NamedTuple:
# 不好方式的:from collections import namedtuplePoint = namedtuple("Point", ["x", "y"])# 更好的方式:from typing import NamedTupleclass Point(NamedTuple):x: floaty: float最后,如果你既不使用 namedtuple 也不使用数据类,你可能需要考虑直接使用 Pydantic 。
Proper Logging 而不是 print这不是标准库的最新添加,但值得使用 - 你应该使用正确的日志记录而不是打印语句,如果你在本地调试问题,则可以使用 print,但对于任何无需用户干预即可运行的生产就绪程序,正确的日志记录是必须的 。
特别是考虑到设置 Python 日志记录非常简单:
import logginglogging.basicConfig(filename='Application.log',level=logging.WARNING,format='[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',datefmt='%H:%M:%S')logging.error("Some serious error occurred.")# [12:52:35] {<stdin>:1} ERROR - Some serious error occurred.logging.warning('Some warning.')# [12:52:35] {<stdin>:1} WARNING - Some warning.与打印语句相比,上面的简单配置将为你提供卓越的调试体验,最重要的是,你可以进一步自定义日志库以记录到不同的位置、更改日志级别、自动轮换日志等 。
f-strings 而不是 formatPython 包含很多格式化字符串的方法,包括 C 样式格式化、f 字符串、模板字符串或 .format 函数,不过,其中之一 - f-strings - 格式化的字符串文字,它们写起来更自然,可读性更强,并且是前面提到的选项中最快的 。
因此,我认为没有必要争论或解释为什么要使用它们,然而,在某些情况下不能使用 f 字符串:
使用 % 格式的唯一原因是用于记录:
import loggingthings = "something happened..."logger = logging.getLogger(__name__)logger.error("Message: %s", things)# 评估内部记录器方法logger.error(f"Message: {things}")# 立即评估在上面的示例中,如果你使用 f 字符串,则表达式将立即计算,而使用 C 样式格式,替换将被推迟到实际需要时,这对于消息分组很重要,其中具有相同模板的所有消息都可以记录为一个,这不适用于 f 字符串,因为模板在传递给记录器之前填充了数据 。


推荐阅读