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

ely*_*ely 4 python unit-testing arguments functools

当从一个API转到另一个API时,有时可以帮助在每个API中的相似关键字之间进行映射,允许一个控制器API灵活地分派到其他库,而无需用户在引擎盖下使用不同的API.

假设某个库other_api有一个名为的方法"logarithm",而基础的关键字参数是我需要从我的代码中分解出来的,比如"log_base_val"; 所以要使用它other_api我需要输入(例如):

other_api.logarithm(log_base_val=math.e)
Run Code Online (Sandbox Code Playgroud)

考虑像这样的玩具类:

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)
Run Code Online (Sandbox Code Playgroud)

有了Foo,我可以映射一些其他API,其中始终调用此参数base,如下所示:

f = Foo()
ln = f.arg_binder("logarithm", base=math.e)
Run Code Online (Sandbox Code Playgroud)

并且ln在逻辑上等同于(with log_base_val=math.ein kwargs,from functools):

other_api.logarithm(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

但是,通过调用手动创建相同的参数绑定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
Run Code Online (Sandbox Code Playgroud)

因此,测试f1 == f2不会按预期成功:

In [16]: f1 == f2
Out[16]: False
Run Code Online (Sandbox Code Playgroud)

所以问题是:测试参数绑定函数是否导致正确的输出函数对象的规定方法是什么?

Mar*_*ers 6

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

f1.func is f2.func
Run Code Online (Sandbox Code Playgroud)

函数对象本身不实现__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
Run Code Online (Sandbox Code Playgroud)