在Python 3中使用Python 2 Dict比较 [英] Use Python 2 Dict Comparison in Python 3

查看:112
本文介绍了在Python 3中使用Python 2 Dict比较的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将一些代码从Python 2移植到Python3.这很丑陋,但是我试图使Python 3的结果尽可能与Python 2的结果相同.我有类似的代码:

I'm trying to port some code from Python 2 to Python 3. It's ugly stuff but I'm trying to get the Python 3 results to be as identical to the Python 2 results as possible. I have code similar to this:

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时,出现错误:

When I port this code from Python 2 to Python 3, I get an error:

TypeError: unorderable types: dict() < dict()

所以我将排序行更改为:

So I changed the sorted line to this:

objs = sorted(objs, key=id)

但是字典的顺序在Python 2和Python 3之间仍然发生了变化.

But the ordering of the dictionaries still changed between Python 2 and Python 3.

有没有办法在Python 3中复制Python 2比较逻辑?难道仅仅是id曾经被使用过并且在Python版本之间不可靠吗?

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?

推荐答案

如果要在2.7(使用任意排序顺序)和3.x(拒绝)中与早期版本的Python 2.x相同的行为对字典进行排序),内德·巴切德尔(Ned Batchelder)对排序命令如何工作的问题的答案使您成为其中的一部分,但并非一路走来.

If you want the same behavior as earlier versions of Python 2.x in both 2.7 (which uses an arbitrary sort order instead) and 3.x (which refuses to sort dicts), Ned Batchelder's answer to a question about how sorting dicts works gets you part of the way there, but not all the way.

首先,它为您提供了旧式cmp函数,而不是新式key函数.幸运的是,2.7和3.x均具有 functools.cmp_to_key 解决这个问题. (您当然可以将代码重写为一个关键函数,但这可能会使查看所发布的代码与您的代码之间的任何区别变得更加困难……)

First, it gives you an old-style cmp function, not a new-style key function. Fortunately, both 2.7 and 3.x have functools.cmp_to_key to solve that. (You could of course instead rewrite the code as a key function, but that may make it harder to see any differences between the posted code and your code…)

更重要的是,它不仅在2.7和3.x中没有做同样的事情,甚至在2.7和3.x中都不起作用.要了解原因,请查看代码:

More importantly, it not only doesn't do the same thing in 2.7 and 3.x, it doesn't even work in 2.7 and 3.x. To understand why, look at the code:

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.

Notice that it's calling cmp on the mismatched values.

如果这些命令可以包含其他命令,那是基于这样的事实,即cmp(d1, d2)最终将调用此函数……这在较新的Python中显然是不正确的.

If the dicts can contain other dicts, that's relying on the fact that cmp(d1, d2) is going to end up calling this function… which is obviously not true in newer Python.

最重要的是,在3.x版本中,cmp甚至都不存在.

On top of that, in 3.x cmp doesn't even exist anymore.

此外,这还取决于任何值都可以与任何其他值进行比较的事实-您可以返回任意结果,但不会出现异常.在2.x中确实如此(在少数情况下除外),但在3.x中则并非如此.如果您不希望将字典与不可比较的值进行比较(例如,{1: 2} < {1: 'b'}引发异常是可以的),那么对您来说这可能不是问题.

Also, this relies on the fact that any value can be compared with any other value—you might get back arbitrary results, but you won't get an exception. That was true (except in a few rare cases) in 2.x, but it's not true in 3.x. That may not be a problem for you if you don't want to compare dicts with non-comparable values (e.g., if it's OK for {1: 2} < {1: 'b'} to raise an exception), but otherwise, it is.

当然,如果您不希望将任意结果用于dict比较,您是否真的想要任意结果用于值比较?

And of course if you don't want arbitrary results for dict comparison, do you really want arbitrary results for value comparisons?

解决所有三个问题的方法很简单:您必须替换cmp,而不是调用它.所以,像这样:

The solution to all three problems is simple: you have to replace cmp, instead of calling it. So, something like this:

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使用的不同类型对象的确切规则,请

If you want the exact rules for comparison of objects of different types that 2.7 used, they're documented, so you can implement them. But if you don't need that much detail, you can write something simpler here (or maybe even just not trap the TypeError, if the exception mentioned above is acceptable).

这篇关于在Python 3中使用Python 2 Dict比较的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆