2014-09-04 18 views
3

我试图将一些代码从Python 2移植到Python 3.它是丑陋的东西,但我试图让Python 3的结果与Python 2的结果一样可能。我有类似的代码如下:在Python中使用Python 2 Dict比较3

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的

之间还改变了字典的排序是有办法REPLI在Python 3中指定Python 2比较逻辑?简单地说,之前使用了id,并且在Python版本之间不可靠?

+0

您是否需要在3中复制Python 2的逻辑,或者您是否只需要*一些*这两者之间的一致性排序? – DSM 2014-09-04 21:44:01

+0

'如果你只关心关键比较,'sorted(objs,key = sorted)'有效。 (并在2和3中实现它,真的没有办法匹配3的任意排序) – roippi 2014-09-04 21:49:10

+0

我想复制顺序。 – 2014-09-04 21:49:26

回答

3

如果您希望在2.7(使用任意排序顺序)和3.x(拒绝排序字符串)中使用与Python 2.x早期版本相同的行为,Ned Batchelder's answer to a question about how sorting dicts works可以帮助您实现这一目标,但不是全部。


首先,它给你一个旧式cmp功能,并不是一个新的风格key功能。幸运的是,2.7和3.x都有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中显然不是真的。

最重要的是,3.x cmp已经不存在了。另外,这依赖于任何值都可以与任何其他值进行比较的事实 - 您可能会返回任意结果,但您不会得到异常。在2.x中这是事实(除少数情况外),但在3.x中并非如此。如果您不想比较字典与非可比值(例如,如果{1: 2} < {1: 'b'}可以引发异常),那么这对您来说可能不是问题,否则就是这样。

当然,如果你不想要任意结果的字典比较,你真的想要任意结果的价值比较?

解决所有这三个问题很简单:您必须更换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,they're documented对象的比较确切的规则,这样你就可以实现它们。但是,如果你不需要那么多的细节,你可以在这里写一些简单的东西(或者,如果上面提到的例外是可以接受的,那么甚至可以不捕获TypeError)。

0

Is there a way to replicate the Python 2 comparison logic in Python 3? Is it simply that id was used before and is not reliable between Python versions?

id永远不可靠。你得到的任何给定对象的id是一个完全任意的值;即使在相同的机器和Python版本上,它也可能不同于一次运行。

Python 2.x实际上并未记录它按id排序。 All it says是:

Outcomes other than equality are resolved consistently, but are not otherwise defined.

但是,这只是使点甚至更好:订单被明确定义为arbitary(除了是任何给定的运行过程中保持一致)。这与在Python 3.x中使用key=id进行排序所得到的结果完全相同,不管它是否以相同的方式工作。*

所以你在3.x中做同样的事情。两个任意顺序不同的事实仅仅意味着任意是任意的。


如果你想要某种重复排序的基于它所包含的字典,你只需要决定的顺序是什么,然后你就可以建造它。例如,您可以按顺序对项目进行排序,然后进行比较(如果项目是或包含字典,则递归地传递相同的键功能)。**

而且,设计并实施了某种合理的,任意排序,它当然会在2.7和3.x中以相同的方式工作。


*请注意,这是等价的身份比较,只限于购买比较。如果您只是将它用于sorted,这会导致您的排序不再稳定。但由于无论如何都是以任意顺序进行的,所以这并不重要。

**请注意,Python 2.x用于使用类似于此的规则。从脚注到上述:“Python的早期版本使用排序(键值)列表的字典式比较,但这对于比较平等的常见情况来说非常昂贵。”所以,这告诉你这是一个合理的规则 - 只要它实际上是你想要的规则,并且你不介意性能成本。

+0

你在“递归地传递相同的关键函数”时丢失了我“我该怎么做? – 2014-09-04 21:56:32

+0

@AmyForbes:你的关键函数必须以某种适合值的方式转换所有的值;对于本身就是字典的值来说,关键函数本身就是实现这一目标的方法。 – abarnert 2014-09-04 22:08:23

0

CPython2.x中的逻辑有些复杂,因为行为是由dict.__cmp__决定的。 python实现可以在here找到。

但是,如果你真的想要一个可靠的排序,你需要排序一个比id更好的密钥。你可能使用functools.cmp_to_key将链接答案的比较函数转换为关键函数,但实际上,它不是一个好顺序,因为它完全是任意的。

最好的办法是按字段的值(或多个字段)对所有字典进行排序。 operator.itemgetter可以很好地用于此目的。使用这个作为关键函数应该可以为任何有点现代的实现和python版本提供一致的结果。

+0

我尝试使用您链接到的Python实现,但未定义“cmp”。在递归的情况下,值是一个不成文的字典,会发生什么? – 2014-09-04 21:55:10

+0

@AmyForbes:'cmp'在3.x中不再存在,但可以根据[2.x文档](https://docs.python.org/2/library/functions .html#cmp):'如果x == y else返回0(如果x abarnert 2014-09-04 21:58:00

+0

@AmyForbes:但该代码将与递归情况打破... – abarnert 2014-09-04 21:58:36

0

如果你只需要简单说是潜在的不同平台不同的Python的多次运行一致的,但实际上并不关心实际订单再简单的办法就是对它们进行排序之前转储类型的字典,以JSON命令:

import json 

def sort_as_json(dicts): 
    return sorted(dicts, key=json.dumps) 

print(list(sort_as_json([{'foo': 'bar'}, {1: 2}]))) 
# Prints [{1: 2}, {'foo': 'bar'}] 

显然,这只适用于你的字典是JSON可表示的,但是因为你从JSON加载它们,这应该是没有问题的。在你的情况下,你可以通过简单地对反序列化JSON之前加载对象的文件进行排序来获得相同的结果。