背景
偶然在网上看到一篇文章Python 疑难问题:[] 与 list() 哪个快?为什么快?快多少呢?提到python3.9为list()实现了更快的vectorcall协议,提升了使用list()创建列表的速度,恍然:啊!python都更新到3.9了呀。想起昨天看到的一篇文章,java都已经更新到14了。技术更新是如此之快,我的学习进度如此之慢!
概述
通过研究3.9新特性,了解下之前没有了解/使用的基础知识。
特性一:字典合并与更新运算符
dict.update
and {**d1,**d2}
use:
>>> d1 = {"user":"12311"}
>>> d2 = {"name":"lalalala"}
>>> d3 = {}
>>> d3.update(d1)
>>> print(d3)
{'user': '12311'}
>>> d3.update(d2)
>>> print(d3)
{'user': '12311', 'name': 'lalalala'}
>>> d4 = dict(d1,**d2)
>>> print(d4)
{'user': '12311', 'name': 'lalalala'}
新:
合并( | ) 更新( | =) |
use:
>>> x = {"key1": "value1 from x", "key2": "value2 from x"}
>>> y = {"key2": "value2 from y", "key3": "value3 from y"}
>>> x | y
{'key1': 'value1 from x', 'key2': 'value2 from y', 'key3': 'value3 from y'}
>>> y | x
{'key2': 'value2 from x', 'key3': 'value3 from y', 'key1': 'value1 from x'}
特性二:新增用于移除前缀和后缀的字符串方法
增加了 str.removeprefix(prefix)
和 str.removesuffix(suffix)
用于方便地从字符串移除不需要的前缀或后缀。
如何理解?看代码实现清晰明了
def removeprefix(self: str, prefix: str, /) -> str:
if self.startswith(prefix):
return self[len(prefix):]
else:
return self[:]
def removesuffix(self: str, suffix: str, /) -> str:
# suffix='' should not call self[:-0].
if suffix and self.endswith(suffix):
return self[:-len(suffix)]
else:
return self[:]
endwith(suffix[, start[, end]])
可传入元组
PEP 616 – String methods to remove prefixes and suffixes中提到suffix参数可为元组
suffix 为字符串:
name = "123nanana"
print(name.endwith("na"))
>>>True
suffix 为元组:
>>> name3 = "fsdsfs"
>>> if name3.endswith(("mixin","test")): print("123333")
... else: print("4444444444444")
...
4444444444444
特性三:标准多项集中的类型标注泛型
了解这个特性之前,我们先了解以下type hint是什么?先看下这篇文章全面理解Python中的类型提示(Type Hints),python其实和php有点像,都是弱类型语言,不像java必须指定类型(变量,返回值,传入值等)
type hint总结:
可添加的类型,参阅官方文档
优点
- 易于理解代码,在调用函数时能更快知道输入和输出的数据类型
- 易于重构
- 易于使用库
- Type Linters尽早捕获错误,但是在更复杂的情况(如 嵌套函数)是检测不出来的
- 验证运行数据 可使用pydantic在业务逻辑运行之前自动检查,而不需要type assert
from datetime import datetime
from typing import List
from pydantic import BaseModel, ValidationError
class User(BaseModel):
id: int
name = 'John Doe'
signup_ts: datetime = None
friends: List[int] = []
external_data = {'id': '123', 'signup_ts': '2017-06-01 12:22',
'friends': [1, 2, 3]}
user = User(**external_data)
try:
User(signup_ts='broken', friends=[1, 2, 'not number'])
except ValidationError as e:
print(e.json())
使用
Python 3.5+版本
通用标准:
- 使用
:
语句将信息附加到变量或函数参数中。 ->
运算符用于将信息附加到函数/方法的返回值中。
Python 3.6示例
from typing import List
class A(object):
def __init__() -> None:
self.elements : List[int] = []
def add(element: int) -> None:
self.elements.append(element)
- 强制你导入所有类型依赖项,即使它们根本不在运行时使用。
- 在类型提示中,会使用到复合类型,例如List[int]。而为了构造这些复杂类型,解释器在首次加载此文件时需要执行一些操作。
这两个问题在Python 3.7中得到解决!
from __future__ import annotations
—加入该语句,将不再构造复合类型
Python2.7+版本
由于作者没用过2.7版本,本文不作赘述。
- 可用类型注释
- 类型库可作为pypi包使用
缺点: 长度限制;可能会与typelint产生冲突(通过引入typed-ast parser解决)
重点来啦
到这里,我们已经了解了type hint ,而 标准多项集中的类型标注泛型指的是什么呢?
我们不需要再导入typing的大多数类型(List,Dict),内置多项集类型如list和dict作为通用类型
tuple # typing.Tuple
list # typing.List
dict # typing.Dict
set # typing.Set
frozenset # typing.FrozenSet
type # typing.Type
collections.deque
collections.defaultdict
collections.OrderedDict
collections.Counter
collections.ChainMap
collections.abc.Awaitable
collections.abc.Coroutine
collections.abc.AsyncIterable
collections.abc.AsyncIterator
collections.abc.AsyncGenerator
collections.abc.Iterable
collections.abc.Iterator
collections.abc.Generator
collections.abc.Reversible
collections.abc.Container
collections.abc.Collection
collections.abc.Callable
collections.abc.Set # typing.AbstractSet
collections.abc.MutableSet
collections.abc.Mapping
collections.abc.MutableMapping
collections.abc.Sequence
collections.abc.MutableSequence
collections.abc.ByteString
collections.abc.MappingView
collections.abc.KeysView
collections.abc.ItemsView
collections.abc.ValuesView
contextlib.AbstractContextManager # typing.ContextManager
contextlib.AbstractAsyncContextManager # typing.AsyncContextManager
re.Pattern # typing.Pattern, typing.re.Pattern
re.Match # typing.Match, typing.re.Match
题外话: ...
这个是啥?
原文: Python 为什么会有个奇怪的“…”对象?
使用场景:
- 扩展切片语法(?不懂,没有示例)
- 代替
pass
- 在type hint中的用法
代表不定长的参数,如Tuple[int,...]
表示一个元组,元素是int类型但数量不限
代表不确定的变量类型,如下例子
from typing import TypeVar, Generic
T = TypeVar('T')
def fun_1(x: T) -> T: ... # T here
def fun_2(x: T) -> T: ... # and here could be different
fun_1(1) # This is OK, T is inferred to be int
fun_2('a') # This is also OK, now T is str
- 无限循环
对于列表和字典这样的容器,如果其内部元素是可变对象的话,则存储的是对可变对象的引用。那么,当其内部元素又引用容器自身时,就会递归地出现无限循环引用。>>> mydict = {"name":"test"} >>> mydict["name"] = mydict >>> mydict {'name': {...}} >>> mylist = [1,2,3,] >>> mylist[0] = mylist >>> mylist [[...], 2, 3]
特性四: 新的解析器
旧的:LL(1)
新的:基于PEG的新解析器
在 Python 3.10 中,旧解析器将被移除,依赖于它的所有功能也将被移除(主要是 parser 模块,它早已被弃用)。 只有 在 Python 3.9 中,你可以使用命令行开关 (-X oldparser) 或环境变量 (PYTHONOLDPARSER=1) 切换回 LL(1) 解析器。
特性五: 新增zoneinfo模块
在了解这个模块之前,我们有必要深入了解一下datetime—基本的日期和时间类型;而datetime和time之间的关联与区别可以理解为datetime基于time进行了封装;
特点: 引入IANA时区数据库
简介: zoneinfo 模块添加了 zoneinfo.ZoneInfo,这是一个基于系统时区数据的实体 datetime.tzinfo 实现。
特性六: 新增 graphlib模块
用于操作图表结构
其他特性
ast 模块新增 ast.unparse()
ast.unparse()
用于反解析ast.AST对象并产生响应的代码字符串;
ast常用于静态代码检查,代码生成等,如pylint就会用到;
asyncio模块 - 多进程线程
添加了coroutinu shutdown_default_executor()
,可为等待 ThreadPoolExecutor 结束关闭的默认执行器安排关闭日程操作;
添加了asyncio.PidfdChildWatcher
,linux专属子监视器实现;
添加了 coroutine asyncio.to_thread()
。 它主要被用于在单独线程中运行 IO 密集型函数以避免阻塞事件循环,实质上就相当于是 run_in_executor()
的高层级版本
random 模块
添加了random.Random.randbytes
方法生成随机字节串;
结语
大概浏览了3.9的变更,发现有些东西根本没接触过(可能未来会有接触吧…)也不知道作为测试工程师 会应用在什么场景,因此不作记录。