python – 如何测试memoized函数?

python – 如何测试memoized函数?,第1张

概述我有一个简单的memoizer,我用来节省昂贵的网络电话的时间.粗略地说,我的代码看起来像这样: # mem.pyimport functoolsimport timedef memoize(fn): """ Decorate a function so that it results are cached in memory. >>> import rando 我有一个简单的memoizer,我用来节省昂贵的网络电话的时间.粗略地说,我的代码看起来像这样:
# mem.pyimport functoolsimport timedef memoize(fn):    """    Decorate a function so that it results are cached in memory.    >>> import random    >>> random.seed(0)    >>> f = lambda x: random.randint(0,10)    >>> [f(1) for _ in range(10)]    [9,8,4,2,5,3,6]    >>> [f(2) for _ in range(10)]    [9,6,10,9]    >>> g = memoize(f)    >>> [g(1) for _ in range(10)]    [3,3]    >>> [g(2) for _ in range(10)]    [8,8]    """    cache = {}    @functools.wraps(fn)    def wrapped(*args,**kwargs):        key = args,tuple(sorted(kwargs))        try:            return cache[key]        except KeyError:            cache[key] = fn(*args,**kwargs)            return cache[key]    return wrappeddef network_call(user_ID):    time.sleep(1)    return 1@memoizedef search(user_ID):    response = network_call(user_ID)    # do stuff to response    return response

我对这段代码进行了测试,在这里我模拟了network_call()的不同返回值,以确保我在search()中做的一些修改按预期工作.

import mockimport [email protected]('mem.network_call')def test_search(mock_network_call):    mock_network_call.return_value = 2    assert mem.search(1) == [email protected]('mem.network_call')def test_search_2(mock_network_call):    mock_network_call.return_value = 3    assert mem.search(1) == 3

但是,当我运行这些测试时,我得到了一个失败,因为search()返回一个缓存的结果.

CAESAR-BAUTISTA:~ caesarbautista$py.test test_mem.py============================= test session starts ==============================platform darwin -- Python 2.7.8 -- py-1.4.26 -- pytest-2.6.4collected 2 itemstest_mem.py .F=================================== FAILURES ===================================________________________________ test_search_2 _________________________________args = (<Magicmock name='network_call' ID='4438999312'>,),keywargs = {}extra_args = [<Magicmock name='network_call' ID='4438999312'>]entered_patchers = [<mock._patch object at 0x108913dd0>]exc_info = (<class '_pytest.assertion.reinterpret.AssertionError'>,AssertionError(u'assert 2 == 3\n +  where 2 = <function search at 0x10893f848>(1)\n +    where <function search at 0x10893f848> = mem.search',<traceback object at 0x1089502d8>)patching = <mock._patch object at 0x108913dd0>arg = <Magicmock name='network_call' ID='4438999312'>    @wraps(func)    def patched(*args,**keywargs):        # don't use a with here (backwards compatability with Python 2.4)        extra_args = []        entered_patchers = []        # can't use try...except...finally because of Python 2.4        # compatibility        exc_info = tuple()        try:            try:                for patching in patched.patchings:                    arg = patching.__enter__()                    entered_patchers.append(patching)                    if patching.attribute_name is not None:                        keywargs.update(arg)                    elif patching.new is DEFAulT:                        extra_args.append(arg)                args += tuple(extra_args)>               return func(*args,**keywargs)/opt/Boxen/homebrew/lib/python2.7/site-packages/mock.py:1201:_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _mock_network_call = <Magicmock name='network_call' ID='4438999312'>    @mock.patch('mem.network_call')    def test_search_2(mock_network_call):        mock_network_call.return_value = 3>       assert mem.search(1) == 3E       assert 2 == 3E        +  where 2 = <function search at 0x10893f848>(1)E        +    where <function search at 0x10893f848> = mem.searchtest_mem.py:15: AssertionError====================== 1 Failed,1 passed in 0.03 seconds ======================

有没有办法测试记忆功能?我考虑了一些替代方案,但它们都有缺点.

一种解决方案是模拟memoize().我不愿意这样做,因为它泄漏了测试的实现细节.从理论上讲,我应该能够在没有系统其他部分的情况下记忆和取消默认功能,包括测试,从功能角度注意.

另一种解决方案是重写代码以公开修饰函数.也就是说,我可以这样做:

def _search(user_ID):    return network_call(user_ID)search = memoize(_search)

然而,这遇到了与上面相同的问题,尽管它可能更糟,因为它不适用于递归函数.

解决方法 是否真的需要在功能级别定义您的memoization?

这有效地使得memoized数据成为一个全局变量(就像函数一样,它的共享范围).

顺便说一下,这就是你在测试时遇到困难的原因!

那么,如何将它包装成一个对象呢?

import functoolsimport timedef memoize(meth):    @functools.wraps(meth)    def wrapped(self,*args,**kwargs):        # Prepare and get reference to cache        attr = "_memo_{0}".format(meth.__name__)        if not hasattr(self,attr):            setattr(self,attr,{})        cache = getattr(self,attr)        # Actual caching        key = args,tuple(sorted(kwargs))        try:            return cache[key]        except KeyError:            cache[key] = meth(self,**kwargs)            return cache[key]    return wrappeddef network_call(user_ID):    print "Was called with: %s" % user_ID    return 1class NetworkEngine(object):    @memoize    def search(self,user_ID):        return network_call(user_ID)if __name__ == "__main__":    e = NetworkEngine()    for v in [1,1,2]:        e.search(v)    NetworkEngine().search(1)

产量:

Was called with: 1Was called with: 2Was called with: 1

换句话说,NetworkEngine的每个实例都有自己的缓存.只需重用相同的一个来共享一个缓存,或者实例化一个新缓存以获得一个新的缓存.

在您的测试代码中,您将使用:

@mock.patch('mem.network_call')def test_search(mock_network_call):    mock_network_call.return_value = 2    assert mem.NetworkEngine().search(1) == 2
总结

以上是内存溢出为你收集整理的python – 如何测试memoized函数?全部内容,希望文章能够帮你解决python – 如何测试memoized函数?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1206479.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-04
下一篇 2022-06-04

发表评论

登录后才能评论

评论列表(0条)

保存