如何提高Python代碼的可讀性?| 例談多重賦值和元組解包

2021-03-02 Python那些事

(點擊上方公眾號,可快速關注一起學Python)

來源:MLGroup    連結:

http://mp.weixin.qq.com/s/71Zbq9b8RM2WpRDVvtidNQ

原文連結: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 = item
   print(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.")

使用深層拆包能夠更清晰的看到正在處理的對象,另外,深層拆包的過程同樣嚴格。

多重賦值可以提高代碼的可讀性和代碼的正確性。它可以使您的代碼更具描述性,同時還可以對您要解包的迭代對象的大小和形狀進行隱式斷言。

多重賦值一個最常見的用途是替換硬編碼索引和硬編碼的列表。

(完)

看完本文有收穫?請轉發分享給更多人

關注「Python那些事」,做全棧開發工程師

相關焦點

  • 讓你的代碼更「地道」:提高Python編碼水平的小技巧
    雖然程式設計師依然可以通過不同的方式實現同樣的功能,編寫出優秀的代碼,只要代碼能夠滿足預期目的就OK。編寫非慣用Python程序也沒有問題。但就像我們不斷練習英文的口音一樣,也有一些人也想讓自己的Python代碼變得更地道。本文中,我將分享自己在過去幾年中積累的一些習慣用法,希望對提高你的Python編碼水平有所幫助。
  • Python學習筆記:列表、元組和字典
    第3章 列表、元組和字典python內置的三種常見數據結構:列表(list)、元組(tuple)和字典(dict)。這三種數據結構都可用於保存多個數據項。列表和元組比較相似,它們都按順序保存元素,每個元素都有自己的索引,因此列表和元組都可通過索引訪問元素。二者的區別在於列表是可修改的,元組不可修改。字典則以key-value的形式保存數據。
  • Python容器:列表與元組
    與字符串不同的是,元組和列表並不要求所有元素的種類相同,每個元素都可以是任何python類型的對象。列表可變,賦值後可插入或刪除其中的元素。元組不可變,賦值後不可更改。改變a不影響b、c和d的複製。; c'Harpo'有時候這個過程被稱為元組解包。
  • Python每天一分鐘:如何把元組/字典作為參數傳遞給函數(附代碼)
    python可變參數當然,Python 也不例外,那麼如何在定義函數時支持使用可變參數呢?我們今天就以元組和字典為例給大家詳細介紹如何給函數傳遞可變參數python函數的可變參數我們首先來了解下python中函數的形參與實參定義:
  • 12個Python小竅門,迅速提高代碼質量
    圖源:unsplash使用和學習Python的人不在少數,但是真正掌握了解這門語言的人並不多。你了解最新的Python功能和技術嗎?這12個小技巧能幫助你提高代碼質量。例如:7.在一行中分配多個變量可用逗號分隔變量和值,從而把多個值分配給各個變量:這在對序列(例如列表或元組)解構/解包時也同樣適用,並且是將序列的元素更巧妙地分配給各個變量,因為不需要使用循環或單獨索引序列中的每個元素。
  • python變量類型,列表和元組
    微信公眾號:學點啥玩點啥小白友好型python變量類型,列表和元組# -*- coding: utf-8 -*-"""Created on Mon Jan 25 12:25:55 2021@author: sd"""#第2章.變量和簡單數據類型#變量#1.字符串
  • 11-python中的元組
    通過前兩天的文章10-python中的字典我們學習了有關字典的知識,今天我們將學習一下python中的元組。(一)元組介紹    元組(),是python內置的數據結構之一,是一個不可變序列。    可變序列和不可變序列區分標準:        不可變序列沒有增刪改的操作        可變序列在對序列中的元素進行增刪改操作時,對象地址不發生改變代碼示例:lst = [11, 22, 33]print('增加前:', lst, id(lst))lst.append(
  • Python如何定義變量,不可變數據,數字、字符串、元組詳解
    每個變量在使用前都必須賦值,變量賦值以後該變量才會被創建。等號(=)用來給變量賦值。等號(=)左邊是一個變量名,等號(=)右邊是存儲在變量中的值。多個變量賦值相同:多個變量賦值不同:注意變量有幾個,右邊的值就要幾個,不要少,注意逗號隔開。
  • 解密Python中的args和kwargs
    本文中你會知道:本文假設你已經知道在Python中如何定義函數,以及如何使用列表和字典。向函數傳多個參數*args和**kwargs允許向函數傳多個參數。記住,使用符號*標記參數後,所得到的可迭代對象不是列表而是元組。元組類似於列表,因為它們都支持切片和迭代。然而,元組在至少一個方面是非常不同的:列表是可變的,而元組不是可變的。要對此進行測試,請運行以下代碼。
  • Python 入門系列 —— 3. 代碼縮進和注釋
    C:\Users\Your Name>python myfile.pyPython 縮進 縮進 指的是代碼行開頭處的空格,在其他程式語言中使用的 縮進 僅僅是為了提高可讀性,而在 python 中這個縮進卻是非常重要的,它決定了你的語法是否正確。
  • python代碼結構:使用if語句、while循環和for迭代,附詳細說明!
    如果在該長度下寫不完代碼,可以使用連接符\(反斜線)。把它放在一行的技術位置,python將其解釋為同一行。3.使用if、elif和else進行比較兩個等號(==)是用來判斷相等的,而一個等號(=)是把某個值賦給一個變量。
  • python運算符總結
    本文章基於版本python3,老版本中,不等於可以用 <> 來表示,python3已經移除了該運算符。python賦值運算符python和大部分語言一樣,用一個等於號來表示賦值。賦值運算符賦值運算符可以和算術運算符相結合,形成一個複合運算符,該運算符的含義是先將變量和運算符右項進行指定的算術運算,再將運算結果賦值給變量。
  • 怎麼從細節中提高python代碼質量?今天我們來聊聊中間變量
    因此對於表達式 x, y = y, x,其 在內存中執行的順序如下:1) 先計算右邊的表達式y,x,因此先在內存中創建元組(y,x),其標示符和值分別為y、 x及其對應的值,其中y和x是在初始化時已經存在於內存中的對象。
  • 一文看懂Python列表、元組和字符串操作
    賦值引用a = {1:[1,2,3]}b = aprint(id(a) == id(b))輸出:True賦值引用,a 和 b 都指向同一個對象。對於a[1][0]和b[1][0]還是對於對象1的引用,並沒有重新新建一個對象,這符合python的存儲機制。
  • 詳解 Python 的 enumerate 函數
    你應該在何時何地,如何使用內置的 enumerate() 函數來寫出更加簡潔、更加具有 Python 範兒的循環結構呢?這樣一來既提高了你的代碼可讀性,也減少了拼寫錯誤的可能。改變初始索引值enumerate() 函數的另一個有用的特性就是它可以改變初始的索引值。 此函數接受一個可選擇的預設參數,這個參數可以用來設置索引計數器的初始值。
  • Python 為什麼只需一條語句「a,b=b,a」,就能直接交換兩個變量?
    最顯而易見的例子就是多重賦值,即在一條語句中同時給多個變量賦值:>>> x, y = 1, 2 >>> print(x, y) # 結果:1 2在此例中,賦值操作符「=」號的右側的兩個數字會被存入到一個元組中
  • 你的代碼「balance」怎麼樣?找到簡潔性和可讀性的平衡點
    代碼越簡潔,可讀性越高且能夠長期存在。用Python編寫簡潔代碼的方法有很多,本文想與大家分享筆者在日常Python項目中發現的特別有用的五個技巧。1.列表、字典和集合推導式列表推導式是眾多Python程式設計師最喜歡的特徵之一。這是一種從迭代器創建列表的簡潔方法。
  • Python函數詳解
    ; func<function inner at 0x031AA270>>>>在上面的例子中,outer函數被調用時將會返回一個函數,並將返回的函數賦值給變量func。函數定義的執行會綁定當前本地命名空間中的函數名(可以將命名空間當作名字到值的一種映射,並且這種映射還可以嵌套,命名空間和範圍會在另一個教程中詳細介紹)到一個函數對象,該對象是一個對函數中可執行代碼的包裝器。這個函數對象包含了一個對當前全局命名空間的引用,而當前命名空間指該函數調用時所使用的全局命名空間。此外,函數定義不會執行函數體,只有在函數被調用時才會執行函數體。
  • Python能做的5件有趣的事情,你做過幾個?
    ,這樣使用就允許它不賦值)value = value or {}# 相當於value = value if value else {}2.and的用法python中x and y表示如果x是假,結果就是
  • Python列表和元組
    @Author :RunsenPython列表和元組總結什麼是列表和元組列表是動態的,長度大小不固定,可以隨意地增加、刪減或者改變而元組是靜態的,長度大小固定,無法增加刪減或者改變定義列表和函數```l = [1, 2, 'hello', 'world'] # 列表中同時含有