反向和反向python3.x OrderedDict有效

时间:2012-08-08 06:38:52

标签: python dictionary python-3.x reverse performance

在多次尝试创建将反转键值对的单行并反转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')])
    '''

这有效,但效率低吗?通过使用reverse(list(a.items()))这会产生很多开销,所以不是pythonic吗?对于inverted_attributes也一样。

重点是避免循环等等,但这会在我们扩大规模时降低性能吗?

2 个答案:

答案 0 :(得分: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只会迭代到它生成新的dict。

两者产生类似的时间。

$ 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

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

  

这有效,但效率低吗?通过使用reverse(list(a.items()))这会产生很多开销,所以不是pythonic吗?对于inverted_attributes也一样。

有些东西可以产生大量的开销,而另一方面是pythonic的东西可以非常高效而且不是非常pythonic,这个术语有点滥用,但这只是我的意见

来自维基百科:

  

Python社区中常见的新词是pythonic,它可以具有与程序风格相关的广泛含义。要说代码是pythonic就是说它很好地使用了Python习语,它很自然或者说流利的语言。同样地,对于一个接口或语言特征来说它是pythonic就是说它适用于Python习语,它的使用与其他语言很好地融合。

  相比之下,unpythonic代码的标志是它试图用Python编写C ++(或Lisp,Perl或Java)代码 - 即提供粗略的转录而不是来自另一种语言的形式的惯用翻译。 pythonicity的概念与Python的极简主义可读性哲学紧密相关,并避免了“有多种方法可行”的方法。难以理解的代码或难以理解的习语是单声道的。

至于:

  

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

这很难说,不知道你为什么要进行这样的转换,或者它们是否是你系统中不可或缺的一部分,从根本上来说,它们正在增加线性时间/空间开销,这可能会或可能不会很好,如果条目的数量仍然很小,那么没有问题,但是如果在每次请求时,假设在Web服务器上发生这种情况,那么你就是在大量的情况下这样做,这可能非常苛刻,并且可能需要重新设计以避免这种情况。 / p>

答案 1 :(得分:0)

在Python 3.x中,最好的方法是避免不必要的中间list。您接近了所有解决方案,但是您总是不必要地使用列表解析或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初始化,没有中间副本(创建tuple vk的反向tuple没关系; CPython优化了这样的固定长度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())) 开销。

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

OrderedDict

从3.5开始的一些时间,其中内置ipython运行得足够快,以至于方法之间的百分比差异实际上有些重要。我正在使用%timeit的{​​{1}}魔术来简化:

# 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