Python偏函数partial的用法小结

Written by

in

文章目录
  • functools.partial(func, /, *args, **keywords) 会返回一个新可调用对象,它把原函数 func 的部分位置参数和/或关键字参数“预先绑定”。这样你就能得到一个“定制版”的函数,后续只需要补齐剩余参数即可调用。 返回对象类型是 functools.partial 实例,但和函数用法相同(可调用)。 它拥有属性:p.func(原函数)、p.args(预绑定位置参数)、p.keywords(预绑定关键字参数)。
  • 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 给了多个值(不允许)
  • 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
  • 这在 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)) 也很实用(用来把行号/模型索引等传给槽函数)。
  • 用 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())
  • 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))
  • 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!!!
  • 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 才能正确作为方法(描述符)工作。
  • 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 的元数据
  • 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())
  • from functools import partial print_info = partial(print, “[INFO]”) # 固定前缀 print_warn = partial(print, “[WARN]”) print_info(“system started”) print_warn(“disk almost full”)
  • 不能使用“占位符”来跳过中间某个位置参数(stdlib 没有这个特性)。需要时用关键字参数或 lambda 重排。 重复提供同名位置参数会报错(“给了多个值”);重复关键字后者覆盖前者。 不要把可变对象当作“默认值状态”去修改(例如绑定 list 后在原函数里修改它),除非你就是有意为之;partial 持有的引用和普通默认参数一样需要小心共享状态。 在框架/回调里,优先用 partial 传递额外上下文,比 lambda 更“可调试/可序列化”。
  • partial:给函数“预装参数”,写出更简洁的 API/回调。 partialmethod:在类中定义“半绑定方法”,正确处理 self。 常见场景:回调传参、并发任务/进程池、排序/映射的柯里化、日志前缀、装饰器参数化。 到此这篇关于Python偏函数partial的用法小结的文章就介绍到这了,更多相关Python偏函数partial内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: Python利用partial偏函数生成不同的聚合函数 详解Python中的偏函数(Partial Functions) python偏函数partial用法 Python偏函数Partial function使用方法实例详解 Python使用functools模块中的partial函数生成偏函数 python中偏函数partial用法实例分析
  • 目录
    • 什么是partial?
    • 1) 基本用法与参数合并规则
    • 2) 配合标准库:map/sorted/reduce等“柯里化”场景
    • 3) 回调函数需要“额外上下文”——用partial传额外参数
    • 4) 装饰器/工厂的参数化:让“可调用签名更好看”
    • 5)partialvslambda:各有优劣
    • 6) 深入属性与调试
    • 7) 与实例方法的细节:partialvspartialmethod
    • 8) 与functools.update_wrapper的配合(可选)
    • 9) 与并发库(concurrent.futures/multiprocessing)的实战
    • 10) 在日志/打印等“固定上下文”的场景
    • 11) 常见误区与最佳实践
    • 小结

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

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

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

    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 给了多个值(不允许)
    

    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
    

    这在 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)) 也很实用(用来把行号/模型索引等传给槽函数)。

    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())
    

    • 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))
    

    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!!!
    

    • 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 才能正确作为方法(描述符)工作。

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

    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())
    

    from functools import partial
    
    print_info = partial(print, "[INFO]")      # 固定前缀
    print_warn = partial(print, "[WARN]")
    print_info("system started")
    print_warn("disk almost full")
    

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

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

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

    您可能感兴趣的文章:

    • Python利用partial偏函数生成不同的聚合函数
    • 详解Python中的偏函数(Partial Functions)
    • python偏函数partial用法
    • Python偏函数Partial function使用方法实例详解
    • Python使用functools模块中的partial函数生成偏函数
    • python中偏函数partial用法实例分析

    站内搜索