1. 程式人生 > 程式設計 >Python 3.8正式釋出重要新功能一覽

Python 3.8正式釋出重要新功能一覽

Python3.8有哪些你要關注的新內容?

Python3.8 都有哪些新功能,在文件手冊中,大家可以有一個概覽。這麼多新內容,哪些是大家最先要關注一下的呢?下面,營長就帶大家從深度和廣度兩方面,瞭解那些最大的變化,幫助大家快速上手 Python3.8.

新功能手冊:

https://docs.python.org/3.8/whatsnew/3.8.html

在本文中,你將瞭解到Python 3.8如何:

  • 使用賦值表示式簡化一些程式碼結構
  • 在你自己的函式中強制執行僅位置引數
  • 指定更精確的型別提示
  • 使用f字串進行更簡單的除錯

除了少數例外,Python 3.8對早期版本進行了許多小的改進。在本文結尾處,你將看到許多這些不太引人注意的更改,並討論了一些使Python 3.8比其先前版本更快的優化。最後,你還會獲得一些有關升級到新版本的建議。

一、賦值表示式(Assignment expressions)

引入賦值表示式,可以說是Python3.8 中最大的一個變化了。注意,現在已經用新的符號了(:=),形似海象側牙,也被稱為“海象運算子”。賦值表示式可以在統一表達式中賦值並返回值,比如下面的程式碼,執行給變數分配值,並列印這個值

>>> walrus = False 
>>> print(walrus) 
False
Python3.8中,可以使用 walrus 運算子將上面兩個語句合併為一句
>>> print(walrus := True) 
True

賦值表示式可以把 True 分配給 walrus,並直接 print 這個值。一定要有(:= ),不然表示式也是無法正常執行的,有了新的賦值表示式符號,不僅在構造上更簡便,有時也可以更清楚的傳達程式碼意圖。

比如,在while迴圈中,就體現了(:= )的優勢

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": 
    break 
  inputs.append(current)

這段程式碼與上面的程式碼是等效的,不過,如果使用賦值表示式,還可以再進一步簡化這段迴圈:

inputs = list() 
while (current := input("Write something: ")) != "quit": 
  inputs.append(current)

現在的程式碼雖然更簡化了,但是可讀性就變差了,所以,大家要使用賦值表示式的方法還需要結合自身進行判斷。
PEP572中描述了複製表示式的所有細節,大家可以深入閱讀。

https://www.python.org/dev/peps/pep-0572/ https://www.python.org/dev/peps/pep-0572/#examples

僅位置引數(Positional-Only Arguments)

內建函式 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 -- Notation For Positional-Only Parameters
https://www.python.org/dev/peps/pep-0457/ 

事實證明,雖然float() 呼叫了引數 x,但並不允許使用其名稱

>>> float(x="3.8") 
Traceback (most recent call last): 
 File "<stdin>",line 1,in <module> 
TypeError: float() takes no keyword arguments

使用 float() 時,只允許按位置指定引數,而不能使用關鍵字引數。Python3.8 之前,這類僅位置引數只適用於內建引數,在我們自己定義的函式中,沒有簡單的方法指定引數為僅位置引數。

>>> def incr(x): 
...   return x + 1 
... 
>>> incr(3.8) 
4.8 
 
>>> incr(x=3.8) 
4.8

上面這段程式碼使用了 *args,模擬了僅位置引數,但是不夠靈活,不易讀,而在 Python3.8 中,可以用 / 來表示必須通過僅位置引數之前的引數,可以重寫incr()接收位置引數:

>>> def incr(x,/): 
...   return x + 1 
... 
>>> incr(3.8) 
4.8 
 
>>> incr(x=3.8) 
Traceback (most recent call last): 
 File "<stdin>",in <module> 
TypeError: incr() got some positional-only arguments passed as 
      keyword 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>",in <module> 
TypeError: greet() got some positional-only arguments passed as 
      keyword arguments: 'name'

greet() 中,/ 放在 name 和 greeting 之間,表示 name 是僅位置引數,greeting 是可以通過位置或關鍵字傳遞的常規引數。

大家可能覺得僅位置引數的可讀性似乎並不好,但是使用後會發現,很多情況下,只有僅位置引數可以優化我們的程式碼。此外,使用僅位置函式還有一個好處,可以更輕鬆地重構函式,更改函式的名稱時,不必擔心給其他程式碼帶來的影響。僅位置函式還很好的補充了僅關鍵字引數,可以使用 * 指定僅關鍵字引數:

>>> def to_fahrenheit(*,celsius): 
...   return 32 + celsius * 9 / 5 
... 
>>> to_fahrenheit(40) 
Traceback (most recent call last): 
 File "<stdin>",in <module> 
TypeError: to_fahrenheit() takes 0 positional arguments but 1 was given 
 
>>> to_fahrenheit(celsius=40) 
104.0

上段程式碼中,celsius 是僅關鍵字引數。

還可以通過按 / 和分隔的順序組合僅位置、常規和僅關鍵字引數 *,例如下段程式碼中,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>",in <module> 
TypeError: headline() got some positional-only arguments passed as 
      keyword 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>",in <module> 
TypeError: headline() takes from 1 to 2 positional arguments 
      but 3 were given

更多詳細型別

此時,Python的型別系統已經相當成熟。但是,在Python 3.8中,鍵入中添加了一些新功能,以允許進行更精確的鍵入:

  • 文字型別
  • 打字字典
  • 最終物件
  • 協定

Python支援可選的型別提示,通常作為程式碼上的註釋:

def double(number: float) -> float: 
  return 2 * number

在此示例中,數字應該是浮點數,並且double()函式也應該返回浮點數。但是,Python將這些註釋視為提示。它們不會在執行時強制執行:

>>> double(3.14) 
6.28 
 
>>> double("I'm not a float") 
"I'm not a floatI'm not a float"

double()將“我不是浮點數”作為引數,即使那不是浮點數。有些庫可以在執行時使用型別,但這並不是Python型別系統的主要用例。

相反,型別提示允許靜態型別檢查器對Python程式碼進行型別檢查,而無需實際執行指令碼。這讓人想起Java,Rust和Crystal等其他語言會出現的編譯器捕獲型別錯誤。此外,型別提示可作為程式碼的文件,使其更易於閱讀,並改善了IDE中的自動完成功能。

注意:有幾種可用的靜態型別檢查器,包括Pyright,Pytype和Pyre。本文中使用Mypy。你可以使用pip從PyPI安裝Mypy:

從某種意義上說,Mypy是Python型別檢查器的參考實現,並在Jukka Lehtasalo的領導下由Dropbox開發。Python的建立者Guido van Rossum是Mypy團隊的成員。

你可以在原始PEP 484和Python型別檢查(指南)中找到有關型別提示的更多資訊。

PEP484
https://www.python.org/dev/peps/pep-0484/

Python 3.8已接受幷包含四個有關型別檢查的新PEP,每個都有簡短示例。

PEP 586引入了文字型別。文字型別有點特殊,它代表一個或多個特定值。文字型別的一種用例是,當使用字串引數描述特定行為時,能夠精確地新增型別。以下為示例:

# draw_line.py 
 
def draw_line(direction: str) -> None: 
  if direction == "horizontal": 
    ... # Draw horizontal line 
  elif direction == "vertical": 
    ... # Draw vertical line 
 
  else: 
    raise ValueError(f"invalid direction {direction!r}") 
draw_line("up")

該程式將通過靜態型別檢查器,即使“向上”是無效方向。型別檢查器僅檢查“ up”是否為字串。在這種情況下,更準確地說方向必須是文字字串“水平”或文字字串“垂直”。使用文字型別,你可以完全做到這一點:
因為可以將方向的允許值暴露給型別檢查器,你現在可以得到有關錯誤的警告:

$ mypy draw_line.py 
draw_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表示多個文字值之一:

由於這是一個相當普遍的用例,因此你可以(並且應該)使用更簡單的表示法Literal [“ horizontal”,“ vertical”]]。將型別新增到draw_line()時,你已經使用了後者。如果仔細檢視上面Mypy的輸出,你會發現它在內部將較簡單的表示法轉換為Union表示法。

在某些情況下,函式的返回值的型別取決於輸入引數。一個示例是open(),它可以根據mode的值返回文字字串或位元組陣列。這可以通過過載來處理。

以下示例表示計算器的流程,該計算器可以將答案返回為正數(38)或羅馬數字(XXXVIII):

# calculator.py 
from typing import Union 
 
 
ARABIC_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_2 
 
  if to_roman: 
    return _convert_to_roman_numeral(result) 
  else: 
     
        return result

該程式碼具有正確的型別提示:add()的結果將為str或int。但是,通常會以true或False作為to_roman的值來呼叫此程式碼,在這種情況下,你會希望型別檢查器準確推斷出是否返回str或int。這可以通過使用Literal和@overload來完成:

# calculator.py 
from typing import Literal,overload,Union 
ARABIC_TO_ROMAN = [(1000,"I")] 
def _convert_to_roman_numeral(number: int) -> str: 
  """Convert number to a roman numeral string""" 
  result = list() 
  for arabic,arabic) 
    result.append(roman * count) 
  return "".join(result) 
@overload 
def add(num_1: int,to_roman: Literal[True]) -> str: ... 
@overload 
def add(num_1: int,to_roman: Literal[False]) -> int: ... 
def add(num_1: int,int]: 
  """Add two numbers""" 
  result = num_1 + num_2 
  if to_roman: 
    return _convert_to_roman_numeral(result) 
  else: 
    return result

新增的@overload簽名將幫助你的型別檢查器根據to_roman的文字值來推斷str或int。請注意,省略號(...)是程式碼的文字部分。它們在過載簽名中代表功能主體。

作為對Literal的補充,PEP 591引入了Final。該限定符規定不應重新分配、重新定義或覆蓋變數或屬性。以下是輸入錯誤:

from typing import Final 
ID: Final = 1 
... 
ID += 1

Mypy將突出顯示行ID + = 1,並請注意你無法將其分配給最終名稱“ ID”。這可以確保程式碼中的常量值永遠不變。

此外,還有一個@final裝飾器,可以將其應用於類和方法。用@final裝飾的類不能被子類化,而@final方法不能被子類覆蓋:

from typing import final 
@final 
class Base: 
  ... 
class Sub(Base): 
  ..

Mypy將使用無法從最終類“ Base”繼承”來的錯誤訊息標記此示例。要了解有關Final和@final的更多資訊,請參閱PEP 591。

支援更具體型別提示的第三個PEP是PEP 589,它引入了TypedDict。可以使用類似於型別化NamedTuple的符號來指定dictionaries 中鍵和值的型別。

傳統上,dictionaries 是使用Dict註釋的。問題在於,這僅允許一種型別鍵和一種型別值,通常導致諸如Dict [str,Any]這樣的註釋。例如,一個註冊Python版本資訊的dictionaries :
與version對應的值是一個字串,而release_year是一個整數。這無法使用Dict精確表示。使用新的TypedDict,你可以執行以下操作:

from typing import TypedDict 
class PythonVersion(TypedDict): 
  version: str 
  release_year: int 
py38 = PythonVersion(version="3.8",release_year=2019

然後,型別檢查器將能夠推斷出py38 [“ version”]的型別為str,而py38 [“ release_year”]是一個int值。在執行時,TypedDict是常規dict,並且照常忽略型別提示。你也可以將TypedDict純粹用作註釋:

Mypy會告知你任何值的型別錯誤,或者你使用的是尚未宣告的鍵。更多示例請參見PEP 589。

Mypy已經支援協議已有一段時間了。但是,2019年5月才正式官方支援。

協議是一種規範Python對鴨子型別支援的方式:

當我看到一隻鳥走路像鴨子,游泳像鴨子,像鴨子一樣嘎嘎叫時,我把它稱為鴨子。

鴨式型別讓你可以,比如在具有.name屬性的任何物件上讀取.name,而無需真正關心物件的型別。支援型別系統似乎違反直覺。通過結構子型別轉化,仍然有可能瞭解鴨子的型別。

例如,你可以定義一個名為Named的協議,該協議可以標識具有.name屬性的所有物件:

from typing import Protocol 
class Named(Protocol): 
  name: str 
def greet(obj: Named) -> None: 
  print(f"Hi {obj.name}"

這裡,greet()可以接受任何物件,只要它定義了.name屬性即可。有關協議的更多資訊,請參見PEP 544和Mypy文件。

使用f字串進行更簡單的除錯

f字串是在Python 3.6中引入的,已經非常流行。它們可能是Python庫僅在3.6版及更高版本上受支援的最常見原因。f字串是格式化的字串文字。你可以通過前導f識別它:

>>> 
>>> style = "formatted" 
>>> f"This is a {style} string" 
'This is a formatted string'

使用f字串時,可以將變數甚至表示式括在花括號內。然後在執行時對它們進行評估,並將其包含在字串中。一個f字串中可以包含多個表示式:

在最後一個表示式{math.pi * r * r:.2f}中,還使用了格式說明符。格式說明符與表示式之間用冒號分隔。

.2f表示該區域被格式化為帶有2個小數的浮點數。格式說明符與.format()相同。有關支援的格式說明符完整列表,請參見官方文件。

官方文件

https://docs.python.org/3/library/string.html#format-specification-mini-language

在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'

你可以在=周圍新增空格,並照常使用格式說明符:

> 10的格式說明符表示名稱應在10個字串內右對齊。=也適用於更復雜的表示式:

>>> f"{name.upper()[::-1] = }" 
"name.upper()[::-1] = 'CIRE'"

指導委員會模式(The Python Steering Council)

從技術上講,Python的管理並不是一項語言功能。但是,Python 3.8是首個不是在Guido van Rossum的仁慈獨裁統治下開發的Python版本。Python語言現在由一個由五個核心開發人員組成的指導委員會管理:

  • Barry Warsaw
  • Brett Cannon
  • Carol Willing
  • Guido van Rossum
  • Nick Coghlan

通往Python新治理模型的道路是自組織方面的一次有趣的研究。吉多·範·羅蘇姆(Guido van Rossum)在1990年代初建立了Python,並被親切地稱為Python的仁慈獨裁者(BDFL)。多年來,Python增強建議書(PEP)越來越多地參與了關於Python語言的決策。儘管如此,Guido仍在所有新語言功能上都擁有最終決定權。

在對賦值表示式進行了漫長的討論之後,Guido在2018年7月宣佈退出BDFL職位(這次是真的)。他故意沒有指定繼任者。相反,他要求核心開發人員團隊弄清楚今後應該如何管理Python。

幸運的是,PEP流程已經很完善,因此使用PEP討論並決定新的治理模型順理成章。2018年秋季,PEP提出了幾種模式,包括選舉新的BDFL(更名為GUIDO),或者是放棄集中領導,轉向基於共識和投票的社群模式。2018年12月,核心開發人員投票選擇了指導委員會的模式。

PyCon 2019上的Python指導委員會。從左至右:Barry Warsaw,Brett Cannon,Carol Willing,Guido van Rossum和Nick Coghlan(圖片來源:Geir Arne Hjelle)

指導委員會由上圖中Python社群的五名成員組成。在每個主要的Python版本釋出之後,將選舉一個新的指導委員會。換句話說,Python 3.8發行後將進行一次選舉。

儘管這是一次公開選舉,但預計大多數(甚至全部)老一屆指導委員會的成員將再次當選。指導委員會具有決定寬泛的Python語言決定權力,但應儘可能少地行使這些權力。

你可以在PEP 13中閱讀有關新治理模式的全部資訊,在PEP 8000中可以看到確定新模式的過程。有關更多資訊,請參閱PyCon 2019主題演講,並在Brett Cannon和Talk Python To Me和Changelog播客,並在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','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()方便地檢視軟體包的原始碼。以下示例從realpython-reader包中打印出__init__.py:

>>> [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 reader 
Import 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']

require()列出軟體包的依賴關係。可以看到,例如realpython-reader在後臺使用feedparser來閱讀和解析文章提要。
PyPI上有一個importlib.metadata的反向埠,該埠在Python的早期版本上也可以用。可以使用pip安裝:

try: 
  from importlib import metadata 
except ImportError: 
  import importlib_metadata as metadata 
..

新增和改進的數學和統計功能

Python 3.8對現有的標準庫軟體包和模組進行了許多改進。標準庫中的數學有了一些新功能。math.prod()與內建sum()類似,但對於乘法乘積:

>>> import math 
>>> math.prod((2,8,7,7)) 
784 
>>> 2 * 8 * 7 * 7 
784

這兩個語句是等效的。當你把因素儲存在可迭代物件中時,prod()將更易於使用。

另一個新功能是math.isqrt()。可以使用isqrt()來找到平方根的整數部分:

9的平方根是3。你可以看到isqrt()返回整數結果,而math.sqrt()始終返回浮點數。15的平方根約等於3.9。請注意,本例中,isqrt()將答案截斷為下一個整數。

最後,現在你可以更輕鬆地使用標準庫中的n維點和向量。使用math.dist()找到兩點之間的距離,並通過math.hypot()找到向量的長度:

這使得使用標準庫更容易處理點和向量。但是,如果要對點或向量進行許多計算,則應簽出NumPy。

統計模組還具有幾個新功能:

  • statistics.fmean()計算浮點數的平均值。
  • statistics.geometric_mean()計算浮點數的幾何平均值。
  • statistics.multimode()查詢序列中最頻繁出現的值。
  • statistics.quantiles()計算用於將資料等概率分為n個連續區間的切點。

以下為使用這些功能的示例:


>>> import statistics 
>>> data = [9,3,2,1,9] 
>>> statistics.fmean(data) 
4.25 
>>> statistics.geometric_mean(data) 
3.013668912157617 
>>> statistics.multimode(data) 
[9,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)",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.64450132212

在此示例中,使用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有一個SyntaxWarning功能,可以警告不是SyntaxError的可疑語法。Python 3.8添加了一些新功能,可以在編碼和除錯過程中為你提供幫助。

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; perhaps 
      you missed a comma? 
Traceback (most recent call last): 
 File "<stdin>",line 2,in <module> 
TypeError: 'tuple' object is not callable

該警告正確地將丟失的逗號標識為真正的罪魁禍首。

優化

Python 3.8進行了一些優化,有的讓程式碼執行得更快,有的優化減少了記憶體佔用。例如,與Python 3.7相比,在Python 3.8中查詢命名元組中的欄位要快得多:

>>> 
>>> 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

可以看到,在Python 3.8中在namedtuple上查詢.twitter的速度提高了30-40%。從具有已知長度的可迭代物件初始化列表時,可以節省一些空間。這樣可以節省記憶體:

>>> import sys 
>>> # Python 3.7 
>>> sys.getsizeof(list(range(20191014))) 
181719232 
>>> # Python 3.8 
>>> sys.getsizeof(list(range(20191014))) 
161528168

本例中,該列表在Python 3.8中使用的記憶體比Python 3.7少了大約11%。

其他優化還包括子流程效能更高,帶有shutil的檔案複製速度更快,pickle中的預設效能提高以及operator.itemgetter操作更快。有關優化的完整列表,請參見官方文件。

所以,我們必須要更新到 Python3.8 嗎?

如果你想嚐鮮新功能,那是肯定要升級的。

實際產品的開發環境需要升級到 Python3.8 嗎?首先,如果在 Python3.8 中執行 3.7 版本程式碼,問題應該不會很大;Python3.8 的beta版本也試用幾個月了,也解決了不少問題,如果能升級到Python3.8,肯定也是安全的,還能在新版本中進行優化。

如果你想嘗試一下Python3.8,可以閱讀下的文件,以幫助更好的完成移植
https://docs.python.org/3.8/whatsnew/3.8.html#porting-to-python-3-8

還有不能遺漏官方文件:

https://www.python.org/downloads/release/python-380/

更多閱讀:

https://realpython.com/python38-new-features/ https://docs.python.org/3.8/whatsnew/3.8.html

總結

以上所述是小編給大家介紹的Python 3.8正式釋出重要新功能一覽,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回覆大家的。在此也非常感謝大家對我們網站的支援!
如果你覺得本文對你有幫助,歡迎轉載,煩請註明出處,謝謝!