记录python版本更新需要注意的部分,一些被新版本覆盖的旧版本更改不会存在于旧版本中
python3.6
新的特性
1.PEP 498:格式化字符串字面值
格式如下:
f'{value}'
和普通字符串区别:
1.添加前缀f
2.支持{value},value为变量值(支持一些较复杂格式可以点击标题链接到api查看)
2.PEP 487:自定义类创建
现在可以在不使用元类的情况下自定义子类的创建。 当一个新的子类被创建时将在基类上调用新的 __init_subclass__
类方法:
class PluginBase:
subclasses = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.subclasses.append(cls)
class Plugin1(PluginBase):
pass
class Plugin2(PluginBase):
pass
为了让来自 __init_subclass__()
实现的零参数 super()
调用能正确工作,自定义元类必须保证新的 __classcell__
命名空间入口被传播到 type.__new__
(如 创建类对象 中所描述)。
3.bpo-27350:新的 dict 实现
dict实现修改,与Python 3.5相比,内存使用量减少了20%至25%。
区别大概说一下:
python 3.5及之前:
当初始化一个空字典时,底层会初始化一个8 * 3的二维数组,行内容为[key的哈希值,指向key的指针,指向v的指针]
插入数据时获取key的哈希值,然后对8取余数,插入二维数组余数索引位置
注意:
1.使用开放寻址处理hash冲突
2.字典数量超过数组容量2/3时,数组扩容,容量翻倍,扩容后就是对新的数组长度取余数,原来的数据位置需要重新计算移动,插入效率变低
python 3.5之后
当初始化一个空字典时,底层会初始化一个长度为8的一维数组和一个空的二维数组(内容和之前一样),
插入数据时获取key的哈希值,然后对8取余数,二维数组直接后面添加数据,假设数据在二维数组的索引为n,在一维数组索引为余数的地方放入n
注意:这个修改在3.6版本还持保守态度,在3.7成为正式规范
python3.7
新的特性
1.PEP 553:内置的 breakpoint()
Python 3.7 包含了新的内置 breakpoint()
函数,作为一种简单方便地进入 Python 调试器的方式。
内置 breakpoint()
会调用 sys.breakpointhook()
。 在默认情况下后者会导入 pdb
然后再调用 pdb.set_trace()
,但是通过将 sys.breakpointhook()
绑定到你选定的函数,breakpoint()
可以进入任何调试器。 此外,环境变量 PYTHONBREAKPOINT
可被设置为你选定的调试器的可调用对象。 设置 PYTHONBREAKPOINT=0
会完全禁用内置 breakpoint()
。
简单来说就是包装了pdb,更加方便使用pdb,举例:
使用 pdb 调试器设置断点
def f():
a = 1
# 设置一个断点
import pdb; pdb.set_trace()
return 1
使用 breakpoint() 设置断点
def f():
a = 1
breakpoint(header="start")
return 1
执行f()的效果相同,如下:
$ python.exe xxx.py
start
> /home/nsf/Desktop/workspace/leecode/wechat/test1.py(3)<module>()
-> return 1
(Pdb) print(a)
1
(Pdb)
使用其他调试器
通过 PYTHONBREAKPOINT
还可选用别的调试器。例如,当我们想要使用 PuDB (一个基于控制台的可视化调试器)时,只需修改 PYTHONBREAKPOINT
:
安装PuDB
pip install pudb
运行
$ PYTHONBREAKPOINT=pudb.set_trace python3.9 xxx.py
默认开启5555端口,通过浏览器可查看,类似pycharm的调试功能
调试命令
pdb命令行:
1)进入命令行Debug模式,python -m pdb xxx.py
2)h:(help)帮助
3)w:(where)打印当前执行堆栈
4)d:(down)执行跳转到在当前堆栈的深一层(个人没觉得有什么用处)
5)u:(up)执行跳转到当前堆栈的上一层
6)b:(break)添加断点
b 列出当前所有断点,和断点执行到统计次数
b line_no:当前脚本的line_no行添加断点
b filename:line_no:脚本filename的line_no行添加断点
b function:在函数function的第一条可执行语句处添加断点
7)tbreak:(temporary break)临时断点
在第一次执行到这个断点之后,就自动删除这个断点,用法和b一样
8)cl:(clear)清除断点
cl 清除所有断点
cl bpnumber1 bpnumber2... 清除断点号为bpnumber1,bpnumber2...的断点
cl lineno 清除当前脚本lineno行的断点
cl filename:line_no 清除脚本filename的line_no行的断点
9)disable:停用断点,参数为bpnumber,和cl的区别是,断点依然存在,只是不启用
10)enable:激活断点,参数为bpnumber
11)s:(step)执行下一条命令
如果本句是函数调用,则s会执行到函数的第一句
12)n:(next)执行下一条语句
如果本句是函数调用,则执行函数,接着执行当前执行语句的下一条。
13)r:(return)执行当前运行函数到结束
14)c:(continue)继续执行,直到遇到下一条断点
15)l:(list)列出源码
l 列出当前执行语句周围11条代码
l first 列出first行周围11条代码
l first second 列出first--second范围的代码,如果second<first,second将被解析为行数
16)a:(args)列出当前执行函数的函数
17)p expression:(print)输出expression的值
18)pp expression:好看一点的p expression
19)run:重新启动debug,相当于restart
20)q:(quit)退出debug
21)j lineno:(jump)设置下条执行的语句函数
只能在堆栈的最底层跳转,向后重新执行,向前可直接执行到行号
22)unt:(until)执行到下一行(跳出循环),或者当前堆栈结束
23)condition bpnumber conditon,给断点设置条件,当参数condition返回True的时候bpnumber断点有效,否则bpnumber断点无效
注意:
1:直接输入Enter,会执行上一条命令
2:输入PDB不认识的命令,PDB会把他当做Python语句在当前环境下执行
python3.8
新的特性
1.PEP 572:海象运算符
新增的语法 :=
可在表达式内部为变量赋值,举例:
# demo1
if (n := len(a)) > 10:
print(f"List is too long ({n} elements, expected <= 10)")
# demo2
while (block := 1) != '':
process(block)
# demo3
[clean_name.title() for name in names if (clean_name := normalize('NFC', name)) in allowed_names]
由上面的例子可以看出,海象运算符的作用是简化一些逻辑表达,所以请尽量将海象运算符的使用限制在清晰的场合中,以降低复杂性并提升可读性
注意:
1.demo2不等同于
block = 1
while block != '':
process(block)
demo2中每次循环会重新给block赋值为1,所以当循环中使用海象运算符时需要特别注意
2.海象运算符作用域是能扩散到表达式外的
2.PEP 570:仅限位置形参
新增了一个函数形参语法 /
用来指明某些函数形参必须使用仅限位置而非关键字参数的形式。 这种标记语法与通过 help()
所显示的使用 Larry Hastings 的 Argument Clinic 工具标记的 C 函数相同。
在下面的例子中,形参 a 和 b 为仅限位置形参,c 或 d 可以是位置形参或关键字形参,而 e 或 f 要求为关键字形参:
def f(a, b, /, c, d, *, e, f):
print(a, b, c, d, e, f)
以下均为合法的调用:
f(10, 20, 30, d=40, e=50, f=60)
但是,以下均为不合法的调用:
f(10, b=20, c=30, d=40, e=50, f=60) # b cannot be a keyword argument
f(10, 20, 30, 40, 50, f=60) # e must be a keyword argument
好处:
1.允许纯 Python 函数完整模拟现有的用 C 代码编写的函数的行为,例如,内置的 divmod()
函数不接受关键字参数
def divmod(a, b, /):
"Emulate the built in divmod() function"
return (a // b, a % b)
2.在不需要形参名称时排除关键字参数。 例如,内置的 len()
函数的签名为 len(obj, /)
。 这可以排除len(obj='hello')
这种笨拙的调用形式
3.将形参标记为仅限位置形参,允许在未来修改形参名而不会破坏原有的代码。 例如,在 statistics
模块中,形参名 dist 在未来可能被修改。
def quantiles(dist, /, *, n=4, method='exclusive')
...
4.极大地简化了需要接受任意关键字参数的函数和方法的实现。 例如,以下是一段摘自 collections
模块的代码:
class Counter(dict):
def __init__(self, iterable=None, /, **kwds):
# Note "iterable" is a possible keyword argument
总结:约束规范位置参数和关键字参数,让目前的一些写法更加简化和兼容,从此也能看出代码易用和规范需要寻找一个平衡
3.bpo-36817:f-字符串支持 =
增加 =
说明符用于 f-string。 形式为 f'{expr=}'
的 f-字符串将扩展表示为表达式文本,加一个等于号,再加表达式的求值结果。 例如:
>>> user = 'eric_idle'
>>> member_since = date(1975, 7, 31)
>>> f'{user=} {member_since=}'
"user='eric_idle' member_since=datetime.date(1975, 7, 31)"
其他语言特性修改
1.bpo-35224:字典推导式
字典推导式已与字典字面值实现同步,会先计算键再计算值,举例:
def f1():
print(1)
def f2():
print(2)
dd = {f1(): f2() for _ in range(2)}
"""
python3.8输出:
1
2
1
2
python3.6输出:
2
1
2
1
"""
此次修改是因为海象运算符,在字典推导式中使用海象运算符有时需要保证顺序,举例:
>>> names = ['Martin von Löwis', 'Łukasz Langa', 'Walter Dörwald']
>>> {(n := normalize('NFC', name)).casefold() : n for name in names}
{'martin von löwis': 'Martin von Löwis',
'łukasz langa': 'Łukasz Langa',
'walter dörwald': 'Walter Dörwald'}
python3.9
新的特性
1.PEP 584:字典合并与更新运算符
合并 (|
) 与更新 (|=
) 运算符已被加入内置的 dict
类,它们为现有的 dict.update
和 {**d1, **d2}
字典合并方法提供了补充
示例:
>>> 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'}
性能优化
1.bpo-32856:列表推导
Optimized the idiom for assignment a temporary variable in comprehensions. Now for y in [expr]
in comprehensions is as fast as a simple assignment y = expr
. For example:
sums = [s for s in [0] for x in data for s in [s + x]]
Unlike the :=
operator this idiom does not leak a variable to the outer scope.
(Contributed by Serhiy Storchaka in bpo-32856.)
对于列表推导,会有不同的表达,举例来说,比如:[f(x) + g(f(x)) for x in range(10)]可能有若干种写法:
1.[f(x) + g(f(x)) for x in range(10)]
2.f_samples = (f(x) for x in range(10))
[y+g(y) for y in f_samples]
3.def func(x):
y = f(x)
return y + g(y)
[func(x) for x in range(10)]
4.[y + g(y) for x in range(10) for y in [f(x)]]
相比较而言第四种更加能够让人接受,但是缺点是你构造了一个[f(x)]临时变量,速度比较慢,
本次性能优化就是让for y in [expr] 与简单赋值语句 y = expr 一样快速
python3.10
新的特性
1.PEP 634:结构模式匹配
相当于c语言中的case,举例:
def http_error(status):
match status:
#简单变量
case 400:
return "Bad request"
# 支持其他变量
case (0, 0):
print("Origin")
# 类对象支持参数
case Point(x=0, y=0):
print("Origin is the point's location.")
# 支持if子句
case Point(x, y) if x == y:
print(f"The point is located on the diagonal Y=X at {x}.")
# 支持"或"语句
case 401 | 403 | 404:
return "Not allowed"
# 其他情况
case _:
return "Something's wrong with the Internet"
2.PEP 604:新型联合运算符
旧版:
def square(number: Union[int, float]) -> Union[int, float]:
return number ** 2
新版:
def square(number: int | float) -> int | float:
return number ** 2
这种新语法支持isinstance()
和issubclass()
:
>>> isinstance(1, int | str)
True
其他语言特性修改
1.bpo-29882 : int
新增int.bit_count()`,返回给定整数的二进制展开式中 1 的数量
python3.11
改进的模块
1.bpo-44357:math
添加math.cbrt()
:返回 x 的立方根