当前位置: 代码网 > it编程>前端脚本>Python > Python偏函数partial的用法小结

Python偏函数partial的用法小结

2026年01月12日 Python 我要评论
本篇博客介绍python偏函数partial的用法。什么是partial?functools.partial(func, /, *args, **keywords) 会返回一个新可调用对象,它把原函数

本篇博客介绍python偏函数partial的用法。

什么是partial?

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

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

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

from functools import partial

def power(base, exp, *, mod=none):
    res = base ** exp
    return res if mod is none else res % 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 = 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 给了多个值(不允许)

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

from functools import partial, reduce
from operator import mul

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=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) 回调函数需要“额外上下文”——用partial传额外参数

这在 gui(pyside6/qt)、异步回调、信号、钩子、线程池回调里非常常见。

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)
        # 给回调多传一个 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 做“带参数的装饰器”或“工厂函数”非常自然:

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,得到一个“可当装饰器用”的可调用
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:各有优劣

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

# partial 只能“从左到右”补位置参数(或直接用关键字)
g1 = partial(f, 1)                 # ==> f(1, b, c)

# lambda 可自由重排
g2 = lambda b, c: f(1, c, b)       # 调换了 b/c 的位置
print(g1(2, 3), g2(2, 3))

6) 深入属性与调试

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)                 # 原函数 <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 的绑定(描述符行为)。
from functools import partialmethod

class logger:
    def log(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

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 的元数据

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

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

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) 常见误区与最佳实践

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

小结

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

到此这篇关于python偏函数partial的用法小结的文章就介绍到这了,更多相关python偏函数partial内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com