当前位置:  开发笔记 > 编程语言 > 正文

修补函数的__call__

如何解决《修补函数的__call__》经验,为你挑选了2个好方法。

我需要在测试中修补当前的日期时间.我正在使用这个解决方案:

def _utcnow():
    return datetime.datetime.utcnow()


def utcnow():
    """A proxy which can be patched in tests.
    """
    # another level of indirection, because some modules import utcnow
    return _utcnow()

然后在我的测试中我做了类似的事情:

    with mock.patch('***.utils._utcnow', return_value=***):
        ...

但今天我想到了一个想法,我可以通过修补__call__功能utcnow而不是额外的功能来简化实现_utcnow.

这对我不起作用:

    from ***.utils import utcnow
    with mock.patch.object(utcnow, '__call__', return_value=***):
        ...

如何优雅地做到这一点?



1> Michele d'Am..:

[编辑]

也许这个问题最有趣的部分是为什么我无法修补somefunction.__call__

因为函数不使用__call__代码而是__call__(方法包装器对象)使用函数的代码.

我没有找到任何有关这方面的资料来源,但我可以证明它(Python2.7):

>>> def f():
...     return "f"
... 
>>> def g():
...     return "g"
... 
>>> f

>>> f.__call__

>>> g

>>> g.__call__

f代码替换g代码:

>>> f.func_code = g.func_code
>>> f()
'g'
>>> f.__call__()
'g'

当然f,f.__call__参考文献没有改变:

>>> f

>>> f.__call__

__call__改为恢复原始实现和复制引用:

>>> def f():
...     return "f"
... 
>>> f()
'f'
>>> f.__call__ = g.__call__
>>> f()
'f'
>>> f.__call__()
'g'

这对f功能没有任何影响.注意:在Python 3中,您应该使用__code__而不是func_code.

我希望有人能指出我解释这种行为的文档.

你有办法解决这个问题:utils你可以定义

class Utcnow(object):
    def __call__(self):
        return datetime.datetime.utcnow()


utcnow = Utcnow()

现在你的补丁可以像魅力一样工作.


按照我认为是实现测试的最佳方式的原始答案.

我有自己的黄金法则:从不修补受保护的方法.在这种情况下,事情有点平滑,因为保护方法只是为了测试而引入,但我不明白为什么.

这里真正的问题是你不能datetime.datetime.utcnow直接修补(正如你在上面的评论中写的那样是C扩展).你可以做的是datetime通过包装标准行为和覆盖utcnow功能来修补:

>>> with mock.patch("datetime.datetime", mock.Mock(wraps=datetime.datetime, utcnow=mock.Mock(return_value=3))):
...  print(datetime.datetime.utcnow())
... 
3

好吧,这不是很清楚,但你可以介绍自己的功能,如

def mock_utcnow(return_value):
    return mock.Mock(wraps=datetime.datetime, 
                     utcnow=mock.Mock(return_value=return_value)):

现在

mock.patch("datetime.datetime", mock_utcnow(***))

在没有任何其他图层的情况下完成所需的操作,并进行各种导入.

另一种解决方案可以是进口datetimeutils和补丁***.utils.datetime; 这可以让你有一些自由来改变datetime参考实现,而无需改变你的测试(在这种情况下也要注意改变mock_utcnow() wraps参数).



2> zvone..:

修补__call__函数时,您正在设置该实例__call__属性.Python实际上调用了类上定义的方法.__call__

例如:

>>> class A(object):
...     def __call__(self):
...         print 'a'
...
>>> a = A()
>>> a()
a
>>> def b(): print 'b'
...
>>> b()
b
>>> a.__call__ = b
>>> a()
a
>>> a.__call__ = b.__call__
>>> a()
a

分配任何东西a.__call__都是毫无意义的.

然而:

>>> A.__call__ = b.__call__
>>> a()
b

TLDR;

a()不打电话a.__call__.它叫type(a).__call__(a).

链接

有一个很好的解释为什么会出现这种情况来回答"为什么type(x).__enter__(x)而不是x.__enter__()Python标准contextlib?" .

有关特殊方法查找的 Python文档中记录了此行为.


找到另一个更好解释的问题:http://stackoverflow.com/questions/34490998/why-typex-enter-x-instead-of-x-enter-in-python-standard-context
推荐阅读
雨天是最美
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有