首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Python函数使用记忆化返回不同的结果

Python函数使用记忆化返回不同的结果
EN

Stack Overflow用户
提问于 2018-07-30 06:40:02
回答 1查看 230关注 0票数 0

如果我使用一个内存装饰器,我的Python寻径函数会返回不同的结果。它自己返回一个正确的值,但在被记忆之后,它返回了一个不正确的值。

我正在讨论的函数如下所示:

@functools.lru_cache(maxsize=None)
def recursiveTraversal(originIndex, target, steps):
    startingVertex = data[originIndex]

    if startingVertex["ID"] == target:
        if steps == 0:
            return Path(0, [])
        else:
            return None
    else:
        if steps == 0:
            return None
        else:
            potentialPaths = []
            for i in startingVertex["Edges"]:
                nextVertex = data[i]
                nextPath = recursiveTraversal(i, target, steps - 1)
                if nextPath == None:
                    continue
                nextPath.weight += int(nextVertex["Weight"])
                nextPath.vertices.append(i)

                potentialPaths.append(nextPath)
            if len(potentialPaths) > 0:
                minPath = min(potentialPaths, key=lambda x: x.weight)
                return minPath
            else:
                return None

一个完整的可运行的示例can be found here。文件的上半部分是所有数据,代码在底部。要重现这种情况,只需注释掉第15行,并观察到输出是不同的。

如何让memoized版本输出与普通版本相同的内容?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-07-30 09:31:58

问题是您正在修改recursiveTraversal的返回值的属性。此函数返回Path对象,您可以修改它们的属性weightvertices。因此,对于非缓存版本,每次使用(x, y, z)参数调用函数时,都会创建一个新的Path(0, [])对象,并且稍后会在for循环中修改它的属性。但是对于每个(x, y, z)调用,您都可以确保从一个新的对象开始。现在,对于缓存版本,缓存包装器只提供以前创建的Path对象的实例(它已经具有修改过的weightvertices属性),并且这些对象会被进一步修改(即,这会修改缓存),而不是通过向下遍历递归树来提供新的对象。这可以从下面的示例中看出:

# Augment `Path` class with `__repr__`.
class Path:
    # Other methods go here.

    def __repr__(self):
        return '{}({}, {})'.format(self.__class__.__name__, repr(self.weight), repr(self.vertices))

data = [
    {'ID': '2', 'Weight': 1, 'Edges': [1]},
    {'ID': '1', 'Weight': 1, 'Edges': []}
]

print(recursiveTraversal(0, '1', 1))  # Prints "Path(1, [1])".
print(recursiveTraversal(1, '1', 0))  # Prints "Path(1, [1])".

检查函数recursiveTraversal似乎是,对于steps=0,它应该返回Path(0, []),以防目标匹配。不过,它会返回Path(1, [1])。这是因为前面对recursiveTraversal的调用已经调用了recursiveTraversal(1, '1', 0)并修改了结果的weightvertices属性。当执行对recursiveTraversal(1, '1', 0)的第二次显式调用时,您将获得对该对象的缓存引用。

可能的解决方案

一种可能的解决方案是在进一步修改它们之前创建缓存对象的副本。这可以防止缓存被修改。

from copy import deepcopy

# Within `recursiveTraversal`:
# ...
nextPath = deepcopy(recursiveTraversal(i, target, steps - 1))
# ...
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51585130

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档