(译)用多重赋值和元组解包提高python代码的可读性
原文鏈接(侵刪): http://treyhunner.com/2018/03/tuple-unpacking-improves-python-code-readability/
無論我是教python新手還是老手,經常發現他們沒有很好的使用多重賦值。
多重賦值允許你在一行代碼中同時分配多個值。也許你在了解它之后會覺得也不過如此嘛,但你要記住,多重賦值有可能會很棘手。
這篇文章就來詳細的介紹下多重賦值。
多重賦值如何工作
在本文中,當我說到多重賦值,元組解包,迭代解包的時候,指的都是同一件事情。
python中的多重賦值看起來像這樣:
x, y = 10, 20 復制代碼這里我們把x設為10,y設為20。
上面的代碼實際上是創建了一個包含10,20的元組,然后循環遍歷該元組,并從循環中獲取兩個items中的每一個,然后分別分配給x和y。
像這樣:
(x, y) = (10, 20) 復制代碼在元組中使用括號是可選的,多重賦值也一樣,下面這幾行代碼都是等價的:
x, y = 10, 20 x, y = (10, 20) (x, y) = 10, 20 (x, y) = (10, 20) 復制代碼多元賦值通常被稱為“元組拆包”,是因為它經常與元組一起使用。但是我們也可以對任何迭代使用多個賦值,而不僅僅是元組。比如使用列表:
x, y = [10, 20] x 10 y 20 復制代碼使用string:
x, y = 'hi' x 'h' y 'i' 復制代碼任何可以循環的東西都可以通過元組解包/多重賦值來“解壓縮”。
接下來說說如何使用多重賦值。
在循環中解壓縮
你經常會在for循環中看到解壓縮。
這里有一個dict:
person_dictionary = {'name': "Trey", 'company': "Truthful Technology LLC"} 復制代碼通常我們不會對dict這樣使用for循環:
for item in person_dictionary.items():print(f"Key {item[0]} has value {item[1]}") 復制代碼而是像下面這樣,使用多重賦值:
for key, value in person_dictionary.items():print(f"Key {key} has value {value}") 復制代碼實際上,等同于把item賦值給key,value一樣:
for item in person_dictionary.items():key, value = itemprint(f"Key {key} has value {value}") 復制代碼所以多重賦值非常適合把一個字典拆分成鍵值對的形式,這一用法在其他地方也很常見。
比如使用enumerate的時候:
for i, line in enumerate(my_file):print(f"Line {i}: {line}") 復制代碼同樣的,在使用zip的時候:
for color, ratio in zip(colors, ratios):print(f"It's {ratio*100}% {color}.") 復制代碼多任務雖然適用于任何分配,但不僅僅是循環分配。
代替硬編碼索引
我們經常會在代碼中看到硬編碼索引(像point[0], items[1]這樣):
print(f"The first item is {items[0]} and the last item is {items[-1]}") 復制代碼我們可以用多重賦值來代替硬編碼索引,這樣能讓代碼的可讀性更高。
比如,下面是一段有三個硬編碼索引的代碼:
def reformat_date(mdy_date_string):"""Reformat MM/DD/YYYY string into YYYY-MM-DD string."""date = mdy_date_string.split('/')return f"{date[2]}-{date[0]}-{date[1]}" 復制代碼通過使用多重賦值讓這段代碼可讀性更高一些:
def reformat_date(mdy_date_string):"""Reformat MM/DD/YYYY string into YYYY-MM-DD string."""month, day, year = mdy_date_string.split('/')return f"{year}-{month}-{day}" 復制代碼所以當你在代碼中見到硬編碼索引時,停下來想想是否可以用多重賦值使得代碼更可讀。
多重賦值是很嚴格的
在解包我們給它的迭代時,多重賦值實際上相當嚴格。
如果試圖將更大的迭代器拆分成更少的變量,會得到一個錯誤:
x, y = (10, 20, 30) Traceback (most recent call last):File "<stdin>", line 1, in <module> ValueError: too many values to unpack (expected 2) 復制代碼同樣的,如果試圖將一個較小的迭代器拆分成更多的變量,也會得到一個錯誤:
x, y, z = (10, 20) Traceback (most recent call last):File "<stdin>", line 1, in <module> ValueError: not enough values to unpack (expected 3, got 2) 復制代碼這種嚴格的規定相當好,如果當前正在處理的item與我們預期的不同,可以及時的發現并修改。
切片的代替品
多重賦值也可以用來代替切片中的硬編碼。
我們都知道,切片可以方便的獲取列表或者其它序列中的部分元素。
像下面的代碼就是硬編碼的方式;
all_after_first = items[1:] all_but_last_two = items[:-2] items_with_ends_removed = items[1:-1] 復制代碼任何時候當你看到切片索引中使用硬編碼的方式,你都可以使用多重賦值來代替。要做到這一點,我們需要用到* 操作符。
在python3中,* 操作符被加入到多重賦值的語法中,它允許我們在解包列表時使用* 代替剩余的部分,比如:
numbers = [1, 2, 3, 4, 5, 6] first, *rest = numbers rest [2, 3, 4, 5, 6] first 1 復制代碼* 操作符允許我們替換列表中的硬編碼,下面的兩行代碼是等價的:
beginning, last = numbers[:-1], numbers[-1] *beginning, last = numbers 復制代碼同樣的:
head, middle, tail = numbers[0], numbers[1:-1], numbers[-1] head, *middle, tail = numbers 復制代碼深入拆包
深入拆包是python程序員經常忽視的一個特性,雖然使用的不是很頻繁,但是了解了之后將很有用。
對于下面的代碼來說,并不算是深入拆包:
color, point = ("red", (1, 2, 3)) color 'red' point (1, 2, 3) 復制代碼而下面的代碼就可以這樣說了,因為它將point元組進一步分為x,y,z三個變量:
color, (x, y, z) = ("red", (1, 2, 3)) color 'red' x 1 y 2 復制代碼如果對上面的代碼你感到困惑,它其實跟下面的代碼是一樣的,在兩邊加上括號即可:
(color, (x, y, z)) = ("red", (1, 2, 3)) 復制代碼首先通過第一層拆包得到兩個對象,然后再將第二個對象拆包分配給x, y, z三個變量。
考慮下面兩個列表:
start_points = [(1, 2), (3, 4), (5, 6)] end_points = [(-1, -2), (-3, 4), (-6, -5)] 復制代碼下面的代碼使用淺層拆包對列表進行操作:
for start, end in zip(start_points, end_points):if start[0] == -end[0] and start[1] == -end[1]:print(f"Point {start[0]},{start[1]} was negated.") 復制代碼我們用深層拆包可以做同樣的事:
for (x1, y1), (x2, y2) in zip(start_points, end_points):if x1 == -x2 and y1 == -y2:print(f"Point {x1},{y1} was negated.") 復制代碼使用深層拆包能夠更清晰的看到正在處理的對象,另外,深層拆包的過程同樣嚴格。
多重賦值可以提高代碼的可讀性和代碼的正確性。它可以使您的代碼更具描述性,同時還可以對您要解包的迭代對象的大小和形狀進行隱式斷言。
多重賦值一個最常見的用途是替換硬編碼索引和硬編碼的列表。
轉載于:https://juejin.im/post/5aace6746fb9a028b61749dc
總結
以上是生活随笔為你收集整理的(译)用多重赋值和元组解包提高python代码的可读性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Developer Tools
- 下一篇: Docker技术笔记:Docker入门浅