我试图将一些代码从Python 2移植到Python3。这很丑陋,但是我试图使Python 3的结果尽可能与Python 2的结果相同。我有与此类似的代码:
import json # Read a list of json dictionaries by line from file. objs = [] with open('data.txt') as fptr: for line in fptr: objs.append(json.loads(line)) # Give the dictionaries a reliable order. objs = sorted(objs) # Do something externally visible with each dictionary: for obj in objs: do_stuff(obj)
当我将此代码从Python 2移植到Python 3时,出现错误:
TypeError: unorderable types: dict() < dict()
所以我将排序行更改为:
objs = sorted(objs, key=id)
但是字典的顺序在Python 2和Python 3之间仍然发生了变化。
有没有办法在Python 3中复制Python 2比较逻辑?是否只是id以前使用过的方法,在Python版本之间不可靠?
id
如果你想相同的行为在这两个2.7早期版本的Python 2.x的(它使用了一个任意排列顺序,而不是)和3.x(其拒绝某种类型的字典),斯内德尔德的回答一个问题关于如何排序类型的字典工作得您是其中的一部分,但并非始终如此。
首先,它为您提供了旧式cmp功能,而不是新式key功能。幸运的是,2.7和3.x都functools.cmp_to_key必须解决该问题。(您当然可以将代码重写为一个关键函数,但这可能会使查看所发布的代码与您的代码之间的任何区别变得更加困难……)
cmp
key
functools.cmp_to_key
更重要的是,它不仅在2.7和3.x中没有做同样的事情,甚至在2.7和3.x中也 不起作用 。要了解原因,请查看代码:
def smallest_diff_key(A, B): """return the smallest key adiff in A such that A[adiff] != B[bdiff]""" diff_keys = [k for k in A if A.get(k) != B.get(k)] return min(diff_keys) def dict_cmp(A, B): if len(A) != len(B): return cmp(len(A), len(B)) adiff = smallest_diff_key(A, B) bdiff = smallest_diff_key(B, A) if adiff != bdiff: return cmp(adiff, bdiff) return cmp(A[adiff], b[bdiff])
请注意,它正在调用cmp不匹配的值。
如果这些命令可以包含其他命令,则取决于cmp(d1, d2)最终将调用此函数的事实……这在较新的Python中显然不是正确的。
cmp(d1, d2)
最重要的是,在3.xcmp中甚至不存在。
另外,这还取决于任何值都可以与任何其他值进行比较的事实- 您可以返回任意结果,但不会出现异常。在2.x中确实如此(在少数情况下除外),但在3.x中则并非如此。如果您不希望将字典与不可比较的值进行比较(例如,如果可以{1: 2} < {1: 'b'}引发异常),那对您来说可能不是问题,但是相反。
{1: 2} < {1: 'b'}
当然,如果您不希望将任意结果用于dict比较,您是否真的想要任意结果用于值比较?
解决这三个问题的方法很简单:您必须替换cmp,而不是调用它。因此,如下所示:
def mycmp(A, B): if isinstance(A, dict) and isinstance(B, dict): return dict_cmp(A, B) try: return A < B except TypeError: # what goes here depends on how far you want to go for consistency
如果您想要2.7所使用的用于比较不同类型对象的确切规则,则将其记录在案,以便您可以实现它们。但是,如果您不需要那么多细节,则可以在此处编写更简单的内容(TypeError如果可以接受上述异常,甚至可以不捕获)。
TypeError