Python 打包與解包
Packing / Unpacking 時 Asterisk 相關摘要整理
- 單一個 Asterisk: 用在 iterable object 身上
- 雙 Asterisks: 用在 dict object 身上
- 用在變數指定上: 不完全解包
- 用在引數上: 依情境解包成 tuple: iterable
- 用在引數上: 依情境解包成 dict: named-params
- 用函數參數上: 定義 VarArgs 時使用
- 註: Bare Asterisk 用在函數 signature 時: 限制後方的 args 必須使用 keyword arguments.
Python 打包與解包解說
- 把 tuple/list instance 解包:
- iterable: var args *: 集合元素個別指定變數。
- 把 dict instance 解包:
- dict: named-params **: dict instance 中的 attr 與 function parameter mapping。
- 把 tuple/list instance 解包:
VarArgs function definition hints:
- 可變數量參數函數定義 的一種,varargs 皆置於 signature 的最末端。
- var-arg params 定義在 key-word params 之前。
解包(開箱) Unpacking 定義
指的是:
將有序的可迭代資料集合拆解成一個一個的單一元素,並且將元素與變數作對應。
或是將dict 拆解成多個單一元素的集合,可用來當 named-arguments。這邊我會簡單稱解包的目的是: 快速地將變數賦值功能。
所以,開箱同常會伴隨變數指定出現。
打包(裝箱) Packing 定義
打包指的是:
函數定義 VarArgument parameter 時,傳入的多個連續同型別引數可以自動裝載到一個有序集合之中。
或是函數定義 VarArgument parameter 時,傳入的多個連續 key-variable pairs 可以自動裝載到一個 dict 之中。我的理解中打包的目的是: 簡化資料(引數)傳遞給函數的功能。 所以,通常打包會需要函數定義,也會伴隨函數呼叫出現。
進階: 搭配 Asterisk 的打包解包
Asterisk * 在打包解包過程中:
單一個 asterisk 用於: 把不是重點的元素統一收集到帶單一星號的變數身上。
連續兩個 asterisks 用於: 用於告知處理的是 dict 或是 key-value pairs。(區別 dict 與有序集合)
解包開箱 Unpacking
解包或稱為開箱 (Unpacking):
這邊指的是將一個集合或 可迭代物件的元素自動與變數建立關聯。 或是將一個 dict instance 當引數傳給 function 時,Function 依 dict attribute name 與 function signature name 自動配對功能。
Tuple / List 解包範例:
Positional unpacking: 依位置解包
[firstName, lastName] = ['Michael', 'Jackson']
[firstName, lastName] = ('Michael', 'Jackson')
firstName, lastName = 'Michael', 'Jackson'
print(firstName)
# Michael
print(lastName)
# Jackson
Sequence unpacking: 依順序解包
x, y, z = 'CAT'
print(x)
# C
print(y)
# A
print(z)
# T
Asterisk Operator: List/Tuple 引數解包
- 這邊可以將 Asterisk 視為一種 Arguments Unpacking 運算元。其運算為: 將 tuple/list 進行解包。
- 將一個有序集合轉換成 var argument function 所需的引數清單
- 第8行,中的 *terms,將原本的 tuple 轉成元素清單
def string_join(separatpr: str, *args: str):
ret = ""
for item in args:
ret += (item + separatpr)
return ret
terms = ('How', 'are', 'you', 'Tom', '?')
sentence = string_join(' ', *terms) # 以 asterisk 標記將 terms 解包後傳入,
print(sentence)
# How are you Tom ?
# 上方片段等同於下方範例
sentence2 = string_join(' ', 'How', 'are', 'you', 'Tom', '?')
print(sentence2)
# How are you Tom ?
Dict 解包範例:
- 我把它分兩類:
- dict 轉成 iterable,那便擁有 list 的 unpacking 能力。
- dict 轉成 named-parameters 所需 arguments。
Dict to tuples or dict_items
- 基本上就是轉換成其他 iterable,然後走基本 list / tuple 解包流程。
profile = {"name": "Totem", "age":50, 'gender': 'Male'}
name,age,gender = profile.items()
print(age)
# ('age', 50)
for item in profile.items():
print( type(item)) # <class 'tuple'>
# ('name', 'Totem')
# ('age', 50)
# ('gender', 'Male')
for k, v in profile.items(): # 搭配 tuple 解包
print( k, '=', v)
# name = Totem
# age = 50
# gender = Male
show = [print(k, '+', v) for k, v in profile.items()]
DiAsterisk Operator: Dict Unpacking to named-parameters
- 經由將 dict 的 key-value 轉換成 named-parameters 所需引數,因此可以是無序的。
profile = {"name": "Totem", "age": 15, 'gender': 'Male'}
def print_profile(name, gender, age):
print(f"{name} is {age} years old and he is {gender}")
print_profile(**profile)
# Totem is 15 years old and he is Male
DiAsterisk Operator: Dict Unpacking to ?
- 這類型 Dict 解包,目前找不到解釋。只能硬解釋成說是語法糖。目前只知道下方範例中的這種用法。
boy_scores = {"Totem":60, "Robert":70, "Ade":10 }
girl_scores = {"Winnie":65, "May":75, "Anna":85 }
scores ={**boy_scores, **girl_scores}
print(scores)
# {'Totem': 60, 'Robert': 70, 'Ade': 10, 'Winnie': 65, 'May': 75, 'Anna': 85}
迴圈 / Comprehension 解包範例:
- 迴圈本身就是最常見的解包,不用多說吧。
unpack tuples
couples = {("Totem","Winnie"), ("Robert","May"), ("Tony","Jina")}
for k, v in couples:
print( k, '\u2665', v)
# Robert ♥ May
# Tony ♥ Jina
# Totem ♥ Winnie
打包裝箱 Packing
Packing
用在定義函數處: 目的在 自動將不定數的引數打包到一個 list 或 dict 中後傳入方法中 。
用在變數賦值時(常伴隨解包一起發生): 解包時將非主要元素收集在一個集合中。
此時會以 asterisk 標示打包的變數。
所以 Packing 通常用在函數定義上。如果你得打包這個名稱不易理解,我換一個名詞來解釋。
Python 的 Packing 就是其他語言的 VarArgs (Variable Arguments)。
意思就是函數或方法在定義傳入參數時,若想傳入的數量不固定時該如何處理。
我們不可能無限制的 overload 函數。
另一個解法是將同型別引數放到集合中再統一傳入。但是就需要多一個組成集合的動作。
因此,各種語言便出現動態數量引數的傳入語法。
在 Python 中稱為打包 (packing),
也就是自動將不定數的引數 打包 到一個 list 或 dict 中後傳入。
Tuple 打包範例: variable arguments
通常打包伴隨函數定義與函數呼叫。
Iterable style: 其實可想成是 Java 中的 Variable Arguments 函數設計。
在 function 的 signature 最後 部分可以定義一個 不定數參數 。
不定數參數在接收引數時,會將單一個別的引數打包成一個 tuple 供函數使用。varArgs signature 定義方式為在參數前方加上 單一個 Asterisk 。
(其實也可以把 Asterisk 想成是一種 operator: 把引數封裝成 tuple 的運算元)注意: packing 相關參數必須定義在最末端。
VarArgs 打包範例
# 函數定義, *args 標記為可變動數量參數
def string_join(separator: str, *args: str):
print(type(args)) # <class 'tuple'>
ret = ""
for item in args:
ret += (item + separator)
return ret
# 函數呼叫時末端的剩餘參數會被打包成 tuple
print(string_join(" ", 'How', 'are', 'you', '?'))
# How are you ?
Dict 打包範例: key-vale pair arguments
又稱為 key word arguments packing。
Python 中有所謂的 Keyword Arguments 語法,
在引數 (argument) 指名參數 (parameter) 時可以不依參數定義順序 (signature) 來呼叫。
也就是說允許 key-value pair 的方式指定每個參數的引數值。
key word arguments packing: 便是將要傳入的引數打包成一個 dict。
key word arguments packing 與一般 named-args 差別主要在於,dict packing 時可以把名稱未成功配對的引數統一放到一個 dict 引數中。key word arguments signature 定義方式為在參數前方加上 兩個 Asterisks 。
(其實也可以把 DiAsterisk 想成是一種 operator: 把 named-args 封裝成 dict 的運算元)注意: dict packing 時可以把名稱未成功配對的引數統一放到一個 dict 引數中。
dict packing 時是無序的,可與 named-args 交互穿插。
Key Word Args 打包範例
# 除了 name 與 gender 外,其他引數統一打包到 kwargs 這個變數
def print_profile(name='nobody', gender='Male', **kwargs):
print(f'I\'m {name}, {gender}')
for fact in kwargs:
print(fact, ':', kwargs[fact])
# 使用時,不需考慮 named-args 引數順序。
print_profile(gender='Female', height='160',
addr='TW', tel='0955-555-555', name='Winnie')
# I'm Winnie, Female
# height : 160
# addr : TW
# tel : 0955-555-555
Bare Asterisk
Bare Asterisk 用在函數 signature 代表 限制 後方的 args 必須使用 keyword arguments
就單純是一種限制,未遵循會拋出錯誤
Bare Asterisk
def funcAsterisk(name, exam, *, score):
print( f'{name} only got {score} in {exam} exam.')
funcAsterisk("Totem", "math", score='60')
# Totem only got 60 in math exam.
funcAsterisk("Totem", "math", '60')
# TypeError: funcAsterisk() takes 2 positional arguments but 3 were given
Asterisk Underscore
Asterisk Underscore (star underscore): 又是一個怪咖。
會出現在變數指定上,也會出現在函數 signature 上。
因為同時相連出現兩個 operators。Asterisk:就是解包或打包意思。
underscore 起始的變數都是有意義的。
underscore _ 對 python VM/Interpreter 來說,
是用來 暫存最後一個 expression 的回傳值使用。
這邊只是暫借來放不重要的資訊,反正等一下就會被丟棄。
Asterisk Underscore: variable
先說明 underscore :
在此處也可想成是佔位符(placeholder),
也就是這個變數不重要,也不會去用它,所以懶得為他取名稱。
與 Asterisk 搭配使用意味著,將不完全解包的資料暫放在 underscore 變數上。
註: underscore 是一個變數,所以還是可以取值的。
a, _, b = (1, 2, 3)
print(a, b)
# 1 3
a, *_, b = (1,2,3,4,5,6,7,8)
print(a, b)
print(_)
# 1 8
# [2, 3, 4, 5, 6, 7]
Asterisk Underscore: signature
用來當作一撰寫 overloaded funcs 的簡化語法。
# Python 常數範例中:
# 下方寫法意味著 VarArgs signature,無論傳幾個 args 都會導向此函數。
# 禁用所有 setter
def __setattr__(self, *_):
pass
搭配 Asterisk 的打包與解包: 不完全解包
VarArg 引數收集範例: asterisk variable 不完全解包
解說:
有別於 Positional unpacking 或 Sequence unpacking,一個變數對應一個元素的完全解包,
Python 還支援不完全解包,除部分元素個別單一分離外,同時允許將部分元素收集成一個集合。
下方範例: 5 個元素的 tuple,先進行解包為單一元素。前兩元素分別對應變數 a 與 b。
剩餘的 3 元素則以 asterisk 符號標記打包成 others 變數。
a, b , *others = (1,2,3,4,5)
print(a)
# 1
print(b)
# 2
print(others)
# (3,4,5)