如何测试functools.partial生成预期的函数对象

6 投票
1 回答
2034 浏览
提问于 2025-04-18 00:10

在使用不同的API时,有时候把它们之间相似的关键词对应起来会很有帮助,这样一个控制器API就可以灵活地调用其他库,而不需要用户去处理不同API背后的复杂细节。

假设有一个库叫做 other_api,里面有一个叫 "logarithm" 的方法,而我需要从我的代码中提取出一个关键词参数,比如 "log_base_val";这样我在使用 other_api 时,就需要输入(例如):

other_api.logarithm(log_base_val=math.e)

想象一下一个简单的类,像这样:

import other_api
import math
import functools

class Foo(object):
    _SUPPORTED_ARGS = {"base":"log_base_val"}

    def arg_binder(self, other_api_function_name, **kwargs):
        other_api_function = getattr(other_api, other_api_function_name)
        other_api_kwargs = {_SUPPORTED_ARGS[k]:v for k,v in kwargs.iteritems()}
        return functools.partial(other_api_function, **other_api_kwargs)

Foo 中,我可以把另一个API的参数映射过来,在那个API中这个参数总是叫 base,可以这样做:

f = Foo()
ln = f.arg_binder("logarithm", base=math.e)

ln 在逻辑上等同于(在 kwargs 中使用 log_base_val=math.e,来自 functools):

other_api.logarithm(*args, **kwargs)

不过,手动调用 functools 来绑定同样的参数会导致不同的函数对象:

In [10]: import functools

In [11]: def foo(a, b):
   ....:     return a + b
   ....: 

In [12]: f1 = functools.partial(foo, 2)

In [13]: f2 = functools.partial(foo, 2)

In [14]: id(f1)
Out[14]: 67615304

In [15]: id(f2)
Out[15]: 67615568

所以测试 f1 == f2 的时候不会如预期那样成功:

In [16]: f1 == f2
Out[16]: False

那么问题是:有什么推荐的方法来测试参数绑定的函数是否得到了正确的输出函数对象呢?

1 个回答

8

partial()对象上的func属性是指向原始函数的引用:

f1.func is f2.func

函数对象本身并没有实现__eq__这个方法,所以你可以直接用is来检查它们是否是同一个东西。

同样,partial().argspartial().keywords包含了在调用函数时要传递的参数和关键字参数。

示例:

>>> from functools import partial
>>> def foo(a, b):
...     return a + b
... 
>>> f1 = partial(foo, 2)
>>> f2 = partial(foo, 2)
>>> f1.func is f2.func
True
>>> f1.args
(2,)
>>> f2.args
(2,)
>>> f1.keywords is None
True
>>> f2.keywords is None
True

撰写回答