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
混巧、reduce
、total_ordering
勤揩、update_wrapper
咧党、wraps
、lru_cache
這幾個(gè):
被發(fā)配邊疆的 reduce
這個(gè) functools.reduce
就是 Python 2 內(nèi)建庫中的 reduce
陨亡,它之所以出現(xiàn)在這里就是因?yàn)?Guido 的獨(dú)裁傍衡,他并不喜歡函數(shù)式編程中的“map-reduce”概念,因此打算將 map
和 reduce
兩個(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ù) partial
和 partialmethod
用于創(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_ASSIGNMENTS
和 WRAPPER_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_key
和 total_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í),sorted
和 list.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…