Python 中 functools.partial 偏函数

FreeGuideOnline 最新 2026-07-07

Python functools.partial 偏函数完全指南

什么是偏函数

偏函数(Partial Function)是指通过固定原函数的部分参数,生成一个更简单的新函数。在 Python 中,偏函数由标准库 functools 模块的 partial 类提供。

简单说:如果你有一个函数需要很多参数,但大多数场景下某些参数总是相同的值,你就可以“冻结”这些固定值,得到一个只需剩余参数的新函数。

partial 本身是一个可调用的对象,当调用偏函数时,它会将预先填好的参数与原调用参数合并,然后交给原函数执行。

为什么需要偏函数

  • 减少重复代码:避免每次调用都传递相同的参数。
  • 提高可读性:生成具有明确语义的新函数名,让意图更清晰。
  • 适配回调接口:需要将多参数函数传给只接收单参数函数的框架时(如 GUI 事件处理、多线程回调)。
  • 创建快捷方式:在不修改原函数定义的情况下,提供默认行为的变体。

基本语法

from functools import partial

# partial(原函数, *固定位置参数, **固定关键字参数)
新函数 = partial(原函数, arg1, arg2, kwarg1=value)
  • 返回的新函数在调用时只需提供未被固定的参数。
  • 可以同时固定位置参数和关键字参数。

使用示例

示例 1:固定关键字参数

def power(base, exponent):
    return base ** exponent

# 固定 exponent=2 创建平方函数
square = partial(power, exponent=2)

print(square(5))   # 25
print(square(10))  # 100

现在 square 只接受一个参数 baseexponent 始终为 2。

示例 2:固定位置参数

def divide(a, b):
    return a / b

# 固定第一个参数 a=10
half = partial(divide, 10)

# 调用时只需传入第二个参数 b
print(half(2))   # 5.0
print(half(5))   # 2.0

示例 3:同时固定位置参数和关键字参数

import json

# 固定 indent 和 ensure_ascii,使 JSON 总是以缩进、可读形式输出
pretty_json = partial(json.dumps, indent=4, ensure_ascii=False)

data = {"name": "张三", "age": 25}
print(pretty_json(data))

输出:

{
    "name": "张三",
    "age": 25
}

参数覆盖与位置占位符

固定参数被后续调用覆盖

调用偏函数时传入的参数会追加在固定参数的后面,或者覆盖同名的关键字参数。

def greet(greeting, name):
    return f"{greeting}, {name}!"

hello = partial(greet, "Hello")
print(hello("World"))          # Hello, World!
print(hello("Alice"))          # Hello, Alice!

# 调用时也可以覆盖 greeting
print(hello("Bob", greeting="Hi"))  # Hi, Bob!

如果原函数不允许重复关键字参数,传参覆盖可能导致 TypeError,但 partial 用正确的顺序处理了这个问题 —— 当调用时提供的关键字参数与固定关键字参数冲突时,调用时提供的值优先。

位置占位符

functools.partial 本身不支持占位符(如只固定中间参数而保留两边参数)。但你可以使用其他第三方工具(如 toolz.curryfuncy),或者自己用 lambda 实现。

不过有一种替代方案:使用关键字参数来控制需要固定的部分。

# 想固定中间参数,可通过关键字参数实现
def func(a, b, c):
    return a, b, c

# 固定 b=2
p = partial(func, b=2)
print(p(1, 3))      # (1, 2, 3)
print(p(10, c=20))  # (10, 2, 20)

partial 与 lambda 的对比

很多场景下 partial 可以用 lambda 替代,但各有优劣。

特性 partial lambda
可读性 意图明确,固定参数一目了然 稍复杂,需阅读表达式
性能 稍快(C 实现) 稍慢(每次调用创建函数对象)
灵活性 只能固定参数 可以执行任意计算
调试 __repr__ 显示原函数和参数 无有意义的名字
序列化 可通过 pickle 序列化 通常不可序列化

推荐:如果只是单纯的参数绑定,优先使用 partial。它显式地表达了“这是一个偏函数”。

高级用法

作为装饰器使用

可以用 partial 来创建装饰器,固定某个参数。

def add_suffix(prefix, text, suffix):
    return f"{prefix}{text}{suffix}"

# 固定 prefix 和 suffix
bracket_wrapper = partial(add_suffix, prefix="[", suffix="]")
print(bracket_wrapper("info"))   # [info]

在类方法中绑定 self

class Calculator:
    def __init__(self, factor):
        self.factor = factor

    def multiply(self, x):
        return x * self.factor

calc = Calculator(10)

# 固定 calc 实例作为 self,得到一个只需要 x 的函数
multiply_by_10 = partial(calc.multiply)
print(multiply_by_10(5))  # 50

这避免了使用 lambda 捕捉 self

配合 map 等高阶函数

nums = [1, 2, 3, 4, 5]
# 使用 partial 将 pow 固定 exponent=2
squared = list(map(partial(pow, exp=2), nums))
print(squared)  # [1, 4, 9, 16, 25]

使用 partialmethod 定义类方法变体

在类中,可以使用 functools.partialmethod 创建方法的偏函数版本,它类似于 partial 但适用于描述符协议。

from functools import partialmethod

class Multiplier:
    def __init__(self, factor):
        self.factor = factor

    def multiply(self, x, y=1):
        return x * y * self.factor

    # 固定 y=2,得到一个默认乘以两倍因子的方法
    double = partialmethod(multiply, y=2)

m = Multiplier(10)
print(m.multiply(3))    # 30
print(m.double(3))      # 60

常见陷阱与注意事项

  1. 参数顺序决定行为
    使用位置参数固定时,这些参数将按顺序传递给原函数,调用时的位置参数将接在后面。

    def sub(a, b):
        return a - b
    
    # 固定 a=5,调用时传入 b
    p = partial(sub, 5)
    print(p(3))  # 2  (5-3)
    
    # 如果你想让调用时的参数作为 a,需要固定关键字参数
    p2 = partial(sub, b=5)
    print(p2(10)) # 5  (10-5)
    
  2. 可变对象作为默认值
    如果固定的参数是可变对象(列表、字典等),需要注意所有偏函数调用共享同一个对象实例。

    def append_to_list(value, target=[]):
        target.append(value)
        return target
    
    partial_append = partial(append_to_list, target=[])
    print(partial_append(1))  # [1]
    print(partial_append(2))  # [1, 2]   <- 不是预期的 [2]
    

    这里 partial 在定义时只创建了一个列表对象,之后调用都修改同一个列表。若要避免,可以使用 lambda 或工厂函数。

  3. partial 对象属性
    偏函数对象保留了原函数和固定参数的引用,可通过 funcargskeywords 属性查看。

    p = partial(pow, 2, exp=3)
    print(p.func)      # <built-in function pow>
    print(p.args)      # (2,)
    print(p.keywords)  # {'exp': 3}
    

总结

  • functools.partial 用于固定函数的某些参数,生成一个更特定的新函数。
  • 它比 lambda 更显式、高效,且更易于序列化和调试。
  • 适用于减少重复参数、适配回调接口、创建默认行为变体等场景。
  • 记住参数的固定顺序与覆盖规则,避免可变对象带来的共享问题。

合理运用偏函数,能让你的 Python 代码更简洁、更具表达力。