Python 內建模組介紹 — operator 模組
Posted on Apr 22, 2024 in Python 程式設計 - 中階 by Amo Chen ‐ 3 min read
本文將介紹 1 個相對少被提到的 operator 模組,該模組提供與 Python 各種運算子(operator)相對應的函式,適合喜歡 functional programming 的開發者使用,此外也有幾個高實用性的方法可以使用,不妨花一點點時間看看吧!
本文環境
- Python 3
operator 模組簡介
operator 是 1 個提供與 Python 各種運算子(operator)相對應函式的內建模組。
譬如加法運算子 +
, 其對應的 operator 模組函式為 operator.add(a, b) , 所以 c = a + b
可以用 operator 模組改寫成 c = operator.add(a, b)
。
operator 模組為了相容性,也提供雙底線開頭的同名函式,例如 operator.add(a, b)
也會有 1 個雙底線開頭的同名函式 __add__(a, b)
,官方建議不要使用雙底線開頭的同名函式。
operator 模組適合喜歡 functional programming 的人之外,也可以提高程式碼的可讀性,某些不常用的運算子,可以用相對應的 operator 函式表示,譬如 xor
或者無條件捨去小數點(floor division):
xor_answer = operator.xor(1, 0) # 1 ^ 0
floor_div_answer = operator.floordiv(1, 2) # 1 // 2
此外, operator 模組也內建幾個常用的函式,提供開發者使用,我們不需要重新發明輪胎,直接使用 Python 提供的函式即可,例如 operator.countOf(a, b)
可以計算 b
在 a
的出現次數:
>>> import operator
>>> operator.countOf(['a', 'b', 'a', 'c'], 'a')
2
In-place 運算子(operator)
In-place 運算子又稱為「原地」運算子,乍看之下很難懂,但其實指的是 a += b
之類的用法, a += b
其實就是 a = a + b
,執行上可以拆分為 2 個步驟:
- 計算
- 賦值
所以 a += b
是先計算 a + b
後,再將計算結果賦值給 a
。
雖然 operator 模組也有相對應原地運算子的方法,但是對於字串、數字、 tuple 這些 immutable 的資料型態,它只會執行計算的步驟,如下列範例中的 operator.iadd(a, b)
只做 a + b
而已,所以執行完之後 a
仍是原來的值 1
:
>>> import operator
>>> a, b = 1, 2
>>> operator.iadd(a, b)
3
>>> a
1
所以要重新賦值給 a
的話,要寫成 a = operator.iadd(a, b)
才行。
其他包含 list 或者 dictionary 等 mutable 的資料型態,則會有賦值的效果:
>>> import operator
>>> a, b = [1, 2, 3], [3, 2, 1]
>>> operator.iadd(a, b)
[1, 2, 3, 3, 2, 1]
>>> a
[1, 2, 3, 3, 2, 1]
這是使用上特別注意的地方。
attrgetter() & itemgetter()
接著介紹 operator 模組 2 個實用的方法:
operator.attrgetter()
operator.itemgetter()
operator.attrgetter()
首先考慮以下範例,類別 A 內嵌類別 B 的情況:
class B:
def __init__(self):
self.c = 'value'
class A:
def __init__(self):
self.b = B()
a = A()
當我們要取變數 a
裡的屬性 b.c
的值時,可以使用 attrgetter()
,該方法支援用 .
分隔的用法,所以能用 attrgetter('b.c')
表示,該方法會回傳 1 個可呼叫的 object, 我們再將想要取值的變數 a
傳進該 object, 即可完成取值:
>>> import operator
>>> getter = operator.attrgetter('b.c')
>>> getter(a)
'value'
上述範例可以進一步改寫為下列形式:
>>> import operator
>>> operator.attrgetter('b.c')(a)
'value'
這種用法等同於下列寫法:
def resolve_attr(obj, attr):
for name in attr.split("."):
obj = getattr(obj, name)
return obj
值得注意的是內建函式 getattr() 不支援用 .
分隔的用法:
>>> getattr(a, 'b.c')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'b.c'
attrgetter()
與 getattr()
ㄧ樣,如果某屬性不存在的話,則會拋出 AttributeError
,例如:
>>> operator.attrgetter('b.c.d')(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'd'
operator.attrgetter()
也支援 1 次取得多個屬性的方法,例如下列類別:
class User:
name = 'Amo'
last_name = 'Chen'
我們可以用 attrgetter()
一次取得多個屬性值:
>>> name, last_name = operator.attrgetter('name', 'last_name')(User)
>>> name, last_name
('Amo', 'Chen')
相當方便!
operator.itemgetter()
operator.itemgetter()
是 operator 模組另 1 個實用的方法,可以對 1 個 iterable 取 1 個或多個值,使用方法與 attrgetter()
相似,會回傳 1 個可呼叫的 object, 接著再將要取值的變數傳入即可,例如下列範例,我們要取的 list a
中的索引為 1, 3, -1
的值,可以如此表示:
>>> import operator
>>> a = [0, 2, -1, 4, 6]
>>> getter = operator.itemgetter(1, 3, -1)
>>> getter(a)
(2, 4, 6)
除此之外,針對 dictionary 也可以一次取得多個 key 的值:
>>> import operator
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> getter = operator.itemgetter('a', 'c')
>>> getter(d)
(1, 3)
是否相當方便呢!
總結
operator 模組雖然是比較少被談到的模組,但該模組也提供不少高實用性的方法可以使用(詳見 operator — Standard operators as functions 對照表),如果知道有這些方法存在,絕對可以減少一些寫 lambda 函式的情況!
以上!
Enjoy!
References
operator — Standard operators as functions