自從好久之前改用了python3,語言層面的特徵變化就相對較小了,然而對於每個版本,Python都會添加一些新函數。
隨着Python3.8於2019年10月發佈,我發現本身使用的是這種語言的一些特性,關於Python每一個版本新增的特性,有以下python
from typing import List def print_cats(cats: List[str]) -> None: for cat in cats: print(f"{cat} has a name with {len(cat)} letters.") class Cat(object): def __init__(self, name: str, age: int, **attrs): self.cattributes = { "name": name, "age": age, **attrs } cats = "this still works w/o type annotation!" cats: List[str] = ["Meowie", "Fluffy", "Deathspawn"] # 不是字符串列表,Python不會檢查 cats2: List[str] = [Cat("Meowie", 2), Cat("Deathspawn", 8)] print_cats(cats) print_cats(cats2) # 失敗
這將返回:正則表達式
Meowie has a name with 6 letters. Fluffy has a name with 6 letters. Deathspawn has a name with 10 letters. -------------------------------------------- ... TypeError: object of type 'Cat' has no len()
類型註解在這裏並無起到任何做用,那爲何要使用它們呢?由於在建立變量cats並用List[str]時,很明顯分配的數據應該與該結構相匹配,所以對於具備複雜類型的可維護代碼來講,這將變得更加有用。編程
from typing import List class Cat(object): def __init__(self, name: str, age: int, **attrs): self.cattributes = { "name": name, "age": age, **attrs } # 建立類型變量 Cats: type = List[Cat] def print_cats(cats: Cats) -> None: for cat in cats: name: str = cat.cattributes.get("name") print(f"{name} has a name with {len(name)} letters.") cats = [Cat("Meowie", 2), Cat("Deathspawn", 8)] print_cats(cats)
輸出:異步
Meowie has a name with 6 letters. Deathspawn has a name with 10 letters.
在函數/方法定義中鍵入參數稱爲類型暗示,並且類型甚至沒必要是Python數據類型或來自typing模塊。例如最後一行提示性字符串是徹底合法的:ide
import pandas as pd cols = ["name", "age", "gender"] data = [["Meowie", 2, "female"], ["Fluffy", 5, "male"], ["Deathspawn", 8, "rather not say"]] df: pd.DataFrame = pd.DataFrame() df: "name (string), age (integer), gender (string)" = \ pd.DataFrame(data, columns=cols)
在數據處理管道中,若是有不少複雜類型的變量,那這樣的操做可能會頗有用,由於你可能搞不清楚讀取的數據是什麼結構,你會試圖把它們弄清楚。在IDE上鼠標懸停在變量上會有類型提示的信息,而不是一個簡單的pandas.DataFrame提示。
額外的好處是:在python4中,前向引用能夠開箱即用,這意味着你能夠對還沒有定義的類型進行註解。咱們如今仍然能夠利用這種優點,在文件頂部編寫from future import annotations,而後執行如下操做:函數
from __future__ import annotations class Food: """ Food是合法的,即便沒有類別的定義。 """ def __init__(self, ingred_1: Food, ingred_2: Food) -> None: self.ingred_1 = ingred_1 self.ingred_2 = ingred_2
原生類型註解-3.9
內置泛型類型是3.9中的一個特性,咱們不須要從typing中導入以向泛型數據類型添加參數。從3.7版開始,使用from futures import annotations就可使用這種方法,但這是由於它阻止了在運行時計算類型引用。
這個功能讓我很興奮。在3.8中我將typing導入每一個模塊,或者導入在公共模塊中。
示例(信貸:PEP 585):this
>>> l = list[str]() [] >>> list is list[str] False >>> list == list[str] False >>> list[str] == list[str] True >>> list[str] == list[int] False >>> isinstance([1, 2, 3], list[str]) TypeError: isinstance() arg 2 cannot be a parameterized generic >>> issubclass(list, list[str]) TypeError: issubclass() arg 2 cannot be a parameterized generic >>> isinstance(list[str], types.GenericAlias) True def find(haystack: dict[str, list[int]]) -> int: ...
海象算子-3.8
海象有眼睛:,而後有牙齒=。
:=是Python3.8中新增的賦值表達式。spa
complicated = { "data": { "list": [1,2,3], "other": "stuff" } } if (nums := complicated.get('data').get('list')): print(nums)
結果:code
1 2 3
若是沒有海象,會有更多的代碼行。blog
... nums = complicated.get('data').get('list') if nums: print(nums)
因爲控制流語句在編程中常用,使用海象算子能夠簡化代碼。
來自PEP 572:
這樣的命名錶達式的值與合併表達式的值的結果是相同的,但附加的做用是目標被賦給了該值
換言之,用一個表達式表達了兩個語句。
在我複製/粘貼PEP指南的同時,這裏還有一些規範中的示例,我認爲它們是很好的示例。火燒眉毛地想嘗試一下海象算子來理解列表。
# #處理匹配正則表達式 if (match := pattern.search(data)) is not None: # 匹配後... # 一個更直觀易寫的循環 while chunk := file.read(8192): process(chunk) # 重用一個計算成本很高的值 [y := f(x), y**2, y**3] # 在理解filter語句及其輸出之間共享子表達式 filtered_data = [y for x in data if (y := f(x)) is not None]
結論
最近對Python語言的添加提供了一些至關不錯的特性以供實踐。我但願你以爲typing和海象算子對你的編程是有用。
參考連接:https://towardsdatascience.com/whats-new-in-python-2020-part-1-c101939c8800