在 Python 中的最新版本发布!自夏季以来,Python 3.8 已在 beta 版本中可用,但在 2019 年 10 月 14 日,第一个正式版本已准备就绪。现在,我们所有人都可以开始使用新功能并从最新改进中受益。
Python 3.8 带来了什么?该文档很好地概述了新功能。但是,本文将更深入地介绍一些最大的变化,并向您展示如何利用 Python 3.8。
在本文中,您将了解:
-
使用赋值表达式简化一些代码结构
-
在自己的函数中强制仅位置参数
-
指定更精确的类型提示
-
使用 f 字符串进行更简单的调试
除了少数例外,Python 3.8 对早期版本进行了许多小的改进。在本文结尾处,您将看到许多不那么引人注意的更改,并讨论了一些使 Python 3.8 比其先前版本更快的优化。最后,您将获得有关升级到新版本的一些建议。
房间里的海象:赋值表达
Python 3.8 中最大的变化是赋值表达式的引入。它们使用新的符号(:=)编写。该运算符通常被称为海象运算符,因为它类似于海象的侧面的象牙和海象牙。
赋值表达式使您可以在同一表达式中赋值并返回一个值。例如,如果要分配给变量并打印其值,则通常需要执行以下操作:
>>> walrus = False>>> print(walrus)False
复制代码
在 Python 3.8 中,可以使用 walrus 运算符将这两个语句合并为一个:
>>> print(walrus := True)True
复制代码
分配表达式使您可以分配True给walrus,并立即打印该值。但是请记住,如果没有它,海象运算符不会做任何不可能的事情。它只会使某些构造更加方便,并且有时可以更清楚地传达代码的意图。
一种显示海象运算符优势的模式是while循环,您需要在循环中初始化和更新变量。例如,以下代码要求用户输入直到输入quit:
inputs = list()current = input("Write something: ")while current != "quit":inputs.append(current)current = input("Write something: ")
复制代码
此代码不理想。您正在重复该input()语句,并且需要以某种方式将其添加current到列表中,然后再询问用户。更好的解决方案是设置一个无限while循环,然后使用它break来停止循环:
inputs = list()while True:current = input("Write something: ")if current == "quit":breakinputs.append(current)
复制代码
这会将测试移回应有的while行。但是,该行现在发生了几件事,因此需要花费更多的精力才能正确地阅读它。对于海象运算符何时有助于使您的代码更具可读性,请做出最佳判断。
PEP 572 描述了赋值表达式的所有细节,包括将其引入语言的一些原理,以及如何使用海象运算符的几个示例。
仅位置参数
内置函数float()可用于将文本字符串和数字转换为float对象。考虑以下示例:
>>> float("3.8")3.8>>> help(float)class float(object)| float(x=0, /)|| Convert a string or number to a floating point number, if possible.[...]
复制代码
仔细看看的签名float()。注意/参数后的斜杠()。这是什么意思?
注意:有关该/符号的深入讨论,请参阅 PEP 457-仅位置参数的符号。
事实证明,虽然float()调用了一个参数,x但不允许使用其名称:
>>> float(x="3.8")Traceback (most recent call last):File "<stdin>", line 1, in <module>TypeError: float() takes no keyword arguments
复制代码
使用时float(),只允许按位置而不是关键字指定参数。在 Python 3.8 之前,此类仅位置参数仅适用于内置函数。没有简单的方法来指定参数在您自己的函数中应该仅位置:
>>> def incr(x):... return x + 1...>>> incr(3.8)4.8>>> incr(x=3.8)4.8
复制代码
这是可能的模拟位置-only 参数使用*args,但是这是不够灵活,不易阅读,并迫使你实现自己的参数解析。在 Python 3.8 中,您可以/用来表示必须由位置指定之前的所有参数。您可以重写incr()为仅接受位置参数:
>>> def incr(x, /):... return x + 1...>>> incr(3.8)4.8>>> incr(x=3.8)Traceback (most recent call last):File "<stdin>", line 1, in <module>TypeError: incr() got some positional-only arguments passed askeyword arguments: 'x'
复制代码
通过/在之后添加x,您可以将其指定x为仅位置参数。您可以将常规参数与仅位置参数结合使用,方法是将常规参数放在斜杠之后:
>>> def greet(name, /, greeting="Hello"):... return f"{greeting}, {name}"...>>> greet("Łukasz")'Hello, Łukasz'>>> greet("Łukasz", greeting="Awesome job")'Awesome job, Łukasz'>>> greet(name="Łukasz", greeting="Awesome job")Traceback (most recent call last):File "<stdin>", line 1, in <module>TypeError: greet() got some positional-only arguments passed askeyword arguments: 'name'
复制代码
在中greet(),斜线放在name和之间greeting。这意味着它name是仅位置参数,greeting而是可以通过位置或关键字传递的常规参数。
乍一看,仅位置参数似乎有点局限性,与 Python 关于可读性重要性的口号背道而驰。您可能会发现在很多情况下仅位置参数可以改善您的代码。
但是,在正确的情况下,仅位置参数可以在设计函数时提供一定的灵活性。首先,当您的参数具有自然顺序但很难给其提供良好的描述性名称时,仅位置参数才有意义。
使用仅位置参数的另一个可能的好处是,您可以更轻松地重构函数。特别是,您可以更改参数的名称,而不必担心其他代码取决于这些名称。
仅位置参数很好地补充了仅关键字参数。在任何版本的 Python 3 中,都可以使用星号(*)指定仅关键字的参数。任何参数后 *,必须使用关键字来指定:
>>> def to_fahrenheit(*, celsius):... return 32 + celsius * 9 / 5...>>> to_fahrenheit(40)Traceback (most recent call last):File "<stdin>", line 1, in <module>TypeError: to_fahrenheit() takes 0 positional arguments but 1 was given>>> to_fahrenheit(celsius=40)104.0
复制代码
celsius 是仅关键字的参数,因此,如果您尝试根据不含关键字的位置进行指定,Python 会引发错误。
您可以通过按/和分隔此顺序的顺序来组合仅位置,常规和仅关键字的参数*。在以下示例中,text是仅位置参数,border是具有默认值的常规参数,并且width是具有默认值的仅关键字参数:
>>> def headline(text, /, border="♦", *, width=50):... return f" {text} ".center(width, border)...
复制代码
由于text仅位置定位,因此您不能使用关键字text:
>>> headline("Positional-only Arguments")'♦♦♦♦♦♦♦♦♦♦♦ Positional-only Arguments ♦♦♦♦♦♦♦♦♦♦♦♦'>>> headline(text="This doesn't work!")Traceback (most recent call last):File "<stdin>", line 1, in <module>TypeError: headline() got some positional-only arguments passed askeyword arguments: 'text'
复制代码
border另一方面,既可以指定关键字,也可以不指定关键字:
>>> headline("Python 3.8", "=")'=================== Python 3.8 ==================='>>> headline("Real Python", border=":")':::::::::::::::::: Real Python :::::::::::::::::::'
复制代码
最后,width必须使用关键字指定:
>>> headline("Python", "🐍", width=38)'🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍 Python 🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍'>>> headline("Python", "🐍", 38)Traceback (most recent call last):File "<stdin>", line 1, in <module>TypeError: headline() takes from 1 to 2 positional argumentsbut 3 were given
复制代码
您可以在 PEP 570 中阅读有关仅位置参数的更多信息。
更多精确类型
此时,Python 的键入系统已经相当成熟。但是,在 Python 3.8 中,添加了一些新功能typing以允许更精确的键入:
-
文字类型
-
打字字典
-
最终对象
-
通讯协定
Python 支持可选的类型提示,通常作为代码上的注释:
def double(number: float) -> float:return 2 * number
复制代码
在此示例中,您说number应为 a float,并且double()函数也应返回 a float。但是,Python 将这些注释视为 hints。它们不会在运行时强制执行:
>>> double(3.14)6.28>>> double("I'm not a float")"I'm not a floatI'm not a float"
复制代码
double()"I'm not a float"即使不是,也愉快地接受作为参数float。有些库可以在运行时使用类型,但这不是 Python 的类型系统的主要用例。
相反,类型提示允许静态类型检查器对 Python 代码进行类型检查,而无需实际运行脚本。这让人想起编译器捕获其他语言(如 Java,Rust 和 Crystal)的类型错误。此外,类型提示可作为代码的文档,使其更易于阅读,并改善 IDE 中的自动完成功能。
注意:有几种可用的静态类型检查器,包括 Pyright,Pytype 和 Pyre。在本文中,您将使用 Mypy。您可以使用以下方法从 PyPI 安装 Mypy pip:
$ python -m pip install mypy
复制代码
从某种意义上说,Mypy 是 Python 类型检查器的参考实现,并在 Jukka Lehtasalo 的领导下由 Dropbox 开发。Python 的创建者 Guido van Rossum 是 Mypy 团队的成员。
您可以在原始 PEP 484 和 Python 类型检查(指南)中找到有关类型提示的更多信息。
Python 3.8 已接受并包含了四个有关类型检查的新 PEP。您将看到其中每个的简短示例。
PEP 586 介绍了该Literal类型。Literal它有点特殊,因为它代表一个或多个特定值。一种使用情况Literal是,当使用字符串参数描述特定行为时,能够精确地添加类型。考虑以下示例:
# draw_line.pydef draw_line(direction: str) -> None:if direction == "horizontal":... # Draw horizontal lineelif direction == "vertical":... # Draw vertical lineelse:raise ValueError(f"invalid direction {direction!r}")draw_line("up")
复制代码
该程序将通过静态类型检查器,即使"up"方向无效。类型检查器仅检查"up"是字符串。在这种情况下,更精确地说direction必须是文字字符串"horizontal"或文字字符串"vertical"。使用Literal,您可以精确地做到这一点:
# draw_line.pyfrom typing import Literaldef draw_line(direction: Literal["horizontal", "vertical"]) -> None:if direction == "horizontal":... # Draw horizontal lineelif direction == "vertical":... # Draw vertical lineelse:raise ValueError(f"invalid direction {direction!r}")draw_line("up")
复制代码
通过将允许的值暴露direction给类型检查器,现在可以警告该错误:
$ mypy draw_line.pydraw_line.py:15: error:Argument 1 to "draw_line" has incompatible type "Literal['up']";expected "Union[Literal['horizontal'], Literal['vertical']]"Found 1 error in 1 file (checked 1 source file)
复制代码
基本语法为Literal[<literal>]。例如,Literal[38]表示文字值 38。您可以使用来表示多个文字值之一Union:
Union[Literal["horizontal"], Literal["vertical"]]
复制代码
由于这是一个相当普遍的用例,因此您可以(并且应该应该)使用更简单的表示法Literal["horizontal", "vertical"]。在向中添加类型时,您已经使用了后者draw_line()。如果您仔细查看上面 Mypy 的输出,您会发现它在Union内部将较简单的表示法转换为表示法。
在某些情况下,函数的返回值的类型取决于输入参数。一个示例open()可能根据的值返回文本字符串或字节数组mode。这可以通过重载来处理。
以下示例显示了计算器的骨架,该计算器可以将答案返回为正数(38)或罗马数字(XXXVIII):
# calculator.pyfrom typing import UnionARABIC_TO_ROMAN = [(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"),(100, "C"), (90, "XC"), (50, "L"), (40, "XL"),(10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")]def _convert_to_roman_numeral(number: int) -> str:"""Convert number to a roman numeral string"""result = list()for arabic, roman in ARABIC_TO_ROMAN:count, number = divmod(number, arabic)result.append(roman * count)return "".join(result)def add(num_1: int, num_2: int, to_roman: bool = True) -> Union[str, int]:"""Add two numbers"""result = num_1 + num_2if to_roman:return _convert_to_roman_numeral(result)else:return result
复制代码
该代码具有正确的类型提示:的结果add()将为str或int。然而,通常此代码将用文字称为True或False作为价值to_roman在这种情况下,你会喜欢的类型检查来推断是否准确str或int返回。可以和以下命令Literal一起使用@overload:
# calculator.pyfrom typing import Literal, overload, UnionARABIC_TO_ROMAN = [(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"),(100, "C"), (90, "XC"), (50, "L"), (40, "XL"),(10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")]def _convert_to_roman_numeral(number: int) -> str:"""Convert number to a roman numeral string"""result = list()for arabic, roman in ARABIC_TO_ROMAN:count, number = divmod(number, arabic)result.append(roman * count)return "".join(result)@overloaddef add(num_1: int, num_2: int, to_roman: Literal[True]) -> str: ...@overloaddef add(num_1: int, num_2: int, to_roman: Literal[False]) -> int: ...def add(num_1: int, num_2: int, to_roman: bool = True) -> Union[str, int]:"""Add two numbers"""result = num_1 + num_2if to_roman:return _convert_to_roman_numeral(result)else:return result
复制代码
添加的@overload签名将帮助您的类型检查器进行推断str或int根据的字面值to_roman。注意,省略号(...)是代码的文字部分。它们在重载签名中代表功能主体。
作为补充Literal,PEP 591 引入了Final。该限定符指定不应重新分配,重新定义或覆盖变量或属性。以下是输入错误:
from typing import FinalID: Final = 1...ID += 1
复制代码
Mypy 将突出显示该行ID += 1,并注意您Cannot assign to final name "ID"。这为您提供了一种确保代码中的常量永远不会更改其值的方法。
另外,还有一个@final装饰器可以应用于类和方法。装饰了的类@final不能被子类化,而@final方法不能被子类覆盖:
from typing import final@finalclass Base:...class Sub(Base):...
复制代码
Mypy 将使用错误消息标记此示例Cannot inherit from final class "Base"。要了解有关Final和的更多信息@final,请参阅 PEP 591。
第三 PEP 允许更多的特定类型的提示是 PEP 589,其引入TypedDict。可以使用类似于 typed 的符号来指定字典中键和值的类型NamedTuple。
传统上,字典使用注释Dict。问题在于,这仅允许键的一种类型和值的一种类型,通常导致诸如的注释Dict[str, Any]。例如,考虑一个字典,该字典注册有关 Python 版本的信息:
py38 = {"version": "3.8", "release_year": 2019}
复制代码
对应的值为version字符串,而release_year为整数。无法使用精确表示Dict。使用 new TypedDict,您可以执行以下操作:
from typing import TypedDictclass PythonVersion(TypedDict):version: strrelease_year: intpy38 = PythonVersion(version="3.8", release_year=2019)
复制代码
然后,类型检查器将能够推断出py38["version"]类型为str,而类型py38["release_year"]为int。在运行时,a TypedDict是常规的dict,照常忽略类型提示。您也可以TypedDict纯粹用作注释:
py38: PythonVersion = {"version": "3.8", "release_year": 2019}
复制代码
Mypy 会告知您任何值的类型错误,还是使用了未声明的键。有关更多示例,请参见 PEP 589。
Mypy 已经支持协议已有一段时间了。但是,官方验收仅在 2019 年 5 月发生。
协议是形式化 Python 对鸭子输入的支持的一种方式:
当我看到一只鸟走路像鸭子,游泳像鸭子,嘎嘎像鸭子一样时,我称那只鸟为鸭子。(来源)
鸭式打字允许您例如阅读.name具有.name属性的任何对象,而无需真正关心对象的类型。支持打字系统似乎违反直觉。通过结构子类型化,仍然有可能了解鸭子的类型。
例如,您可以定义一个协议Named,该协议可以识别具有.name属性的所有对象:
from typing import Protocolclass Named(Protocol):name: strdef greet(obj: Named) -> None:print(f"Hi {obj.name}")
复制代码
在这里,greet()只要定义了.name属性,就可以使用任何对象。有关协议的更多信息,请参见 PEP 544 和 Mypy 文档。
使用 f 字符串进行更简单的调试
f 字符串是在 Python 3.6 中引入的,已经非常流行。它们可能是仅在 3.6 版及更高版本上支持 Python 库的最常见原因。f 字符串是格式化的字符串文字。您可以通过以下方式识别它f:
>>> style = "formatted">>> f"This is a {style} string"'This is a formatted string'
复制代码
使用 f 字符串时,可以将变量甚至表达式括在花括号内。然后将在运行时对它们进行评估,并将其包含在字符串中。一个 f 字符串中可以包含多个表达式:
>>> import math>>> r = 3.6>>> f"A circle with radius {r} has area {math.pi * r * r:.2f}"'A circle with radius 3.6 has area 40.72'
复制代码
在最后一个表达式中{math.pi * r * r:.2f},您还使用了格式说明符。格式说明符与表达式之间用冒号分隔。
.2f表示该区域的格式为带有 2 个小数的浮点数。格式说明符与相同.format()。有关允许的格式说明符的完整列表,请参见官方文档。
在 Python 3.8 中,可以在 f 字符串中使用赋值表达式。只需确保用括号将赋值表达式括起来即可:
>>> import math>>> r = 3.8>>> f"Diameter {(diam := 2 * r)} gives circumference {math.pi * diam:.2f}"'Diameter 7.6 gives circumference 23.88'
复制代码
但是,Python 3.8 中真正的 f-news 是新的调试说明符。现在=,您可以在表达式的末尾添加,它将同时打印该表达式及其值:
>>> python = 3.8>>> f"{python=}"'python=3.8'
复制代码
这是个捷径,通常在交互式工作或添加打印语句来调试脚本时最有用。在早期版本的 Python 中,您需要对变量或表达式进行两次拼写才能获得相同的信息:
>>> python = 3.7>>> f"python={python}"'python=3.7'
复制代码
您可以在周围添加空格=,并照常使用格式说明符:
>>> name = "Eric">>> f"{name = }""name = 'Eric'">>> f"{name = :>10}"'name = Eric'
复制代码
:>10格式说明称,name10 字符串中应右对齐。=同样适用于更复杂的表达式:
>>> f"{name.upper()[::-1] = }""name.upper()[::-1] = 'CIRE'"
复制代码
有关 f 字符串的更多信息,请参见 Python 3 的 f 字符串:改进的字符串格式语法(指南)。
Python 指导委员会
从技术上讲,Python 的治理不是语言功能。然而,Python 的 3.8 是 Python 的第一个版本下没有发展仁慈的独裁的吉多·范罗苏姆。Python 语言现在由一个由五个核心开发人员组成的指导委员会管理:
-
巴里华沙
-
布雷特·坎农
-
卡罗尔·威林
-
吉多·范·罗苏姆(Guido van Rossum)
-
尼克·科格兰
通往 Python 新治理模型的道路是自组织方面的有趣研究。吉多·范·罗苏姆(Guido van Rossum)在 1990 年代初期创立了 Python,并被亲切地称为 Python 的仁慈生命独裁者(BDFL)。多年来,通过 Python 增强提案(PEP)做出了关于 Python 语言的越来越多的决定。尽管如此,Guido 仍在所有新语言功能上都拥有最终决定权。
在对赋值表达式进行了漫长而漫长的讨论之后,Guido 在 2018 年 7 月宣布他将退出 BDFL 职位(这次是真实的)。他故意没有指定继任者。相反,他要求核心开发人员团队弄清楚今后应该如何管理 Python。
幸运的是,PEP 流程已经很完善,因此使用 PEP 讨论并决定新的治理模型是很自然的。到 2018 年秋季,人们提出了几种模式,包括选举新的 BDFL(更名为审判官重大影响决策官:GUIDO),或在没有集中领导的情况下转向基于共识和投票的社区模式。2018 年 12 月,在核心开发人员投票后选择了指导委员会模型。
指导委员会由上面列出的 Python 社区的五个成员组成。在每个主要的 Python 版本发布之后,将选举一个新的指导委员会。换句话说,Python 3.8 发行后将进行选举。
尽管这是一次公开选举,但可以预计,即使不是全部,大多数首届指导委员会也将连任。指导委员会具有决定 Python 语言的广泛权力,但应努力尽可能少地行使这些权力。
您可以在 PEP 13 中阅读有关新治理模型的所有信息,而 PEP 8000 中介绍了决定新模型的过程。有关更多信息,请参阅 PyCon 2019 主题演讲,并在 Talk Python To Me 和 Changelog 播客上收听 Brett Cannon 。您可以在 GitHub 上关注指导委员会的更新。
其他很酷的功能
到目前为止,您已经看到有关 Python 3.8 新增功能的头条新闻。但是,还有许多其他变化也很酷。在本节中,您将快速了解其中的一些。
importlib.metadata
Python 3.8 的标准库中提供了一个新模块:importlib.metadata。通过此模块,您可以访问有关 Python 安装中已安装软件包的信息。连同其配套模块一起importlib.resources,importlib.metadata改进了旧版的功能pkg_resources。
例如,您可以获得有关pip以下信息:
>>> from importlib import metadata>>> metadata.version("pip")'19.2.3'>>> pip_metadata = metadata.metadata("pip")>>> list(pip_metadata)['Metadata-Version', 'Name', 'Version', 'Summary', 'Home-page', 'Author','Author-email', 'License', 'Keywords', 'Platform', 'Classifier','Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier','Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier','Classifier', 'Classifier', 'Requires-Python']>>> pip_metadata["Home-page"]'https://pip.pypa.io/'>>> pip_metadata["Requires-Python"]'>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*'>>> len(metadata.files("pip"))668
复制代码
当前安装的版本pip是 19.2.3。metadata()可以访问您可以在 PyPI 上看到的大多数信息。例如,您可以看到此版本的pip需要 Python 2.7 或 Python 3.5 或更高版本。使用files(),您将获得构成pip程序包的所有文件的清单。在这种情况下,几乎有 700 个文件。
files()返回Path对象列表。这些使您可以使用方便的方式查看软件包的源代码read_text()。以下示例__init__.py从realpython-reader包装中打印出:
>>> [p for p in metadata.files("realpython-reader") if p.suffix == ".py"][PackagePath('reader/__init__.py'), PackagePath('reader/__main__.py'),PackagePath('reader/feed.py'), PackagePath('reader/viewer.py')]>>> init_path = _[0] # Underscore access last returned value in the REPL>>> print(init_path.read_text())"""Real Python feed readerImport the `feed` module to work with the Real Python feed:>>> from reader import feed>>> feed.get_titles()['Logging in Python', 'The Best Python Books', ...]See https://github.com/realpython/reader/ for more information"""# Version of realpython-reader package__version__ = "1.0.0"...
复制代码
您还可以访问包依赖关系:
>>> metadata.requires("realpython-reader")['feedparser', 'html2text', 'importlib-resources', 'typing']
复制代码
requires()列出软件包的依赖关系。您可以看到,realpython-reader例如,它feedparser在后台用于阅读和解析文章提要。
importlib.metadata PyPI 上有一个可用的反向端口,可在早期版本的 Python 上运行。您可以使用安装它pip:
$ python -m pip install importlib-metadata
复制代码
try:from importlib import metadataexcept ImportError:import importlib_metadata as metadata...
复制代码
有关更多信息,请参阅文档importlib.metadata
新的和改进的math和statistics函数
Python 3.8 对现有的标准库软件包和模块进行了许多改进。math在标准库中有一些新功能。math.prod()与内置相似sum(),但对于乘法:
>>> import math>>> math.prod((2, 8, 7, 7))784>>> 2 * 8 * 7 * 7784
复制代码
这两个语句是等效的。prod()当您已经将因子存储在迭代器中时,将更易于使用。
另一个新功能是math.isqrt()。您可以isqrt()用来查找平方根的整数部分:
>>> import math>>> math.isqrt(9)3>>> math.sqrt(9)3.0>>> math.isqrt(15)3>>> math.sqrt(15)3.872983346207417
复制代码
9 的平方根是 3。您可以看到它isqrt()返回整数结果,而math.sqrt()始终返回 a float。15 的平方根几乎是 3.9。请注意,将答案isqrt() 截断为下一个整数,在这种情况下为 3。
最后,您现在可以更轻松地使用标准库中的 n 维点和向量。您可以使用来找到两点之间的距离math.dist(),以及使用来找到向量的长度math.hypot():
>>> import math>>> point_1 = (16, 25, 20)>>> point_2 = (8, 15, 14)>>> math.dist(point_1, point_2)14.142135623730951>>> math.hypot(*point_1)35.79106033634656>>> math.hypot(*point_2)22.02271554554524
复制代码
这使得使用标准库更容易处理点和向量。但是,如果要对点或向量进行许多计算,则应签出 NumPy。
该statistics模块还具有几个新功能:
statistics.fmean()计算float数字的平均值。
statistics.geometric_mean()计算float数字的几何平均值。
statistics.multimode() 查找序列中最频繁出现的值。
statistics.quantiles()计算将数据以等概率分为 n 个连续区间的切点。
以下示例显示了正在使用的功能:
>>> import statistics>>> data = [9, 3, 2, 1, 1, 2, 7, 9]>>> statistics.fmean(data)4.25>>> statistics.geometric_mean(data)3.013668912157617>>> statistics.multimode(data)[9, 2, 1]>>> statistics.quantiles(data, n=4)[1.25, 2.5, 8.5]
复制代码
在 Python 3.8 中,有了一个新statistics.NormalDist类,可以更轻松地处理高斯正态分布。
若要查看使用的例子NormalDist,你可以尝试比较新的速度statistics.fmean()和传统statistics.mean():
>>> import random>>> import statistics>>> from timeit import timeit>>> # Create 10,000 random numbers>>> data = [random.random() for _ in range(10_000)]>>> # Measure the time it takes to run mean() and fmean()>>> t_mean = [timeit("statistics.mean(data)", number=100, globals=globals())... for _ in range(30)]>>> t_fmean = [timeit("statistics.fmean(data)", number=100, globals=globals())... for _ in range(30)]>>> # Create NormalDist objects based on the sampled timings>>> n_mean = statistics.NormalDist.from_samples(t_mean)>>> n_fmean = statistics.NormalDist.from_samples(t_fmean)>>> # Look at sample mean and standard deviation>>> n_mean.mean, n_mean.stdev(0.825690647733245, 0.07788573997674526)>>> n_fmean.mean, n_fmean.stdev(0.010488564966666065, 0.0008572332785645231)>>> # Calculate the lower 1 percentile of mean>>> n_mean.quantiles(n=100)[0]0.6445013221202459
复制代码
在这个例子中,你使用timeit来衡量的执行时间mean()和fmean()。为了获得可靠的结果,您可以让timeit每个功能执行 100 次,并为每个功能收集 30 个这样的时间样本。基于这些示例,您将创建两个NormalDist对象。请注意,如果您自己运行代码,则可能需要一分钟的时间来收集不同的时间样本。
NormalDist具有许多方便的属性和方法。请参阅文档以获取完整列表。检查.mean和.stdev,您会看到旧版本的statistics.mean()运行时间为 0.826±0.078 秒,而新版本的运行statistics.fmean()时间为 0.0105±0.0009 秒。换句话说,fmean()这些数据的速度要快 80 倍左右。
如果您需要使用 Python 而不是标准库提供的高级统计信息,请查看statsmodels和scipy.stats。
关于危险语法的警告
Python 的SyntaxWarning可以警告可疑的语法,通常不是SyntaxError。Python 3.8 添加了一些新功能,可以在编码和调试过程中为您提供帮助。
is和之间的区别==可能会造成混淆。为相等的值,后者检查,而is是True仅当对象是相同的。Python 3.8 会警告您有关应使用==而不是的情况is:
>>> # Python 3.7>>> version = "3.7">>> version is "3.7"False>>> # Python 3.8>>> version = "3.8">>> version is "3.8"<stdin>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?False>>> version == "3.8"True
复制代码
写长列表时,尤其是垂直格式化时,很容易错过逗号。忘记元组列表中的逗号将给出有关元组不可调用的混乱错误消息。Python 3.8 还会发出警告,指出实际问题:
>>> [... (1, 3)... (2, 4)... ]<stdin>:2: SyntaxWarning: 'tuple' object is not callable; perhapsyou missed a comma?Traceback (most recent call last):File "<stdin>", line 2, in <module>TypeError: 'tuple' object is not callable
复制代码
该警告正确地将丢失的逗号标识为真正的罪魁祸首。
最佳化
Python 3.8 进行了一些优化。一些使代码运行更快。其他减少了内存占用。例如,namedtuple与 Python 3.7 相比,在 Python 3.8 中查找 a 中的字段要快得多:
>>> import collections>>> from timeit import timeit>>> Person = collections.namedtuple("Person", "name twitter")>>> raymond = Person("Raymond", "@raymondh")>>> # Python 3.7>>> timeit("raymond.twitter", globals=globals())0.05876131607996285>>> # Python 3.8>>> timeit("raymond.twitter", globals=globals())0.0377705999400132
复制代码
你可以看到,查找.twitter在namedtuple30-40%,在 Python 3.8 快。从具有已知长度的可迭代对象初始化列表时,可以节省一些空间。这样可以节省内存:
>>> import sys>>> # Python 3.7>>> sys.getsizeof(list(range(20191014)))181719232>>> # Python 3.8>>> sys.getsizeof(list(range(20191014)))161528168
复制代码
在这种情况下,与 3.7 相比,该列表在 Python 3.8 中使用的内存减少了约 11%。
其他优化包括的更高性能subprocess,更快的文件复制shutil,更高的默认性能pickle以及更快的operator.itemgetter操作。有关优化的完整列表,请参见官方文档。
那么,您是否应该升级到 Python 3.8?
让我们从简单的答案开始。如果您想尝试这里看到的任何新功能,那么您确实需要能够使用 Python 3.8。像pyenv和 Anaconda 这样的工具可以很容易地并排安装多个版本的 Python。或者,您可以运行官方的 Python 3.8 Docker 容器。自己尝试使用 Python 3.8 没有任何弊端。
现在,对于更复杂的问题。您是否应该将生产环境升级到 Python 3.8?您是否应该使自己的项目依赖于 Python 3.8 来利用这些新功能?
在 Python 3.8 中运行 Python 3.7 代码的问题应该很少。因此,升级环境以运行 Python 3.8 是非常安全的,并且您将能够利用新版本中进行的优化。Python 3.8 的不同 beta 版本已经可用了几个月,因此希望可以解决大多数错误。但是,如果您想保守一点,可以坚持到第一个维护版本(Python 3.8.1)可用。
升级环境后,就可以开始尝试仅在 Python 3.8 中使用的功能,例如赋值表达式和仅位置参数。但是,您应该注意其他人是否依赖您的代码,因为这也会迫使他们也升级他们的环境。流行的库可能至少会在更长的一段时间内至少支持 Python 3.6。
有关为 Python 3.8 准备代码的更多信息,请参见移植到 Python 3.8
小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏+评论转发+关注我,后面会有很多干货。
我有一些面试题、架构、设计类资料可以说是程序员面试必备!所有资料都整理到网盘了,需要的话欢迎下载!私信我回复【07】即可免费获取

原文出处:xie.infoq.cn/article/d487e1b923dd3599bbc0a71b2
Python 3.8 引入了赋值表达式(海象运算符),仅位置参数,更精确的类型提示,以及使用 f 字符串进行调试等新功能。赋值表达式简化了循环中的变量初始化和更新,仅位置参数提供了更清晰的函数参数定义,类型提示增强提高了代码可读性和静态类型检查,而 f 字符串的调试功能则简化了调试过程。此外,Python 3.8 还对性能进行了优化并引入了新的治理模式。开发者可以根据需求考虑升级到 Python 3.8,享受新功能带来的便利。
Python 3.8 的超酷新功能(接近一万字,请耐心享用,而且建议收藏)&spm=1001.2101.3001.5002&articleId=126878753&d=1&t=3&u=4cfd7b7e1f834d8a8d1c65c3750118be)

被折叠的 条评论
为什么被折叠?



