跳至主要内容

Python 打包與解包

Packing / Unpacking 時 Asterisk 相關摘要整理

Python 打包與解包解說

解包(開箱) 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 常數實作範例

# 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)