2

在多次尝试创建将反转键值对并反转 OrderedDict 的单行代码之后,我有这个:

    from collections import OrderedDict as OD

    attributes=OD((('brand','asus'), ('os','linux'), ('processor','i5'), ('memory','4G')))
    print(attributes)

    reversed_attributes=OD(reversed(list(attributes.items())))
    print(reversed_attributes)

    inverted_attributes=OD([reversed(item) for item in attributes.items()])
    print(inverted_attributes)

    ''' Prints 
        OrderedDict([('brand', 'asus'), ('os', 'linux'), ('processor', 'i5'), ('memory', '4G')])
        OrderedDict([('memory', '4G'), ('processor', 'i5'), ('os', 'linux'), ('brand', 'asus')])
        OrderedDict([('asus', 'brand'), ('linux', 'os'), ('i5', 'processor'), ('4G', 'memory')])
    '''

这行得通,但是效率低吗?通过使用 reversed(list(a.items())) 这会产生很多开销,所以不是 pythonic 吗?倒置属性也是如此。

关键是要避免 for 循环等,但是随着我们扩大规模,这会降低性能吗?

4

2 回答 2

4

有趣的是我还想出了其他方法。

>>> from collections import OrderedDict as OD
>>> attributes = OD((('brand','asus'), ('os','linux'), ('processor','i5'), ('memory','4G')))

如果你想扭转你可以这样做

>>> reverse = OD(attributes.items()[::-1])

或更 Pythonic 的方法:

>>> reverse = OD(reversed(attributes.items()))

请注意,您不需要创建list项目,它已经是一个列表,而reversed生成器OrderedDict将简单地迭代它来生成新的字典。

两者都产生相似的时间。

$ python -m timeit "from collections import OrderedDict as OD; attributes = OD((('brand','asus'), ('os','linux'), ('processor','i5'), ('memory','4G')))" "reverse = OD(attributes.items()[::-1])"
10000 loops, best of 3: 54.8 usec per loop
$ python -m timeit "from collections import OrderedDict as OD; attributes = OD((('brand','asus'), ('os','linux'), ('processor','i5'), ('memory','4G')))" "reverse = OD(reversed(attributes.items()))"
10000 loops, best of 3: 54.4 usec per loop
$ python -m timeit "from collections import OrderedDict as OD; attributes = OD((('brand','asus'), ('os','linux'), ('processor','i5'), ('memory','4G')))" "reversed_attributes=OD(reversed(list(attributes.items())))"
10000 loops, best of 3: 54.4 usec per loop

如果你想反转:

>>> invert = OD(zip(*zip(*attributes.items())[::-1]))

或更多pythonic:

>>> invert = OD(map(reversed, attributes.items()))

再次产生相似的时间。

$ python -m timeit "from collections import OrderedDict as OD; attributes = OD((('brand','asus'), ('os','linux'), ('processor','i5'), ('memory','4G')));" "invert = OD(zip(*zip(*attributes.items())[::-1]))"
10000 loops, best of 3: 57 usec per loop
$ python -m timeit "from collections import OrderedDict as OD; attributes = OD((('brand','asus'), ('os','linux'), ('processor','i5'), ('memory','4G')));" "invert = OD(map(reversed, attributes.items()))"
10000 loops, best of 3: 56.8 usec per loop
$ python -m timeit "from collections import OrderedDict as OD; attributes = OD((('brand','asus'), ('os','linux'), ('processor','i5'), ('memory','4G')));" "inverted_attributes=OD([reversed(item) for item in attributes.items()])"
10000 loops, best of 3: 55.8 usec per loop

您可以结合使用这两种方法来反转和反转。

这行得通,但是效率低吗?通过使用 reversed(list(a.items())) 这会产生很多开销,所以不是 pythonic 吗?倒置属性也是如此。

另一方面,某些东西可能会产生大量开销并且是pythonic,某些东西可能非常非常有效,但不是很pythonic,这个词有点被滥用了,但这只是我的看法

从维基百科发挥:

Python 社区中一个常见的新词是 pythonic,它可以具有与程序风格相关的广泛含义。说代码是 Pythonic 就是说它很好地使用了 Python 习语,它是自然的或表现出对语言的流利程度。同样,说它是 Pythonic 的接口或语言特性是说它与 Python 习语很好地配合,它的使用与语言的其余部分很好地结合。

相比之下,非 Python 代码的一个标志是它试图用 Python 编写 C++(或 Lisp、Perl 或 Java)代码——也就是说,提供粗略的转录,而不是从另一种语言的形式的惯用翻译。pythonicity 的概念与 Python 的可读性极简主义哲学紧密相连,并避免了“有不止一种方法可以做到”的方法。不可读的代码或难以理解的习语是不合 Python 的。

至于:

但是随着我们扩大规模,这会降低性能吗?

这很难说,不知道你为什么要进行这样的转换,或者它们是否是你系统的一个组成部分,从根本上说,它们会增加线性时间/空间开销,这可能是好的,也可能不是好的,如果数字条目的数量仍然很小,那么没问题,但是如果在每个请求中,假设这发生在 Web 服务器上,您正在对大型 dicts 执行此操作,这可能非常苛刻,可能需要重新设计以避免这种情况。

于 2012-08-08T07:44:34.967 回答
0

在 Python 3.x 中,最好的方法是避免不必要的中间lists。您在所有解决方案中都很接近,但您总是list()不必要地使用列表理解或构造函数。在 Python 3.x 中反转和反转的最 Pythonic 的方法是:

 reversed_and_inverted = OD((v, k) for k, v in reversed(attributes.items()))

虽然这稍微不那么 Pythonic,但更快(渐近):

 reversed_and_inverted = OD(map(reversed, reversed(attributes.items())))

这使用生成器表达式OrderedDict从旧的初始化新的,没有中间副本(创建反向tuplevk无关紧要;CPythontuple像这样优化固定长度 s 的使用以避免malloc/free开销)。

同样,对于只做一个或另一个:

# Remove list() wrapper to save copy
reversed_attributes=OD(reversed(attributes.items()))

# Remove list comprehension brackets to generate into the OrderedDict directly
# Explicitly unpack and reverse key and value (repeated calls to reversed
# built-in invoke expensive LEGB and function call machinery)
inverted_attributes=OD((v, k) for k, v in attributes.items())

# Or faster, but slightly less Pythonic in some people's opinions
inverted_attributes=OD(map(reversed, attributes.items()))

从 3.5 开始的一些时间,内置的OrderedDict运行速度足够快,以至于方法之间的百分比差异实际上有些重要。我正在使用ipython's 的%timeit魔法来简化:

# Make a dict large enough that the differences might actually matter

>>> od1 = OrderedDict(enumerate(string.ascii_uppercase))
>>> %timeit -r5 OrderedDict(reversed(od1.items()))
100000 loops, best of 5: 7.29 μs per loop

# Unnecessary list-ification of items view adds ~15% to run time
>>> %timeit -r5 OrderedDict(reversed(list(od1.items())))
100000 loops, best of 5: 8.35 μs per loop

>>> %timeit -r5 OrderedDict((v, k) for k, v in od1.items())
100000 loops, best of 5: 10 μs per loop

# Surprisingly, runs a little faster as a list comprehension; likely a
# coincidence of construction of OrderedDict from an input of known length
# being optimized to presize the output dict (while lists are optimized for
# reading from unknown length input iterable)
>>> %timeit -r5 OrderedDict([(v, k) for k, v in od1.items()])
100000 loops, best of 5: 9.34 μs per loop

# map produces a generator that knows how long it is if the input also knows
# how long it is, so we can beat either one in this case by avoiding repeated
# lookups of reversed and execution of Python level byte code via:
>>> %timeit -r5 OrderedDict(map(reversed, od1.items()))
100000 loops, best of 5: 8.86 μs per loop
于 2015-10-19T13:55:17.127 回答