最新總結(jié):2021那些小眾精巧的 Python 語法匯總

2020 年 python2 停止維護(hù),公司代碼規(guī)范也鼓勵使用 python3.6+版本,而隨著 Python 版本的不斷更新,許多舊的語法在可讀性與效率上都已經(jīng)有更好的替代了舰讹。當(dāng)然,大部分的重要特性独撇,例如裝飾器吨些、生成器、async 等,相信大家都非常熟悉了,這里就面向一些使用率稍微少一些夷野、日常所見代碼中不太常見的能用得上的語法做一個匯總,僅供參考。

日常的自用 Python 腳本沒有太大的工程壓力嚎莉,能緊跟更新步伐、嘗試新的特性沛豌。但是語法糖用的好就是效率提升趋箩,用的不好就是可讀性災(zāi)難,有些語法的出現(xiàn)也伴隨著種種的爭議加派,用更新的語法不代表就能寫出更好的代碼叫确。

翻看語言的更新日志確實(shí)蠻有意思

通過語法的更新變化還有變化帶來的爭議,也能窺透語言的設(shè)計哲學(xué)芍锦、匯聚濃縮在一個特定點(diǎn)上的社區(qū)開發(fā)經(jīng)驗(yàn)竹勉。選擇合適自己的、保持對代碼精簡可讀的追求才是最重要醉旦。

那么就從老到新饶米,理一理那些有意思的小 feature 吧〕岛可能有漏掉有趣的點(diǎn)檬输、也可能有解釋不到位的地方,歡迎各位大佬更正補(bǔ)充匈棘。

Python 3.0-3.6

PEP 3132 可迭代對象解包拓展

Python3.0 引入丧慈,加強(qiáng)了原本的星號運(yùn)算符(*),讓星號運(yùn)算符能夠智能地展開可迭代對象。

>>> a, *b, c = range(5)
>>> a
0
>>> c
4
>>> b
[1, 2, 3]

隱式賦值也同樣適用

>>> for a, *b in [(1, 2, 3), (4, 5, 6, 7)]:
>>>     print(b)
[2, 3]
[5, 6, 7]

注意雙星號(**)不能用相同語法展開字典

人畜無害逃默,用處也不大的一個 feature

PEP 465 矩陣乘法運(yùn)算符

Python3.5 引入鹃愤,顧名思義,使用@符號完域。直接支持 numpy软吐、pandas 等使用。

>>> a = numpy.array([1, 2, 3])
>>> b = numpy.array([10, 20, 30])
>>> a @ b
140

>>> c = numpy.array([[10, 15], [20, 25], [30, 35]])
>>> d = numpy.array([[4, 5, 6], [7, 8, 9]])
>>> c @ d
array([[145, 170, 195],
       [255, 300, 345],
       [365, 430, 495]])

矩陣乘法運(yùn)算符的魔術(shù)方法為__matmul__()吟税、__rmatmul__()凹耙、__imatmul__()三個

本身用處不大,但是提供了一個額外的操作符使用空間肠仪,可以用來重載來進(jìn)行類似距離計算之類的用途肖抱。

>>> from math import sqrt

>>> class Point:
>>>     def __init__(self, x, y):
>>>         self.x = x
>>>         self.y = y
>>>
>>>     def __matmul__(self, value):
>>>         x_sub = self.x - value.x
>>>         y_sub = self.y - value.y
>>>         return sqrt(x_sub**2 + y_sub**2)
>>>
>>> a = Point(1, 3)
>>> b = Point(4, 7)
>>> print(a @ b)
5

爭議主要存在于:作為矩陣乘法來說@操作符沒有直觀聯(lián)系、影響可讀性异旧,不如直接使用 matmul

PEP 3107/484/526 函數(shù)注解/類型提示/變量注解

Python3.0 引入函數(shù)注解意述、3.5 引入 typing,讓 python 也能享受靜態(tài)類型的福利吮蛹』绯纾可以說是 py3 中個人最喜歡的 feature,使用簡單匹涮、效果強(qiáng)大天试,直接讓開發(fā)效率以及代碼可維護(hù)性直線增長。

# 參數(shù)后加:即可標(biāo)注類型然低,函數(shù)結(jié)構(gòu)定義后接->即可標(biāo)注返回類型
def get_hello(name: str) -> str:
    return f"Hello, {name}!"

如上進(jìn)行標(biāo)記之后 IDE 便能自動讀取參數(shù)喜每、返回類型,直接出聯(lián)想爽快如 java雳攘。

而 PEP 484 Typing 則是極大的擴(kuò)充了類型定義語法带兜,支持別名、泛型吨灭、Callable刚照、Union 等等。非常推薦直接閱讀 PEP喧兄。

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

下面就是一個泛型的例子

from typing import TypeVar, Iterable, Tuple

T = TypeVar('T', int, float, complex)
Vector = Iterable[Tuple[T, T]]

def inproduct(v: Vector[T]) -> T:
    return sum(x*y for x, y in v)

def dilate(v: Vector[T], scale: T) -> Vector[T]:
    return ((x * scale, y * scale) for x, y in v)

vec = []  # type: Vector[float]

隨后在 3.6 引入了眾望所歸的變量注解(PEP 526)无畔,使用也很簡單,直接在變量后添加冒號和類型即可吠冤,搭配函數(shù)注解一起食用體驗(yàn)極佳

pi: float = 3.142

# 也同樣支持Union等
from typing import Union

a: Union[float,None] =1.0

3.7 中又引入了延遲標(biāo)記求值(PEP 563)浑彰,讓 typing 支持了前向引用、并減輕了標(biāo)注對程序啟動時間的影響拯辙,如虎添翼郭变。

# 3.7前合法
class Tree:
    def __init__(self, left: 'Tree', right: 'Tree'):
        self.left = left
        self.right = right

# 3.7前不合法颜价、3.7后合法
class Tree:
    def __init__(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

更多的 python 類型檢查示例代碼:

https://github.com/realpython/materials/tree/master/python-type-checking

靜態(tài)類型檢查對 Python 所帶來的副作用主要還是啟動時間上的影響,當(dāng)然大部分場景所帶來的便利是遠(yuǎn)大于這一副作用的诉濒。

PEP 498 f-string

Python3.6 引入周伦,應(yīng)該是用的最多的 feature 之一了,但是看到很多代碼里面還是 str.format未荒,就不得不再提一下专挪。

>>> a = 10
>>> #只需要簡單的在任意字符串字面量前加個f,就可以用花括號直接引用變量
>>> print(f"a = {a}")
a = 10

>>> # 格式化也很方便片排,使用:即可
>>> pi = 3.14159
>>> print(f"pi = {pi: .2f}")
pi = 3.14

也可以在表達(dá)式后接!s 或者!r 來選擇用 str()還是 repr()方法轉(zhuǎn)換為字符串狈蚤。

基本就是 str.format 的語法糖。在 3.8 版本以后划纽,又增加了直接套表達(dá)式的功能,輸出信息非常方便锌畸。

>>> theta = 30
>>> print(f'{theta=}  {cos(radians(theta))=:.3f}')
theta=30  cos(radians(theta))=0.866

PEP 515 數(shù)值字面值下劃線

Python3.6 引入勇劣。輸入太長的數(shù)字字面值怎么辦?

>>> a = 123_456_789
>>> b = 123456789
>>> a == b
True

比較雞肋…

Python 3.7

PEP 557 數(shù)據(jù)類 Data Classes

提供了一個方便的 dataclass 類裝飾器潭枣,直接上代碼舉例:

from dataclasses import dataclass

@dataclass
class InventoryItem:
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

對這個例子比默,這個類會自動生成以下魔術(shù)方法

def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0) -> None:
    self.name = name
    self.unit_price = unit_price
    self.quantity_on_hand = quantity_on_hand
def __repr__(self):
    return f'InventoryItem(name={self.name!r}, unit_price={self.unit_price!r}, quantity_on_hand={self.quantity_on_hand!r})'
def __eq__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) == (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def __ne__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) != (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def __lt__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) < (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def __le__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) <= (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def __gt__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) > (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def __ge__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) >= (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented

這一條 PEP 也是比較有爭議的,主要原因是 Python 其實(shí)已經(jīng)內(nèi)置了不少的類似模型:collection.namedtuple盆犁、typing.NamedTuple命咐、attrs

但是這條 PEP 的提出還是為了保證方便地創(chuàng)建資料類的同時,保證靜態(tài)類型檢查谐岁,而已有的方案都不方便直接使用檢查器醋奠。

Python 3.8

PEP 572 海象牙運(yùn)算符

"逼走"了 Guido van Rossum,最有爭議的 PEP 之一伊佃。首先引入了海象牙運(yùn)算符:=窜司,代表行內(nèi)賦值。

# Before
while True:
    command = input("> ");
    if command == "quit":
        break
    print("You entered:", command)

# After
while (command := input("> ")) != "quit":
    print("You entered:", command)

assignment expressions 在進(jìn)行分支判斷時非常好用航揉,寫的時候能夠舒服很多塞祈。本身使用也集中在 if/while 這種場景,雖然讓語法變復(fù)雜了帅涂,但是總體還是可控的情龄,舒適程度大于風(fēng)險沧奴。

海象運(yùn)算符本身問題不大,但是爭議主要存在于 PEP 572 的第二點(diǎn),對于生成器語義的變化沉填。

在 PEP 572 后,生成器的in后的運(yùn)算順序產(chǎn)生了變化辫继,原本是作為生成器輸入腔召,結(jié)果現(xiàn)在變成了生成器閉包的一部分。

temp_list = ["abc","bcd"]
result_list = (x for x in range(len(temp_list)))
print(list(result_list))

# 等價于
# Before
temp_list = ["abc", "bcd"]


def func_data(data: int):
    for x in range(data):
        yield x


result_list = func_data(len(temp_list))
print(list(result_list))

# After
temp_list = ["abc", "bcd"]


def func_data():
    for x in range(len(temp_list)):
        yield x


result_list = func_data()
print(list(result_list))

這樣的修改目的是配合海象牙運(yùn)算符增加代碼可讀性,但無疑是帶破壞性的修改艇搀,且讓運(yùn)行順序變得迷惑尿扯,讓一些老代碼出現(xiàn)難以發(fā)現(xiàn)的 bug。

python 社區(qū)在激烈辯論后焰雕,這一部分的修改被成功撤銷衷笋,只保留了海象牙運(yùn)算符。

PEP 570 僅限位置形參

在函數(shù)形參處新增一個/語法矩屁,劃分非關(guān)鍵字與關(guān)鍵字形參辟宗。例如

def f(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)

# 以下調(diào)用均合法
f(10, 20, 30, d=40, e=50, f=60)

# 以下調(diào)用均不合法
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

/語法的添加讓調(diào)用函數(shù)時可以在可讀性與簡潔之間自由選擇,可以選擇強(qiáng)制不接受關(guān)鍵字參數(shù)吝秕、不需要形參名稱時也可以省略泊脐。同時也讓接受任意參數(shù)函數(shù)的實(shí)現(xiàn)變得方便了許多,例如:

class Counter(dict):
    def __init__(self, iterable=None, /, **kwds):
        # Note "iterable" is a possible keyword argument

這條本來也有其他方案烁峭,例如裝飾器實(shí)現(xiàn)容客、def fn(.arg1, .arg2, arg3):def fn(a, (b, c), d):等约郁,這里就不一一展開了缩挑,推薦閱讀 PEP 原文。

Python 3.9

PEP 584 字典合并運(yùn)算符

在此之前鬓梅,要想合并兩個字典的畫風(fēng)是這樣的

a={'a':1,'b':2}
b={'c':3}

a.update(b)

# 或者是
c = {**a, **b}

但自從有了|之后供置,可以變成這樣

a |= b
c = a | b

當(dāng)然這個操作符也伴隨著一些爭議,大概是這樣:

反方:合并不符合交換律 正方:python 字典合并本身就不符合交換律绽快,特別是 python3.6 之后統(tǒng)一到有序字典后芥丧,相比合并應(yīng)該更類似于拼接

反方:類似管道寫法進(jìn)行多次合并效率低,反復(fù)創(chuàng)建和銷毀臨時映射 正方:這種問題在序列級聯(lián)時同樣會出現(xiàn)谎僻。如果真出現(xiàn)了合并大量字典的使用場景娄柳,應(yīng)當(dāng)直接顯式循環(huán)合并

反方:|操作符容易和位運(yùn)算混淆。運(yùn)算符行為強(qiáng)依賴于變量種類艘绍,這在 python 是非常不利于可讀性的 正方:確實(shí)有這個問題赤拒,但是|已經(jīng)很混亂了(位運(yùn)算、集合操作诱鞠、__or__()魔術(shù)方法重載)挎挖,所以還是先規(guī)范變量命名吧

即將到來的 Python 3.10

PEP 617 / bpo-12782 括號內(nèi)的上下文管理

這一條是針對with語法(PEP 343)的小變動,讓一個with可以管理多個上下文航夺。使用也很簡單

with (CtxManager() as example):
    ...

with (
    CtxManager1(),
    CtxManager2()
):
    ...

with (CtxManager1() as example,
      CtxManager2()):
    ...

with (CtxManager1(),
      CtxManager2() as example):
    ...

with (
    CtxManager1() as example1,
    CtxManager2() as example2
):
    ...

比較實(shí)用蕉朵,避免了 with 下面接 with 產(chǎn)生不必要縮進(jìn)的尷尬。值得注意的是阳掐,這一條語法變動是新的非 LL(1)文法 CPython PEG 解析器所帶來的副產(chǎn)物始衅。所以 PEP 617 的標(biāo)題是New PEG parser for CPython冷蚂。

PEP 634 結(jié)構(gòu)化模式匹配 match-case

直接上結(jié)構(gòu):

match subject:
    case <pattern_1>:
        <action_1>
    case <pattern_2>:
        <action_2>
    case <pattern_3>:
        <action_3>
    case _:
        <action_wildcard>

是不是感覺熟悉又臭名昭著的 switch-case 終于來了?當(dāng)然還是有區(qū)別的:

這個寫法基本還是 if-elif-else 的語法糖汛闸,運(yùn)行完 case 就自動 break 出來蝙茶。再加上一些看著不錯的模式匹配特性。

def http_error(status):
    match status:
        case 400:
            return "Bad request"
        case 401 | 403 | 404:
            return "Not allowed"
        case 404:
            return "Not found"
        case 418:
            return "I'm a teapot"
        case _:
            return "Something's wrong with the Internet"

這樣的寫法看著就比 if-elif-else 看著清爽了許多诸老。針對元組隆夯、類、列表也有不錯的支持:

# point is an (x, y) tuple
match point:
    case (0, 0):
        print("Origin")
    case (0, y):
        print(f"Y={y}")
    case (x, 0):
        print(f"X={x}")
    case (x, y):
        print(f"X={x}, Y={y}")
    case _:
        raise ValueError("Not a point")

結(jié)語

Python語言的發(fā)展是由技術(shù)方面的進(jìn)步别伏、工程方面的剛需匯聚而成的智慧結(jié)晶蹄衷,身在其中便能體會到來自碼農(nóng)們創(chuàng)造出的代碼設(shè)計之巧思、學(xué)問厘肮。只有盡可能多去理解各類語法意義愧口,才能讓開發(fā)變得流暢;了解語法的構(gòu)成與其爭議类茂,在計算機(jī)科學(xué)領(lǐng)域的視野才會豁然開朗调卑。與時俱進(jìn)才是真正的好碼神~這篇文章就到這里,如果對你有幫助的話不妨點(diǎn)贊大咱、收藏、轉(zhuǎn)發(fā)一下注益,歡迎在評論區(qū)交流以及提出寶貴意見碴巾,更多的Python實(shí)戰(zhàn)技巧,學(xué)習(xí)資料可以私信與我交流丑搔,我會盡我所能的提供幫助!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末厦瓢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子啤月,更是在濱河造成了極大的恐慌煮仇,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谎仲,死亡現(xiàn)場離奇詭異浙垫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)郑诺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門夹姥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人辙诞,你說我怎么就攤上這事辙售。” “怎么了飞涂?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵旦部,是天一觀的道長祈搜。 經(jīng)常有香客問我,道長士八,這世上最難降的妖魔是什么容燕? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮曹铃,結(jié)果婚禮上缰趋,老公的妹妹穿的比我還像新娘。我一直安慰自己陕见,他們只是感情好秘血,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著评甜,像睡著了一般灰粮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上忍坷,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天粘舟,我揣著相機(jī)與錄音,去河邊找鬼佩研。 笑死柑肴,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的旬薯。 我是一名探鬼主播晰骑,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绊序!你這毒婦竟也來了硕舆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤骤公,失蹤者是張志新(化名)和其女友劉穎抚官,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阶捆,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凌节,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了洒试。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刊咳。...
    茶點(diǎn)故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖儡司,靈堂內(nèi)的尸體忽然破棺而出娱挨,到底是詐尸還是另有隱情,我是刑警寧澤捕犬,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布跷坝,位于F島的核電站酵镜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏柴钻。R本人自食惡果不足惜淮韭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贴届。 院中可真熱鬧靠粪,春花似錦、人聲如沸毫蚓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽元潘。三九已至畔乙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間翩概,已是汗流浹背牲距。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钥庇,地道東北人牍鞠。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像评姨,于是被迫代替她去往敵國和親皮服。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容