当前位置: 首页 > news >正文

Python偏函数partial的用法小结

functools.partial(func, /, *args, **keywords) 会返回一个新可调用对象,它把原函数 func 的部分位置参数和/或关键字参数“预先绑定”。
这样你就能得到一个“定制版”的函数,后续只需要补齐剩余参数即可调用。

  • 返回对象类型是 functools.partial 实例,但和函数用法相同(可调用)。
  • 它拥有属性:p.func(原函数)、p.args(预绑定位置参数)、p.keywords(预绑定关键字参数)。

1) 基本用法与参数合并规则

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

fromfunctoolsimportpartial

defpower(base, exp,*, mod=None):

res=base**exp

returnresifmodisNoneelseres%mod

# 1.1 预绑定部分位置参数

square=partial(power, exp=2)# 固定指数

print(square(3))# 9

print(square(3, mod=5))# 4

# 1.2 预绑定关键字参数

cube_mod_7=partial(power, exp=3, mod=7)

print(cube_mod_7(2))# 1 (8 % 7)

# 1.3 后续调用的关键字**可以覆盖**先前绑定的关键字

p=partial(power, exp=2, mod=5)

print(p(3))# 4 (9 % 5)

print(p(3, mod=None))# 9 —— 覆盖为 None

# 1.4 后续调用的**位置参数不能“挪位”覆盖**已绑定的位置参数

mul=lambdaa, b, c: (a, b, c)

p2=partial(mul,10)# a=10 已固定

print(p2(20,30))# (10, 20, 30)

# p2(5, a=1) -> TypeError: a 给了多个值(不允许)

2) 配合标准库:map/sorted/reduce等“柯里化”场景

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

fromfunctoolsimportpartial,reduce

fromoperatorimportmul

nums=[1,2,3,4]

# 2.1 map:把二元函数“变成一元”

double=partial(mul,2)# 固定左操作数

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=lambdax: x["age"])

print(by(students))# 按 age 升序

by_desc=partial(sorted, key=lambdax: x["age"], reverse=True)

print(by_desc(students))

# 2.3 reduce:固定初始值

sum_from_10=partial(reduce,lambdaa, b: a+b, initial=10)

print(sum_from_10(nums))# 20

3) 回调函数需要“额外上下文”——用partial传额外参数

这在 GUI(PySide6/Qt)、异步回调、信号、钩子、线程池回调里非常常见。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

fromfunctoolsimportpartial

importasyncio, concurrent.futures, time

defon_done(label, fut: concurrent.futures.Future):

print(f"[{label}] result ->", fut.result())

defheavy(x):

time.sleep(0.2)

returnx*x

asyncdefmain():

loop=asyncio.get_running_loop()

with concurrent.futures.ThreadPoolExecutor() as pool:

fut=loop.run_in_executor(pool, heavy,9)

# 给回调多传一个 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)) 也很实用(用来把行号/模型索引等传给槽函数)。

4) 装饰器/工厂的参数化:让“可调用签名更好看”

partial做“带参数的装饰器”或“工厂函数”非常自然:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

fromfunctoolsimportwraps, partial

def_retry_impl(func, attempts, delay):

@wraps(func)

defwrapper(*args,**kwargs):

last=None

for_inrange(attempts):

try:

returnfunc(*args,**kwargs)

exceptException as e:

last=e

time.sleep(delay)

raiselast

returnwrapper

# 用 partial 固定 attempts/delay,得到一个“可当装饰器用”的可调用

importtime

retry3=partial(_retry_impl, attempts=3, delay=0.1)

@retry3

defflaky():

print("try...")

iftime.time()%2<1:

raiseValueError("boom")

return"ok"

print("flaky ->", flaky())

5)partialvslambda:各有优劣

  • partial:可读性好、可查看 p.func/p.args/p.keywords、可 picklable(常用于并发/进程池)。
  • lambda:最灵活(能改变参数顺序、做简单计算),但不可 introspect、某些场景不可序列化。

1

2

3

4

5

6

7

8

9

fromfunctoolsimportpartial

deff(a, b, c):return(a, b, c)

# partial 只能“从左到右”补位置参数(或直接用关键字)

g1=partial(f,1)# ==> f(1, b, c)

# lambda 可自由重排

g2=lambdab, c: f(1, c, b)# 调换了 b/c 的位置

print(g1(2,3), g2(2,3))

6) 深入属性与调试

1

2

3

4

5

6

7

8

9

10

11

12

13

14

fromfunctoolsimportpartial

defgreet(greet_word, name, punctuation="!"):

returnf"{greet_word}, {name}{punctuation}"

hi_tom=partial(greet,"Hi","Tom", punctuation=".")

print(hi_tom())# Hi, Tom.

print(hi_tom.func)# 原函数 <function greet ...>

print(hi_tom.args)# ('Hi', 'Tom')

print(hi_tom.keywords)# {'punctuation': '.'}

# 可覆盖同名关键字

print(hi_tom(punctuation="!!!"))# Hi, Tom!!!

7) 与实例方法的细节:partialvspartialmethod

  • partial用在函数或绑定方法都可以。
  • 但如果你在类定义里想创建“半绑定方法”,要用functools.partialmethod,它会正确处理self的绑定(描述符行为)。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

fromfunctoolsimportpartialmethod

classLogger:

deflog(self, level, msg):

print(f"[{level}] {self.name}: {msg}")

debug=partialmethod(log,"DEBUG")# 等价于 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 不会自动绑定,调用会报错;partialmethod 才能正确作为方法(描述符)工作。

8) 与functools.update_wrapper的配合(可选)

partial本身不是函数对象,若你需要较好地保留原函数的__name____doc__等用于文档/帮助,可包一层简单函数并使用update_wrapper

1

2

3

4

5

6

7

8

9

10

11

12

13

fromfunctoolsimportpartial, update_wrapper

defpower(a, b):returna**b

square=partial(power, b=2)

defas_func(p):

defwrapper(*args,**kwargs):

returnp(*args,**kwargs)

returnupdate_wrapper(wrapper, p.func)

square_fn=as_func(square)

print(square_fn.__name__, square_fn.__doc__)# 继承了 power 的元数据

9) 与并发库(concurrent.futures/multiprocessing)的实战

partial往往比lambda更容易被序列化,适合提交到进程池。

1

2

3

4

5

6

7

8

9

10

11

12

13

fromfunctoolsimportpartial

fromconcurrent.futuresimportProcessPoolExecutor, as_completed

defarea(w, h, scale=1.0):

returnw*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)initems]

forfinas_completed(futs):

print("area:", f.result())

10) 在日志/打印等“固定上下文”的场景

1

2

3

4

5

6

fromfunctoolsimportpartial

print_info=partial(print,"[INFO]")# 固定前缀

print_warn=partial(print,"[WARN]")

print_info("system started")

print_warn("disk almost full")

11) 常见误区与最佳实践

  1. 不能使用“占位符”来跳过中间某个位置参数(stdlib 没有这个特性)。
    需要时用关键字参数或 lambda 重排。
  2. 重复提供同名位置参数会报错(“给了多个值”);重复关键字后者覆盖前者。
  3. 不要把可变对象当作“默认值状态”去修改(例如绑定 list 后在原函数里修改它),除非你就是有意为之;partial 持有的引用和普通默认参数一样需要小心共享状态。
  4. 在框架/回调里,优先用 partial 传递额外上下文,比 lambda 更“可调试/可序列化”。

小结

  • partial:给函数“预装参数”,写出更简洁的 API/回调。
  • partialmethod:在类中定义“半绑定方法”,正确处理self
  • 常见场景:回调传参、并发任务/进程池、排序/映射的柯里化、日志前缀、装饰器参数化。
http://www.jsqmd.com/news/569784/

相关文章:

  • Z-Image-Turbo-辉夜巫女多场景落地:文旅IP数字化——地方神社联名AI形象生成
  • Ollama搭配BGE-M3实战:手把手教你构建个人知识库问答系统(附完整代码)
  • Intv_AI_MK11内容安全与审核实战:识别与过滤违规文本
  • 基于Python+Vue开发的婚恋交友管理系统源码+运行步骤+计算机科学与技术
  • 抖音音频智能提取3步法:告别繁琐操作,效率提升10倍的技术指南
  • 从入门到精通解析Python Selenium如何模拟浏览器操作
  • Qwen3-14B后端开发实战:构建高并发AI对话API服务
  • ColabFold:革新蛋白质结构预测的普惠工具
  • Phi-3-Mini-128K实战JavaScript:构建前端智能代码提示插件
  • AlwaysOnTop:终极窗口置顶解决方案,让你告别桌面混乱的烦恼
  • CSSCI论文写作07:如何写作文献综述
  • 2026年质量好的箱体式水源热泵机组/水源热泵地源热泵机组/山东水源热泵机组一体机口碑好的厂家推荐 - 行业平台推荐
  • UI-TARS-desktop场景应用:自动生成销售报告与更新库存实战
  • 终极Cursor Pro破解指南:3步解锁完整AI编程助手功能
  • Phi-4-mini-reasoning入门必看:为何专注推理的模型需特殊提示工程
  • SOLIDWORKS模型导入Adams做运动仿真?先搞定这3个前置设置(路径/命名/格式)
  • 使用Python轻松管理Word页脚
  • Visio流程图智能生成与优化:Phi-4-mini-reasoning理解需求自动绘图
  • 2026年口碑好的山东空气源热泵机组采暖/山东超低温空气源热泵机组/风冷模块空气源热泵机组实力工厂怎么选 - 行业平台推荐
  • QAnything客服知识库:多轮对话历史管理策略
  • Polars 2.0大规模清洗崩溃全解析:内存溢出、Schema冲突、LazyFrame中断——3类高频致命报错的5分钟修复方案
  • 破解数字音乐枷锁:ncmdumpGUI赋能用户掌控音频资产
  • BlenderUSDZ插件:如何解决AR模型导出的三大核心挑战
  • 百联OK卡如何安心回收?选对平台轻松省心! - 团团收购物卡回收
  • 你的Mac也能玩iOS游戏了?PlayCover让苹果生态无缝连接
  • 2026年质量好的无锡全电动注塑机/高速电动注塑机/100吨电动注塑机/进口电动注塑机高评分品牌推荐(畅销) - 行业平台推荐
  • QQ音乐加密文件终极转换指南:3步解锁跨平台播放自由
  • DCT-Net人像卡通化:5分钟搭建个人专属服务,一键生成卡通头像
  • 5分钟搞懂线结构光三维重建:从激光平面到深度信息的完整流程
  • vLLM-v0.17.1集成Dify应用开发:构建企业级AI智能体工作流