Python偏函數(shù)partial的用法小結(jié)
本篇博客介紹Python偏函數(shù)partial的用法。
什么是partial?
functools.partial(func, /, *args, **keywords) 會(huì)返回一個(gè)新可調(diào)用對象,它把原函數(shù) func 的部分位置參數(shù)和/或關(guān)鍵字參數(shù)“預(yù)先綁定”。
這樣你就能得到一個(gè)“定制版”的函數(shù),后續(xù)只需要補(bǔ)齊剩余參數(shù)即可調(diào)用。
- 返回對象類型是 functools.partial 實(shí)例,但和函數(shù)用法相同(可調(diào)用)。
- 它擁有屬性:p.func(原函數(shù))、p.args(預(yù)綁定位置參數(shù))、p.keywords(預(yù)綁定關(guān)鍵字參數(shù))。
1) 基本用法與參數(shù)合并規(guī)則
from functools import partial
def power(base, exp, *, mod=None):
res = base ** exp
return res if mod is None else res % mod
# 1.1 預(yù)綁定部分位置參數(shù)
square = partial(power, exp=2) # 固定指數(shù)
print(square(3)) # 9
print(square(3, mod=5)) # 4
# 1.2 預(yù)綁定關(guān)鍵字參數(shù)
cube_mod_7 = partial(power, exp=3, mod=7)
print(cube_mod_7(2)) # 1 (8 % 7)
# 1.3 后續(xù)調(diào)用的關(guān)鍵字**可以覆蓋**先前綁定的關(guān)鍵字
p = partial(power, exp=2, mod=5)
print(p(3)) # 4 (9 % 5)
print(p(3, mod=None)) # 9 —— 覆蓋為 None
# 1.4 后續(xù)調(diào)用的**位置參數(shù)不能“挪位”覆蓋**已綁定的位置參數(shù)
mul = lambda a, b, c: (a, b, c)
p2 = partial(mul, 10) # a=10 已固定
print(p2(20, 30)) # (10, 20, 30)
# p2(5, a=1) -> TypeError: a 給了多個(gè)值(不允許)
2) 配合標(biāo)準(zhǔn)庫:map/sorted/reduce等“柯里化”場景
from functools import partial, reduce
from operator import mul
nums = [1, 2, 3, 4]
# 2.1 map:把二元函數(shù)“變成一元”
double = partial(mul, 2) # 固定左操作數(shù)
print(list(map(double, nums))) # [2, 4, 6, 8]
# 2.2 sorted:固定 key / reverse
students = [{"name":"A", "age":20}, {"name":"B", "age":18}]
by = partial(sorted, key=lambda x: x["age"])
print(by(students)) # 按 age 升序
by_desc = partial(sorted, key=lambda x: x["age"], reverse=True)
print(by_desc(students))
# 2.3 reduce:固定初始值
sum_from_10 = partial(reduce, lambda a, b: a + b, initial=10)
print(sum_from_10(nums)) # 20
3) 回調(diào)函數(shù)需要“額外上下文”——用partial傳額外參數(shù)
這在 GUI(PySide6/Qt)、異步回調(diào)、信號(hào)、鉤子、線程池回調(diào)里非常常見。
from functools import partial
import asyncio, concurrent.futures, time
def on_done(label, fut: concurrent.futures.Future):
print(f"[{label}] result ->", fut.result())
def heavy(x):
time.sleep(0.2)
return x * x
async def main():
loop = asyncio.get_running_loop()
with concurrent.futures.ThreadPoolExecutor() as pool:
fut = loop.run_in_executor(pool, heavy, 9)
# 給回調(diào)多傳一個(gè) label
fut.add_done_callback(partial(on_done, "task#1"))
# 也可以 await 等它
print("await:", await fut)
asyncio.run(main())
Qt 場景下,button.clicked.connect(partial(handler, extra_arg)) 也很實(shí)用(用來把行號(hào)/模型索引等傳給槽函數(shù))。
4) 裝飾器/工廠的參數(shù)化:讓“可調(diào)用簽名更好看”
用 partial 做“帶參數(shù)的裝飾器”或“工廠函數(shù)”非常自然:
from functools import wraps, partial
def _retry_impl(func, attempts, delay):
@wraps(func)
def wrapper(*args, **kwargs):
last = None
for _ in range(attempts):
try:
return func(*args, **kwargs)
except Exception as e:
last = e
time.sleep(delay)
raise last
return wrapper
# 用 partial 固定 attempts/delay,得到一個(gè)“可當(dāng)裝飾器用”的可調(diào)用
import time
retry3 = partial(_retry_impl, attempts=3, delay=0.1)
@retry3
def flaky():
print("try...")
if time.time() % 2 < 1:
raise ValueError("boom")
return "ok"
print("flaky ->", flaky())
5)partialvslambda:各有優(yōu)劣
- partial:可讀性好、可查看 p.func/p.args/p.keywords、可 picklable(常用于并發(fā)/進(jìn)程池)。
- lambda:最靈活(能改變參數(shù)順序、做簡單計(jì)算),但不可 introspect、某些場景不可序列化。
from functools import partial def f(a, b, c): return (a, b, c) # partial 只能“從左到右”補(bǔ)位置參數(shù)(或直接用關(guān)鍵字) g1 = partial(f, 1) # ==> f(1, b, c) # lambda 可自由重排 g2 = lambda b, c: f(1, c, b) # 調(diào)換了 b/c 的位置 print(g1(2, 3), g2(2, 3))
6) 深入屬性與調(diào)試
from functools import partial
def greet(greet_word, name, punctuation="!"):
return f"{greet_word}, {name}{punctuation}"
hi_tom = partial(greet, "Hi", "Tom", punctuation=".")
print(hi_tom()) # Hi, Tom.
print(hi_tom.func) # 原函數(shù) <function greet ...>
print(hi_tom.args) # ('Hi', 'Tom')
print(hi_tom.keywords) # {'punctuation': '.'}
# 可覆蓋同名關(guān)鍵字
print(hi_tom(punctuation="!!!")) # Hi, Tom!!!
7) 與實(shí)例方法的細(xì)節(jié):partialvspartialmethod
partial用在函數(shù)或綁定方法都可以。- 但如果你在類定義里想創(chuàng)建“半綁定方法”,要用
functools.partialmethod,它會(huì)正確處理self的綁定(描述符行為)。
from functools import partialmethod
class Logger:
def log(self, level, msg):
print(f"[{level}] {self.name}: {msg}")
debug = partialmethod(log, "DEBUG") # 等價(jià)于 def debug(self, msg): return self.log("DEBUG", msg)
info = partialmethod(log, "INFO")
def __init__(self, name):
self.name = name
l = Logger("core")
l.debug("hello") # [DEBUG] core: hello
l.info("world") # [INFO] core: world
若用 partial(log, "DEBUG") 直接賦給類屬性,self 不會(huì)自動(dòng)綁定,調(diào)用會(huì)報(bào)錯(cuò);partialmethod 才能正確作為方法(描述符)工作。
8) 與functools.update_wrapper的配合(可選)
partial 本身不是函數(shù)對象,若你需要較好地保留原函數(shù)的 __name__、__doc__ 等用于文檔/幫助,可包一層簡單函數(shù)并使用 update_wrapper:
from functools import partial, update_wrapper
def power(a, b): return a ** b
square = partial(power, b=2)
def as_func(p):
def wrapper(*args, **kwargs):
return p(*args, **kwargs)
return update_wrapper(wrapper, p.func)
square_fn = as_func(square)
print(square_fn.__name__, square_fn.__doc__) # 繼承了 power 的元數(shù)據(jù)
9) 與并發(fā)庫(concurrent.futures/multiprocessing)的實(shí)戰(zhàn)
partial 往往比 lambda 更容易被序列化,適合提交到進(jìn)程池。
from functools import partial
from concurrent.futures import ProcessPoolExecutor, as_completed
def area(w, h, scale=1.0):
return w * h * scale
if __name__ == "__main__":
scaled_area = partial(area, scale=0.5) # 可被 pickle
items = [(10, 20), (3, 4), (6, 7)]
with ProcessPoolExecutor() as ex:
futs = [ex.submit(scaled_area, w, h) for (w, h) in items]
for f in as_completed(futs):
print("area:", f.result())
10) 在日志/打印等“固定上下文”的場景
from functools import partial
print_info = partial(print, "[INFO]") # 固定前綴
print_warn = partial(print, "[WARN]")
print_info("system started")
print_warn("disk almost full")
11) 常見誤區(qū)與最佳實(shí)踐
- 不能使用“占位符”來跳過中間某個(gè)位置參數(shù)(stdlib 沒有這個(gè)特性)。
需要時(shí)用關(guān)鍵字參數(shù)或 lambda 重排。 - 重復(fù)提供同名位置參數(shù)會(huì)報(bào)錯(cuò)(“給了多個(gè)值”);重復(fù)關(guān)鍵字后者覆蓋前者。
- 不要把可變對象當(dāng)作“默認(rèn)值狀態(tài)”去修改(例如綁定 list 后在原函數(shù)里修改它),除非你就是有意為之;partial 持有的引用和普通默認(rèn)參數(shù)一樣需要小心共享狀態(tài)。
- 在框架/回調(diào)里,優(yōu)先用 partial 傳遞額外上下文,比 lambda 更“可調(diào)試/可序列化”。
小結(jié)
partial:給函數(shù)“預(yù)裝參數(shù)”,寫出更簡潔的 API/回調(diào)。partialmethod:在類中定義“半綁定方法”,正確處理self。- 常見場景:回調(diào)傳參、并發(fā)任務(wù)/進(jìn)程池、排序/映射的柯里化、日志前綴、裝飾器參數(shù)化。
到此這篇關(guān)于Python偏函數(shù)partial的用法小結(jié)的文章就介紹到這了,更多相關(guān)Python偏函數(shù)partial內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
對tensorflow中的strides參數(shù)使用詳解
今天小編就為大家分享一篇對tensorflow中的strides參數(shù)使用詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-01-01
Python實(shí)現(xiàn)合并兩個(gè)字典的8種方法
Python有多種方法可以通過使用各種函數(shù)和構(gòu)造函數(shù)來合并字典,本文主要介紹了Python實(shí)現(xiàn)合并兩個(gè)字典的8種方法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07
python 實(shí)現(xiàn)全球IP歸屬地查詢工具
這篇文章主要介紹了python 實(shí)現(xiàn)全球IP歸屬地查詢工具的示例代碼,幫助大家更好的理解和使用python,感興趣的朋友可以了解下2020-12-12
python實(shí)現(xiàn)自動(dòng)登錄后臺(tái)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)自動(dòng)登錄后臺(tái)管理系統(tǒng),并進(jìn)行后續(xù)操作,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10
Python中標(biāo)準(zhǔn)庫array數(shù)組操作舉例詳解
這篇文章主要介紹了Python中標(biāo)準(zhǔn)庫array數(shù)組操作的相關(guān)資料,Python的array模塊提供了固定類型數(shù)組類,用于高效存儲(chǔ)同類型元素,節(jié)省內(nèi)存并支持?jǐn)?shù)值計(jì)算,需要的朋友可以參考下2025-04-04
Python urlopen()和urlretrieve()用法解析
這篇文章主要介紹了Python urlopen()和urlretrieve()用法解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01
用Python簡單實(shí)現(xiàn)Http服務(wù)端
這篇文章主要為大家介紹了使用Python簡單實(shí)現(xiàn)Http服務(wù)端示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
Python?matplotlib數(shù)據(jù)可視化圖繪制
這篇文章主要介紹了Python?matplotlib數(shù)據(jù)可視化圖繪制,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-07-07

