key = (args, frozenset(kwargs.items()))
这是在不对数据进行任何假设的情况下最好的解决方案。
然而,如果你想要对字典进行记忆化(虽然有些不寻常),你可以特殊处理它。例如,你可以在复制字典的同时递归地应用 frozenset(---.items())
。
如果你使用sorted
,你可能会遇到无法排序的键的糟糕情况。例如,“子集和相等比较不能推广为完整的排序函数。例如,任意两个不交的集合都不相等,也不是彼此的子集,因此以下所有情况都返回False:a<b、a==b或a>b。因此,集合不实现cmp()方法。”
>>> sorted([frozenset({1,2}), frozenset({1,3})])
[frozenset({1, 2}), frozenset({1, 3})]
>>> sorted([frozenset({1,3}), frozenset({1,2})]) # THE SAME
[frozenset({1, 3}), frozenset({1, 2})] # DIFFERENT SORT RESULT
# sorted(stuff) != sorted(reversed(stuff)), if not strictly totally ordered
编辑: Ignacio说:“虽然你不能对任意字典使用sorted(),但kwargs将具有str键。”这是完全正确的。因此,这对于键不是问题,但如果您(或者不太可能的repr)在某种程度上依赖排序,则可能需要记住值。
关于使用str
:
大多数数据都可以正常工作,但在恶意攻击(例如在安全漏洞背景下)的情况下,对抗者可能会创建一个碰撞。这并不容易,因为大多数默认的repr
使用了许多良好的分组和转义。事实上,我无法找到这样的碰撞。但是,使用草率的第三方或不完整的repr
实现可能会出现这种情况。
另外,请考虑以下内容:如果您存储像((<map object at 0x1377d50>,), frozenset(...))
和((<list_iterator object at 0x1377dd0>,<list_iterator object at 0x1377dd0>), frozenset(...))
这样的键,仅通过调用相同的项就会使缓存无限增长。(您可以使用正则表达式解决此问题...)而尝试使用生成器将破坏您正在使用的函数的语义。但是,如果您希望通过is
-样式相等而不是==
-样式相等来进行记忆化,则可能需要这种行为。
此外,在解释器中执行str({1:object()})
之类的操作将每次都返回内存中相同位置的对象!我认为这是垃圾回收器在起作用。这将是灾难性的,因为如果您恰好对<some object at 0x???????>
进行散列,并且稍后由于垃圾回收在同一内存位置创建了相同类型的对象,则将从记忆化函数获得不正确的结果。如上所述,一个可能真正刻意的解决方法是使用正则表达式检测这些对象。
frozenset(kwargs.items())
的排序是否是确定性的? - juanchopanzasorted()
,但 kwargs 将具有字符串键。 - Ignacio Vazquez-Abramskwargs.items()
元组... - juanchopanza