自从好久之前改用了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