Python函数通过记忆返回不同的结果

2024-06-02 14:22:54 发布

您现在位置:Python中文网/ 问答频道 /正文

如果我使用记忆装饰器,我的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行,并观察输出是不同的。你知道吗

如何让记忆版输出与普通版相同的内容?你知道吗


Tags: 记忆函数nonetargetdatareturnifsteps
1条回答
网友
1楼 · 发布于 2024-06-02 14:22:54

问题是您正在修改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))
# ...

相关问题 更多 >