- 特性:字典更新和合并运算符
两个新的运算符,|并|=已添加到内置dict 类中。
这| 运算符用于合并字典,而|= 运算符可用于更新字典。
PEP:584 代码: 对于合并:|
>>> a = {'farhad': 1, 'blog': 2, 'python': 3}
>>> b = {'farhad': 'malik', 'topic': 'python3.9'}
>> > 一个 | b
{'blog': 2, 'python': 3, 'farhad':'malik', 'topic': 'python3.9'}
>>> b | a
{'farhad': 1,'blog': 2, 'python': 3, 'topic':'python3.9' }
对于更新:|=
>>> a |= b
>>> a
{'blog': 2, 'python': 3,'farhad':'malik'}
要记住的关键规则是,如果有任何键冲突,那么将保留最右边的值。这意味着最后看到的值总是获胜。这也是其他dict 操作的当前行为。
详细说明: 正如我们在上面看到的,两个新的运算符| 和|= 已添加到内置dict 类中。
| 运算符用于合并字典,而运算|= 符可用于更新字典。
我们可以将其视为列表| 中的+ (连接),也可以将其视为列表|= 中的+= 运算符(扩展)。
如果我们评估 3.8 版,我们会注意到合并和更新字典的方法很少。
例如,我们可以这样做first_dict.update(second_dict) 。这种方法的问题在于它会first_dict 就地修改。解决此问题的一种方法是将 复制到first_dict 临时变量中,然后执行更新。但是,它添加了额外的不必要的代码只是为了让更新/ 合并工作。
我们也可以使用{**first_dict, **second_dict} . 这种方法的问题在于它不容易被发现,而且更难理解代码的意图。这种方法的另一个问题是映射类型被忽略并且类型始终是 dict 。例如,如果first_dict is adefaultdict 并且second_dict is 类型为 dict 那么它将失败。
最后,该collections 库包含一个ChainMap 函数。它可以接收两个字典,例如ChainMap(first_dict, second_dict) 并返回一个合并的字典,但同样,这个库并不为人所知。
dict 对于具有不兼容__init__ 方法的子类,它也会失败。
- 特点:新的灵活的高性能基于 PEG 的解析器
Python 3.9 版本提议将当前基于 LL(1) 的 Python 解析器替换为高性能且稳定的新的基于 PEG 的解析器。 PEP:617
详细说明: 当前的 CPython 解析器是基于 LL(1) 的。随后,该文法是基于 LL(1) 的,以允许它被 LL(1) 解析器解析。LL(1) 解析器是一个自顶向下的解析器。此外,它从左到右解析输入。当前语法是上下文无关语法,因此不考虑标记的上下文。
Python 3.9 版本提议用新的基于 PEG 的解析器替换它,这意味着它将解除当前的 LL(1) 语法 Python 限制。此外,当前的解析器已经修补了许多将要删除的 hack。因此,从长远来看,它将降低维护成本。
例如,尽管 LL(1) 解析器和语法很容易实现,但这些限制不允许它们以自然的方式向语言设计者和读者表达常见的结构。解析器只查看前面的一个标记来区分可能性。
选择运算符| 是有序的。例如,如果编写以下规则:
规则:A|B|C LL(1) 解析器,一个上下文无关的语法解析器,将生成结构,使得给定的输入字符串将推断它是否需要扩展 A 或 B 或 C。PEG 解析器是不同的。它将检查第一个替代方案是否成功。如果它只失败,那么它将继续第二个或第三个。
PEG 解析器为字符串准确生成一个有效树。因此它不像 LL(1) 解析器那样模棱两可。
PEG 解析器还AST 通过语法操作直接生成规则的节点。这意味着它避免了中间步骤的生成。
要采取的关键点是 PEG 解析器已经过广泛的测试和验证。PEG 解析器性能经过微调。结果,对于大多数指令,它在当前解析器的内存和速度消耗的 10% 以内。这主要是因为没有构造中间语法树。
- 特性:新的字符串函数去除前缀和后缀
str 该对象添加了两个新功能。
- 第一个函数删除前缀。它是str.removeprefix(prefix)。
- 第二个函数删除后缀。它是str.removesuffix(suffix)。
PEP:616 代码:
'farhad_python'.removeprefix('farhad_')
#returns python
'farhad_python'.removesuffix('_python')
#returns farhad
详细说明: 涉及操作文本的数据科学应用程序中的一项常见任务是删除字符串的前缀/后缀。str 该对象添加了两个新功能。这些函数可用于从字符串中删除不需要的前缀和后缀。
第一个函数删除前缀。它是str.removeprefix(prefix). 第二个函数删除后缀。这是str.removesuffix(suffix).
记住字符串是字符的集合,每个字符在字符串中都有一个索引。我们可以使用索引和冒号 : 来返回字符串的子集。此功能称为切片字符串。
如果我们研究函数,它们会在内部检查字符串是否以前缀开头(或以后缀结尾),如果是,则使用str[:] 切片功能返回不带前缀(或后缀)的字符串。
由于这些函数成为标准库的一部分,我们得到了一个一致的、不那么脆弱、高性能且更具描述性的 API。
- 特性:内置泛型类型的类型提示
在此版本中,通过删除 Python 中的并行类型层次结构,注释程序变得更加简单。
该版本在类型模块中当前可用的所有标准集合中启用了对泛型语法的支持。
我们可以使用list 或dict 内置集合类型作为泛型类型,而不是typing.List or typing.Dict在函数的签名中使用。
因此,代码现在看起来更干净,并且更容易理解/解释代码。
PEP:585 详细说明: 虽然 Python 是一种动态类型的语言,但 Python 程序中的类型注释可以实现类型的自省。随后,注解可用于 API 生成的运行时类型检查。
此版本已启用对类型模块中当前可用的所有标准集合中的泛型语法的支持。
泛型类型通常是一个容器,例如列表。它是一种可以参数化的类型。参数化泛型是具有容器元素预期类型的??泛型实例,例如 list[str]
我们可以使用list 或dict 内置集合类型作为泛型类型,而不是使用typing.List or typing.Dict.
例如,我们可以通过注释代码来指导 Python 运行时类型检查:
def print_value(input: str):
print(input)
# 如果输入不是字符串,我们会收到通知
在过去的几个版本中,已经在现有 Python 运行时的基础上逐步构建了许多静态类型功能。其中一些功能受到现有语法和运行时行为的限制。结果,由于泛型,在类型模块中存在重复的集合层次结构。
例如,我们将看到typing.List ,typing.Dictionary 以及内置的list ,dictionary 等等。这使我们能够编写代码:
def read_blog_tags(tags: list[str]) -> None :
for tag in tags:
print("Tag Name", tag)
- 特性:在 DateTime 中支持 IANA 时区
zoneinfo 已创建该模块以支持IANA 时区数据库。这种对IANA 时区数据库的支持已添加到标准库中。
PEP:615 IANA 时区通常称为tz or zone info 。有大量具有不同搜索路径的 IANA 时区可将 IANA 时区指定为日期时间对象。例如,我们可以将搜索路径的名称作为 Continent/City 传递给datetime 对象以设置其tzinfo.
dt = datetime(2000, 01, 25, 01, tzinfo=ZoneInfo("Europe/London"))
如果我们传入一个无效的密钥,那么zoneinfo.ZoneInfoNotFoundError将被引发。
详细说明: 我们使用该datetime 库创建一个datetime 对象并通过设置tzinfo 属性来指定其时区。但是,我们最终可能会在使用datetime.tzinfo 基线时创建复杂的时区规则。
大多数时候,我们只需要设置对象并将其时区设置为 UTC、系统本地时区或 IANA 时区。
我们可以创建一个zoneinfo.ZoneInfo(key) 对象,其中键是字符串类型,指示系统时区数据库中区域文件的搜索路径。zoneinfo.ZoneInfo(key) 可以创建对象并将其设置为对象的tzinfo 属性datetime 。
代码:
from zoneinfo import ZoneInfo
from datetime import datetime
dt = datetime(2000, 01, 25, 01, tzinfo=ZoneInfo("America/Los_Angeles"))
- 功能:取消并发期货的能力
新参数 cancel_futures 已添加到concurrent.futures.Executor.shutdown().
此参数cancels 所有未启动的未决期货。在 3.9 版之前,该过程将等待它们完成,然后再关闭执行程序。
解释: 新参数cancel_futures 已添加到ThreadPoolExecutor 和ProcessPoolExecutor 中。它的工作方式是当参数的值为 True 时,所有挂起的期货将在调用 shutdown() 函数时被取消。
简而言之,当 shutdown() 执行时,解释器检查执行器是否没有被垃圾回收。如果它仍在内存中,那么它会获取所有待处理的工作项,然后取消期货。
一旦没有待处理的工作项,它就会关闭工作人员。
- 特性:AsyncIO 和多处理改进
此版本中对 asyncio 和多??处理库进行了许多改进。
例如,
- 由于重大安全问题,不再支持reuse_address 参数 of 。asyncio.loop.create_datagram_endpoint()
- 添加了新的
coroutines , shutdown_default_executor() 和协程asyncio.to_thread() 。shutdown_default_executor 为等待ThreadPoolExecutor 完成关闭的默认执行程序安排关闭。asyncio.to_thread() 主要用于在单独的线程中运行 IO 绑定函数,以避免阻塞事件循环。 关于多处理库的改进,该类close() 中添加了一个新方法。 multiprocessing.SimpleQueue
此方法显式关闭队列。这将确保队列关闭并且不会停留比预期更长的时间。要记住的关键是,get() , put() ,empty() 一旦队列关闭,就不能调用方法。
- 特性:一致的包导入错误
在 3.9 版本之前导入 Python 库的主要问题是当相对导入超过其顶级包时,Python 中的导入行为不一致。
builtins.__import__() 引发ImportErrorValueError 时引发。importlib.__import__()
现在已经修复了。__import__() 现在引发ImportError 而不是ValueError .
- 特性:随机字节生成
3.9 版本中添加的另一个特性是函数random.Random.randbytes() 。此函数可用于生成随机字节。
我们可以生成随机数,但如果我们需要生成随机字节怎么办?在 3.9 版本之前,开发人员必须发挥创意来生成随机字节。虽然我们可以使用os.getrandom(), os.urandom() or secrets.token_bytes() 但我们不能生成伪随机模式。
例如,为了确保随机数的生成具有预期的行为并且该过程是可重现的,我们通常使用带有 random.Random 模块的种子。
因此,random.Random.randbytes() 引入了方法。它也可以以受控方式生成随机字节。
- 特性:字符串替换功能修复
在 Python 3.9 版本之前,“”.replace(“”,s,n) 所有非零 n 都返回空字符串而不是 s。
此错误使用户感到困惑并导致应用程序中的行为不一致。
3.9 版本已修复此问题,现在与"“.replace(”", s).
函数的工作方式replace 是,对于给定的最大替换出现参数,它用一组新字符替换字符串中的一组字符。
string.replace(s, old, new[, maxreplace])
返回字符串 s 的副本,其中所有出现的子字符串 old 都替换为 new。如果给出了可选参数 maxreplace,则替换第一个 maxreplace 出现。
为了进一步解释这个问题,在 3.9 版本之前,该replace 函数的行为不一致
"".replace("", "blog", 1)
Returns ''
期望看到博客
"".replace("", "|", 1)
返回 ''
人们希望看到 |
"".replace("", "prefix")
但是返回 'prefix'
因此,现在的变化是,如果我们传入:
“”.replace(“”,s,n) 为所有非零 n 返回 s 而不是空字符串
|