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) 可以計算 ba 的出現次數:

>>> import operator
>>> operator.countOf(['a', 'b', 'a', 'c'], 'a')
2

In-place 運算子(operator)

In-place 運算子又稱為「原地」運算子,乍看之下很難懂,但其實指的是 a += b 之類的用法, a += b 其實就是 a = a + b ,執行上可以拆分為 2 個步驟:

  1. 計算
  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 個實用的方法:

  1. operator.attrgetter()
  2. 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

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!