每周一個(gè) Python 模塊 | functools

functools 是 Python 中很簡單但也很重要的模塊,主要是一些 Python 高階函數(shù)相關(guān)的函數(shù)。 該模塊的內(nèi)容并不多袁滥,看 官方文檔 也就知道了。

說到高階函數(shù),這是函數(shù)式編程范式中很重要的一個(gè)概念自晰,簡單地說, 就是一個(gè)可以接受函數(shù)作為參數(shù)或者以函數(shù)作為返回值的函數(shù)稍坯,因?yàn)?Python 中函數(shù)是一類對象酬荞, 因此很容易支持這樣的函數(shù)式特性。

functools 模塊中函數(shù)只有 cmp_to_key瞧哟、partial混巧、reducetotal_ordering勤揩、update_wrapper咧党、wrapslru_cache 這幾個(gè):

被發(fā)配邊疆的 reduce

這個(gè) functools.reduce 就是 Python 2 內(nèi)建庫中的 reduce陨亡,它之所以出現(xiàn)在這里就是因?yàn)?Guido 的獨(dú)裁傍衡,他并不喜歡函數(shù)式編程中的“map-reduce”概念,因此打算將 mapreduce 兩個(gè)函數(shù)移出內(nèi)建函數(shù)庫数苫,最后在社區(qū)的強(qiáng)烈反對中將 map 函數(shù)保留在了內(nèi)建庫中聪舒, 但是 Python 3 內(nèi)建的 map 函數(shù)返回的是一個(gè)迭代器對象,而 Python 2 中會(huì) eagerly 生成一個(gè) list虐急,使用時(shí)要多加注意箱残。

該函數(shù)的作用是將一個(gè)序列歸納為一個(gè)輸出,其原型如下:

reduce(function, sequence, startValue)

使用示例:

>>> def foo(x, y):
...     return x + y
...
>>> l = range(1, 10)
>>> reduce(foo, l)
45
>>> reduce(foo, l, 10)
55

偏函數(shù) partialpartialmethod

用于創(chuàng)建一個(gè)偏函數(shù)止吁,它用一些默認(rèn)參數(shù)包裝一個(gè)可調(diào)用對象被辑,返回結(jié)果是可調(diào)用對象,并且可以像原始對象一樣對待敬惦,這樣可以簡化函數(shù)調(diào)用盼理。

一個(gè)簡單的使用示例:

from functools import partial

def add(x, y):
    return x + y

add_y = partial(add, 3)  # add_y 是一個(gè)新的函數(shù)
add_y(4) # 7

一個(gè)很實(shí)用的例子:

def json_serial_fallback(obj):
    """
    JSON serializer for objects not serializable by default json code
    """
    if isinstance(obj, (datetime.datetime, datetime.date)):
        return str(obj)
    if isinstance(obj, bytes):
        return obj.decode("utf-8")
    raise TypeError ("%s is not JSON serializable" % obj)

json_dumps = partial(json.dumps, default=json_serial_fallback)

可以在 json_serial_fallback 函數(shù)中添加類型判斷來指定如何 json 序列化一個(gè) Python 對象

partialmethod 是 Python 3.4 中新引入的裝飾器,作用基本類似于 partial俄删, 不過僅作用于方法宏怔。舉個(gè)例子就很容易明白:

class Cell(object):
    def __init__(self):
        self._alive = False
    @property
    def alive(self):
        return self._alive
    def set_state(self, state):
        self._alive = bool(state)

    set_alive = partialmethod(set_state, True)
    set_dead = partialmethod(set_state, False)

c = Cell()
c.alive         # False
c.set_alive()
c.alive         # True

在 Python 2 中使用 partialmethod 可以這樣定義:

# Code from https://gist.github.com/carymrobbins/8940382
from functools import partial

class partialmethod(partial):
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return partial(self.func, instance,
                       *(self.args or ()), **(self.keywords or {}))

裝飾器相關(guān)

說到“接受函數(shù)為參數(shù)奏路,以函數(shù)為返回值”,在 Python 中最常用的當(dāng)屬裝飾器了臊诊。 functools 庫中裝飾器相關(guān)的函數(shù)是 update_wrapper鸽粉、wraps,還搭配 WRAPPER_ASSIGNMENTSWRAPPER_UPDATES 兩個(gè)常量使用抓艳,作用就是消除 Python 裝飾器的一些負(fù)面作用触机。

wraps

例:

def decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@decorator
def add(x, y):
    return x + y

add     # <function __main__.wrapper>

可以看到被裝飾的函數(shù)的名稱,也就是函數(shù)的 __name__ 屬性變成了 wrapper玷或, 這就是裝飾器帶來的副作用儡首,實(shí)際上add 函數(shù)整個(gè)變成了 decorator(add),而 wraps 裝飾器能消除這些副作用:

def decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@decorator
def add(x, y):
    return x + y

add     # <function __main__.add>

更正的屬性定義在 WRAPPER_ASSIGNMENTS 中:

>>> functools.WRAPPER_ASSIGNMENTS
('__module__', '__name__', '__doc__')
>>> functools.WRAPPER_UPDATES
('__dict__',)

update_wrapper

update_wrapper 的作用與 wraps 類似偏友,不過功能更加強(qiáng)大蔬胯,換句話說,wraps 其實(shí)是 update_wrapper 的特殊化约谈,實(shí)際上 wraps(wrapped) 相當(dāng)于 partial(update_wrapper, wrapped=wrapped, **kwargs)笔宿。

因此,上面的代碼可以用 update_wrapper 重寫如下:

def decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return update_wrapper(wrapper, func)

用于比較的 cmp_to_keytotal_ordering

cmp_to_key

list.sort 和 內(nèi)建函數(shù) sorted 中都有一個(gè) key 參數(shù)棱诱,這個(gè)參數(shù)用來指定取元素的什么值進(jìn)行比較,例如按字符串元素的長度進(jìn)行比較:

>>> x = ['hello','abc','iplaypython.com']
>>> x.sort(key=len)
>>> x
['abc', 'hello', 'iplaypython.com']

也就是說排序時(shí)會(huì)先對每個(gè)元素調(diào)用 key 所指定的函數(shù)涝动,然后再排序迈勋。同時(shí),sortedlist.sort 還提供了 cmp 參數(shù)來指定如何比較兩個(gè)元素醋粟,但是在 Python 3 中該參數(shù)被去掉了靡菇。

cmp_to_key 是 Python 2.7 中新增的函數(shù),用于將比較函數(shù)轉(zhuǎn)換為 key 函數(shù)米愿, 這樣就可以應(yīng)用在接受 key 函數(shù)為參數(shù)的函數(shù)中厦凤。比如 sorted()min()育苟、 max()较鼓、 heapq.nlargest()itertools.groupby() 等违柏。

sorted(range(5), key=cmp_to_key(lambda x, y: y-x))      # [4, 3, 2, 1, 0]

total_ordering

total_ordering 同樣是 Python 2.7 中新增函數(shù)博烂,用于簡化比較函數(shù)的寫法。如果你已經(jīng)定義了__eq__ 方法漱竖,以及 __lt__禽篱、__le____gt__ 或者 __ge__() 其中之一馍惹, 即可自動(dòng)生成其它比較方法躺率。官方示例:

@total_ordering
class Student:
    def __eq__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) ==
                (other.lastname.lower(), other.firstname.lower()))
    def __lt__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) <
                (other.lastname.lower(), other.firstname.lower()))

dir(Student)    # ['__doc__', '__eq__', '__ge__', '__gt__', '__le__', '__lt__', '__module__']

再看一個(gè)示例:

from functools import total_ordering

@total_ordering
class Student:
    def __init__(self, firstname, lastname):
        self.firstname = firstname
        self.lastname = lastname

    def __eq__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) ==
                (other.lastname.lower(), other.firstname.lower()))

    def __lt__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) <
                (other.lastname.lower(), other.firstname.lower()))

print dir(Student)

stu = Student("Huoty", "Kong")
stu2 = Student("Huoty", "Kong")
stu3 = Student("Qing", "Lu")

print stu == stu2
print stu > stu3

輸出結(jié)果:

['__doc__', '__eq__', '__ge__', '__gt__', '__init__', '__le__', '__lt__', '__module__']
True
False

用于緩存的lru_cache

這個(gè)裝飾器是在 Python3 中新加的玛界,在 Python2 中如果想要使用可以安裝第三方庫 functools32。該裝飾器用于緩存函數(shù)的調(diào)用結(jié)果悼吱,對于需要多次調(diào)用的函數(shù)脚仔,而且每次調(diào)用參數(shù)都相同,則可以用該裝飾器緩存調(diào)用結(jié)果舆绎,從而加快程序運(yùn)行鲤脏。示例:

from functools import lru_cache

@lru_cache(None)
def add(x, y):
    print("calculating: %s + %s" % (x, y))
    return x + y

print(add(1, 2))
print(add(1, 2))  # 直接返回緩存信息
print(add(2, 3))

輸出結(jié)果:

calculating: 1 + 2
3
3
calculating: 2 + 3
5

由于該裝飾器會(huì)將不同的調(diào)用結(jié)果緩存在內(nèi)存中,因此需要注意內(nèi)存占用問題吕朵,避免占用過多內(nèi)存猎醇,從而影響系統(tǒng)性能。

相關(guān)文檔:

blog.windrunner.me/python/func…

kuanghy.github.io/2016/10/26/…

pymotw.com/3/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末努溃,一起剝皮案震驚了整個(gè)濱河市硫嘶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌梧税,老刑警劉巖沦疾,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異第队,居然都是意外死亡哮塞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門凳谦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來忆畅,“玉大人,你說我怎么就攤上這事尸执〖铱” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵如失,是天一觀的道長绊诲。 經(jīng)常有香客問我,道長褪贵,這世上最難降的妖魔是什么掂之? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮竭鞍,結(jié)果婚禮上板惑,老公的妹妹穿的比我還像新娘。我一直安慰自己偎快,他們只是感情好冯乘,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著晒夹,像睡著了一般裆馒。 火紅的嫁衣襯著肌膚如雪姊氓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天喷好,我揣著相機(jī)與錄音翔横,去河邊找鬼。 笑死梗搅,一個(gè)胖子當(dāng)著我的面吹牛禾唁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播无切,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼荡短,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了哆键?” 一聲冷哼從身側(cè)響起掘托,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎籍嘹,沒想到半個(gè)月后闪盔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辱士,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年泪掀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片识补。...
    茶點(diǎn)故事閱讀 40,852評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡族淮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凭涂,到底是詐尸還是另有隱情,我是刑警寧澤贴妻,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布切油,位于F島的核電站,受9級特大地震影響名惩,放射性物質(zhì)發(fā)生泄漏澎胡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一娩鹉、第九天 我趴在偏房一處隱蔽的房頂上張望攻谁。 院中可真熱鬧,春花似錦弯予、人聲如沸戚宦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽受楼。三九已至垦搬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間艳汽,已是汗流浹背猴贰。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留河狐,地道東北人米绕。 一個(gè)月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像馋艺,于是被迫代替她去往敵國和親栅干。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 包(lib)丈钙、模塊(module) 在Python中非驮,存在包和模塊兩個(gè)常見概念。 模塊:編寫Python代碼的py...
    清清子衿木子水心閱讀 3,812評論 0 27
  • functools 模塊提供用于調(diào)整或擴(kuò)展函數(shù)和其他可調(diào)用對象的工具,而無需完全重寫它們星岗。 裝飾器 partial...
    妄心xyx閱讀 1,304評論 0 17
  • 1 functools函數(shù) functools模塊用于高階函數(shù):作用與或者返回其它函數(shù)的函數(shù)填大。一般來說,對于該模塊...
    lakerszhy閱讀 9,998評論 0 7
  • 薩菲立刻完了水電費(fèi)qwersdfasdfwe
    閆盡歡閱讀 288評論 0 0
  • 他去快遞公司郵寄包裹遇到為他服務(wù)的她俏橘,她溫柔善良允华,她的笑容像一朵泛開的漣漪一圈一圈蕩進(jìn)他的每一滴血液里。 于是他開...
    云中聽書閱讀 222評論 1 7