37

Python 3.8 究竟要不要升级?用过之后的小哥这样说

 4 years ago
source link: https://www.tuicool.com/articles/JN36zq3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文转自雷锋网,如需转载请至雷锋网官网申请授权。

距 Python 3.8 稳定版正式发布已经过去了小半个月,不少 Python 常驻用户已经将 Python 更新到了 3.8 版本,也有一些朋友担心代码运行兼容性等问题,依然坚挺在 Python3.7 中。

那么,究竟要不要更新到 Python 3.8?新版本有哪些特点?它能为程序猿们带来怎样的收益?一位外国的 python 忠实小哥哥发了一篇文章,用众多实例详细讲解了 Python 3.8 特别的新功能。雷锋网 AI 开发者也将其更多功能整理编译到后文中,希望这篇文章能帮助你更好的理解 Python 3.8。

QnUz2eN.jpg!web

海象(walrus )运算符

Animesh Gaitonde 是 python 的狂热爱好者,下面是他对 python 3.8 中 walrus 运算符的使用心得——

最近,python 社区发布了该语言的 3.8 版本。作为python 的超级粉丝 ,我研究了发行说明,有一个特别的操作符引起了我的注意,该运算符称为 walrus 运算符(:=)或赋值表达式运算符。

这个新运算符(:=)使我们能够将值赋给表达式中的变量。这个符号有点像海象的眼睛和獠牙(因此也称为「海象运算符」)。

NRVB7jm.jpg!web

  • walrus 牛刀小试

现在让我们看看下面的代码段:

countries = [「India」,「USA」,「France」,「Germany」] 
 
if len(countries) < 5: 
 
print ("Length of countries is " + len(countries)) 

在这个代码段中,我们将调用函数 len()两次。有什么方法可以避免重新调用以提高可读性吗?是的,在改进代码之后,我们得到了以下结果:

country_size = len(countries) 
 
if country_size < 5: 
 
print ("Length of countries is " + country_size) 

还有进一步改进的余地吗?我们是否可以避免在单独的行中为变量「country_size」赋值?在 Python3.8 中引入的 walrus 运算符可以拯救我们,它使我们可以在 if 语句本身中声明和赋值:

if country_size := len(countries) < 5 : 
 
print ("Length of countries is " + country_size) 

让我们进一步探讨这个运算符的能力。

Y36NRbf.jpg!web

  • 代码行数与复杂度的平衡

让我们看看下面的例子:

eauayiV.jpg!web

多次调用成本高昂的函数

在上面的示例中,通过多次调用运行成本高的函数来填充列表。但在 walrus 运算符的帮助下,我们可以将结果存储在一个变量中,并在进一步的计算中重用同一个变量,从而避免多次调用 get_count()函数。下面是使用 walrus 运算符后的示例:

nAzeuqI.jpg!web

使用 walrus 运算符避免多个函数调用

从上面的例子可以看出,walrus 运算符减少了代码行,使代码更具可读性,从而简化了审阅者的工作。此外,它在代码行数和代码复杂度之间达到了更好地平衡。

  • 理解效率低下

NNR3yif.jpg!web

基于条件填充列表

在上面的例子中,我们正在执行多个操作。最初,我们创建了一个空列表,然后迭代一个 id 列表,并通过检查结果是否有效来填充该列表。

通过 walrus 运算符,我们可以简化上面的代码,并将所有内容放在一行中。

iuyAzmE.jpg!web

使用者需避免对 walrus 运算符的错误理解

  • 分块处理文件

在处理一个大文件时,我们将文件分成块并读取。每次读取块时,都会检查该值,并将其作为 while 循环中的终止条件,代码如下:

chunk = file.read(256) 
 
while chunk: 
 
process(chunk) 
 
chunk = file.read(256) 

通过使用 walrus 运算符,我们可以在 while 循环的表达式中读取并分配所读数值,这样还能够避免在 while 循环外显式声明变量。下面是一个例子:

while chunk := file.read(256) : 
 
process(chunk) 
  • 正则表达式匹配

正则表达式匹配是一个需要两个步骤的过程。在第一步中,我们检查是否发生匹配,在下一步中,我们提取子组:

zqMBr2z.jpg!web

正则表达式匹配

从上面的代码可以看出,如果匹配,我们正在重新计算 re.match(info),这会根据数据降低程序的速度。

上述代码利用 walrus 运算符可以重写如下,并且可以避免重新计算:

jYBbEra.jpg!web

正则表达式匹配:=

  • 哪里不能用 walrus 运算符?

1. 给变量赋值

a = 5 #Valid

a := 5 #InValid

empty_list = [] #Valid

empty_list := [] #InValid

如上所示,我们不能将=运算符与:=运算符一起使用,walrus 运算符只能是表达式的一部分。

2. 加减运算

a += 5 #Valid

a :+=5 # Invalid

3. lambda 函数中的赋值表达式

(lambda: a:= 5) # Invalid

lambda: (a := 5) # Valid, but not useful

(var := lambda: 5) # Valid

  • PEP-572 与争议

walrus 运算符是作为 pep-572(python 增强建议)的一部分引入的。

一个面向大众的工具,必须得到发明者圭多·范·罗森(Guido van Rossum)和他所选的代表们的批准。因此,围绕 walrus 运算符的争论很多,其中部分内容如下:

1. 句法变异

开发人员提出了许多替代「:=」,例如表达式->名称、名称->表达式、{表达式} 名称等。很少有使用现有关键字的建议,而其他使用新的运算符的建议。

2. 向后兼容性

这个特性不会向后兼容,也不会在以前的 python 版本上运行。

3. 运算符名称

人们推荐的名字,比如'assignment operator'、'named expression operator'、'becomes operator'等等,而不是像 walrus operator 这样的行话,会导致混淆。

femQVfz.jpg!web

3mAnYbf.jpg!web

关于 walrus 运算符的争论

关于 walrus 运算符的详细介绍就是这些,除此之外,Python3.8 也有其它新功能——

仅位置参数(Positional-Only Arguments)

这是新增的一个函数形参语法,用来指明某些函数形参必须使用仅限位置而非关键字参数的形式。这种标记语法与通过 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 

这种标记形式的一个用例是它允许纯 Python 函数完整模拟现有的用 C 代码编写的函数的行为。另一个用例是在不需要形参名称时排除关键字参数。例如,内置的 len() 函数的签名为 len(obj, /)。

除了这一点,在 Python3.8 中,可以用 / 来表示必须通过仅位置参数之前的参数。这极大地方便了之前在自定义函数中,开发者没有简单的方法指定参数为仅位置参数的问题。

def incr(x, /): 
 
return x + 1 

更多关于仅位置参数:https://www.python.org/dev/peps/pep-0570/

用于已编译字节码文件的并行文件系统缓存

新增的 PYTHONPYCACHEPREFIX 设置 (也可使用 -X pycache_prefix) 可将隐式的字节码缓存配置为使用单独的并行文件系统树,而不是默认的每个源代码目录下的 __pycache__ 子目录。

缓存的位置会在 sys.pycache_prefix 中报告 (None 表示默认位置即 __pycache__ 子目录)。

更详细内容:https://bugs.python.org/issue33499

调试构建使用与发布构建相同的 ABI

不管是在发布模式还是调试模式下构建,Python 现在都使用相同的 ABI。在   Unix 上,当 Python 以调试模式构建时,现在可以加载以发布模式构建的 C  扩展和使用稳定 ABI 构建的 C 扩展

更详细内容:https://bugs.python.org/issue36721

f 字符串支持一个方便的 = 说明符进行调试

=在 f-string 中添加了一个说明符。f 字符串(例如)f'{expr=}' 将扩展为表达式的文本、等号,然后扩展为求值表达式的表示形式。

更详细内容:https://bugs.python.org/issue36817

PEP 587:Python 初始化配置

在 PEP 587 添加了新的 C API 以配置 Python 初始化,从而提供了对整个配置的更好控制和更好的错误报告。

新的结构:

  • PyConfig

  • PyPreConfig

  • PyStatus

  • PyWideStringList

新的函数:

  • PyConfig_Clear()

  • PyConfig_InitIsolatedConfig()

  • PyConfig_InitPythonConfig()

  • PyConfig_Read()

  • PyConfig_SetArgv()

  • PyConfig_SetBytesArgv()

  • PyConfig_SetBytesString()

  • PyConfig_SetString()

  • PyPreConfig_InitIsolatedConfig()

  • PyPreConfig_InitPythonConfig()

  • PyStatus_Error()

  • PyStatus_Exception()

  • PyStatus_Exit()

  • PyStatus_IsError()

  • PyStatus_IsExit()

  • PyStatus_NoMemory()

  • PyStatus_Ok()

  • PyWideStringList_Append()

  • PyWideStringList_Insert()

  • Py_BytesMain()

  • Py_ExitStatusException()

  • Py_InitializeFromConfig()

  • Py_PreInitialize()

  • Py_PreInitializeFromArgs()

  • Py_PreInitializeFromBytesArgs()

  • Py_RunMain()

更详细内容:https://www.python.org/dev/peps/pep-0587/

Vectorcall: 用于 CPython 的快速调用协议

添加 "vectorcall" 协议到 Python/C API。它的目标是对已被应用于许多类的现有优化进行正式化。任何实现可调用对象的扩展类型均可使用此协议。

更详细内容:https://www.python.org/dev/peps/pep-0590/

具有外部数据缓冲区的 pickle 协议 5

当使用 pickle 在 Python 进程间传输大量数据以充分发挥多核或多机处理的优势时,非常重要一点是通过减少内存拷贝来优化传输效率,并可能应用一些定制技巧例如针对特定数据的压缩。

pickle 协议 5 引入了对于外部缓冲区的支持,这样 PEP 3118 兼容的数据可以与主 pickle 流分开进行传输,这是由通信层来确定的。

更详细内容:https://www.python.org/dev/peps/pep-0574/

qYrUj2A.jpg!web

博客地址:

http://t.cn/Ai389QHq

更多关于 Python3.8:

https://docs.python.org/zh-cn/3.8/whatsnew/3.8.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK