【问题标题】:How to test that functools.partial produces the expected function object如何测试 functools.partial 生成预期的函数对象
【发布时间】:2014-03-27 16:23:15
【问题描述】:

当从一个 API 转换到另一个 API 时,有时在每个 API 中的相似关键字之间进行映射会很有帮助,允许一个控制器 API 灵活地分派到其他库,而无需用户为不同 API 的底层操心。

假设某个库 other_api 有一个名为 "logarithm" 的方法,并且我需要从代码中考虑到 base 的关键字参数,例如 "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,其中该参数始终称为base,如下所示:

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

并且ln 逻辑上等价于(log_base_val=math.ekwargs,来自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

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

【问题讨论】:

    标签: python unit-testing arguments functools


    【解决方案1】:

    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
    

    【讨论】:

    • 我是否需要使用inspect 来获得任何绑定的kwargs,所以两个partial 对象之间的“平等”是这个测试和-ed 以及通过inspect 对kwargs 的测试吗?
    • @EMS:不,.args.keywords 属性可用于此。
    猜你喜欢
    • 2015-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-21
    • 1970-01-01
    相关资源
    最近更新 更多