解包是指透過疊代,從集合(例如list、tuple或dict)中取出元素的動作。
解包後的值接著可以在同一道敘述中賦值給變數。
這種行為最常見的例子是for item in list,其中item會在整個疊代過程中依序取得每個list元素的值。
多重賦值是指在單一敘述中,把多個變數賦值給解包後的值的能力。
這能讓程式碼更精簡、更好讀,做法是用逗號分隔要賦值的變數,例如first, second, third = (1,2,3)或for index, item in enumerate(iterable)。
特殊運算子*和**經常用於解包的場合。
*可以用來把多個list/tuple合併成一個list/tuple,做法是把每一個都_解包_到新的共同list/tuple裡。
**可以用來把多個字典合併成一個字典,做法是把每一個都_解包_到新的共同dict裡。
當*運算子沒有搭配集合使用時,它會把若干個值_打包_成一個list。
這在多重賦值中很常用,用來把所有沒有個別賦值的「剩餘」元素集中到單一變數裡。
在 Python 中,使用或定義可接受任意數量位置引數或關鍵字引數的函式時,也很常利用這種解包/打包行為。
你經常會看到這些「特殊」參數定義成def some_function(*args, **kwargs),而這些「特殊」引數則這樣使用:some_function(*some_tuple, **some_dict)。
*<variable_name>和**<variable_name>不應與*和**搞混。*和**分別用於乘法和次方,而*<variable_name>和**<variable_name>則是打包與解包運算子。
在多重賦值中,賦值運算子(=)左邊的變數數量必須與右邊的值的數量相符。
若要分隔各個值,請使用逗號,:
>>> a, b = 1, 2
>>> a
1
如果多重賦值拿到的變數數量與給定的值不符,就會拋出ValueError:
>>> x, y, z = 1, 2
ValueError: too many values to unpack (expected 3, got 2)
多重賦值不限於單一資料型態:
>>> x, y, z = 1, "Hello", True
>>> x
1
>>> y
'Hello'
>>> z
True
多重賦值可以用來交換lists中的元素。
這個做法在排序演算法中相當常見。
例如:
>>> numbers = [1, 2]
>>> numbers[0], numbers[1] = numbers[1], numbers[0]
>>> numbers
[2, 1]
由於tuples是不可變的,你無法交換tuple中的元素。
下面的範例使用lists,但同樣的概念也適用於tuples。
在 Python 中,可以把list/tuple/dict的元素解包到不同的變數裡。
由於值在lists/tuples中是以特定順序出現的,它們會依相同順序解包到變數中:
>>> fruits = ["apple", "banana", "cherry"]
>>> x, y, z = fruits
>>> x
"apple"
如果有不需要的值,可以用_標記:
>>> fruits = ["apple", "banana", "cherry"]
>>> _, _, z = fruits
>>> z
"cherry"
從list或tuple內部的list/tuple(也就是巢狀 list/tuple)解包並賦值,運作方式和淺層解包相同,但通常需要加上限定詞,才能釐清值的上下文或位置:
>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [c, d]] = fruits_vegetables
>>> a
"apple"
>>> d
"potato"
你也可以只深層解包巢狀list/tuple的其中一部分:
>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [a, [c, d]] = fruits_vegetables
>>> a
["apple", "banana"]
>>> c
"carrot"
如果解包時的變數位置不正確,或是值的數量不對,就會得到ValueError:
>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [d]] = fruits_vegetables
ValueError: too many values to unpack (expected 1)
*解包 list/tuple解包list/tuple時,你可以用*運算子捕捉「剩餘」的值。
這比對list/tuple切片(在某些情況下較不易讀)更清楚。
例如,我們可以取出第一個元素,並把其餘的值打包成一個不含第一個元素的新list:
>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *last = fruits
>>> x
"apple"
>>> last
["banana", "cherry", "orange", "kiwi", "melon", "mango"]
我們也可以取出list開頭和結尾的值,同時把中間的所有值集中在一起:
>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *middle, y, z = fruits
>>> y
"melon"
>>> middle
["banana", "cherry", "orange", "kiwi"]
我們也可以在深層解包中使用*:
>>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>>> [[a, *rest], b] = fruits_vegetables
>>> a
"apple"
>>> rest
["banana", "melon"]
解包字典和[解包list/tuple]有點不同。
對字典疊代時,預設會疊代鍵。
所以在解包dict時,你只能解包鍵,而不能解包值:
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory
>>> x
"apple"
如果你想解包值,可以使用<dict>.values()方法:
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.values()
>>> x
6
如果鍵和值都需要,請使用<dict>.items()方法。
<dict>.items()會產生一個包含鍵-值配對的可疊代檢視。
這些配對可以解包成tuple:
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.items()
>>> x
("apple", 6)
打包是指把多個值集中到一個list並賦值給變數的能力。
當你想_解包_值、做些修改,然後再把結果_打包_回一個變數時,這就很有用。
它也讓你能對 2 個以上的lists/tuples/dicts進行合併。
*打包 list/tuple使用*運算子可以打包list/tuple。
這會把所有值打包成一個list/tuple。
>>> fruits = ("apple", "banana", "cherry")
>>> more_fruits = ["orange", "kiwi", "melon", "mango"]
# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
>>> combined_fruits = *fruits, *more_fruits
# If there is no * on to the left of the "=" the result is a tuple
>>> combined_fruits
("apple", "banana", "cherry", "orange", "kiwi", "melon", "mango")
# If the * operator is used on the left side of "=" the result is a list.
# Note the trailing comma.
>>> *combined_fruits_too, = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']
# A list literal can be used instead, but might not be as readable.
>>> [*combined_fruits_too] = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']
關於*和**用法的更多細節,請參考PEP 3132和PEP 448。
**打包字典打包字典是使用**運算子來完成。
這會把一個字典中所有的鍵-值配對打包到另一個字典,或把兩個字典合併在一起。
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}
# then the pairs are packed into combined_fruits_inventory
>>> combined_fruits_inventory
{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
*和**
當你建立一個可接受任意數量引數的函式時,可以在函式定義中使用*args或**kwargs。
*args用來打包任意數量的位置(非關鍵字)引數,
**kwargs則用來打包任意數量的關鍵字引數。
*args的用法:
# This function is defined to take any number of positional arguments
>>> def my_function(*args):
... print(args)
# Arguments given to the function are packed into a tuple
>>> my_function(1, 2, 3)
(1, 2, 3)
>>> my_function("Hello")
("Hello")
>>> my_function(1, 2, 3, "Hello", "Mars")
(1, 2, 3, "Hello", "Mars")
**kwargs的用法:
# This function is defined to take any number of keyword arguments
>>> def my_function(**kwargs):
... print(kwargs)
# Arguments given to the function are packed into a dictionary
>>> my_function(a=1, b=2, c=3)
{"a": 1, "b": 2, "c": 3}
*args和**kwargs也可以互相搭配使用:
>>> def my_function(*args, **kwargs):
... print(sum(args))
... for key, value in kwargs.items():
... print(str(key) + " = " + str(value))
>>> my_function(1, 2, 3, a=1, b=2, c=3)
6
a = 1
b = 2
c = 3
你也可以在*args之前寫入參數,以允許特定的位置引數。
個別的關鍵字引數則必須出現在**kwargs之前。
def my_function(<positional_args>, *args, <key-word_args>, **kwargs)
如果沒有依照這個順序,就會發生錯誤。
>>> def my_function(a, b, *args):
... print(a)
... print(b)
... print(args)
>>> my_function(1, 2, 3, 4, 5)
1
2
(3, 4, 5)
引數順序寫錯會導致錯誤:
>>> def my_function(*args, a, b):
... print(args)
>>>my_function(1, 2, 3, 4, 5)
Traceback (most recent call last):
File "c:\something.py", line 3, in <module>
my_function(1, 2, 3, 4, 5)
TypeError: my_function() missing 2 required keyword-only arguments: 'a' and 'b'
你可以用*把一個list/tuple的引數解包到函式呼叫中。
這對於不接受iterable的函式非常有用:
>>> def my_function(a, b, c):
... print(c)
... print(b)
... print(a)
numbers = [1, 2, 3]
>>> my_function(*numbers)
3
2
1
把*解包搭配內建的zip()是另一個常見的用途。
zip()函式接受多個可疊代物件,並回傳由tuples組成的list,其中每個iterable的值會分組在一起:
>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
>>> a, *rest = zip(*values)
>>> rest
[('y', 2, False), ('z', 3, True)]