2012-08-08 82 views
2

許多嘗試創建單行將反轉鍵值對,和反向的OrderedDict後,我有這樣的:反轉和反python3.x 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')]) 
    ''' 

這工作,但它是效率低下?通過使用反轉(列表(a.items()))這是否產生了很多開銷,所以不是pythonic? reverse_attributes也一樣。

問題的關鍵在於避免出現循環等問題,但是這會降低性能嗎?

+0

'逆轉(名單(a.items()))'產生很大的開銷,因爲你無需創建一個'list',然後它迭代相反。刪除'list'構造函數會直接反轉'items'(不需要中間複製)。同樣,當初始化新OrderedDict時,你想使用一個生成器表達式(w/o'[]'),而不是列表理解,以避免創建無意義的中間列表。 – ShadowRanger 2015-10-19 13:48:56

回答

3

有趣我也想出了其他方法。

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

,如果你想扭轉你可以做到這一點

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

或更Python的方法:

>>> 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])) 

或更Python:

>>> 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 

您可以結合使用這兩種方法來反轉和反轉。

這有效,但效率不高?通過使用反轉(列表(a.items()))這是否產生了很多開銷,所以不是pythonic? reverse_attributes也一樣。

東西能產生大量的開銷,是對另一方面的東西可以非常非常有效的,而不是很Python的Python的,這個詞已經被有點虐待,但是那只是我的意見

使出wikipedia:

Python社區中的一個常見新詞是pythonic,它可以與程序風格有廣泛的含義。要說代碼是pythonic,就是說它很好地使用了Python成語,它是自然的或者表現出流暢的語言。同樣,說一個接口或語言特性,它是pythonic是說,它適用於Python成語,它的使用與其他語言良好的銜接。

相反,unpythonic代碼的一個標誌是它試圖用Python編寫C++(或Lisp,Perl或Java)代碼 - 也就是說,它提供了一個粗略的轉錄,而不是來自另一種語言的表單的慣用翻譯。pythonicity的概念與Python的極簡主義可讀性哲學緊密相連,並且避免了「有多種方法可行」的方法。無法讀取的代碼或難以理解的習慣用法是不合理的。

爲:

但這種減少表現爲我們擴大規模?

這很難說,不知道爲什麼要做出這樣的變換,或者判斷你的系統的一個組成部分,從根本上裸minimun他們增加線性時間/空間開銷可能會或可能不會如果條目數量仍然很少,那麼沒有問題,但如果在每次請求時,假設這發生在網絡服務器上,那麼您的這些操作是在大量的字符上進行的,這可能會非常嚴酷,並且可能需要重新設計以避免這個。

+1

samy.vilar,非常感謝你的迴應 - 學到了很多東西,並且對zip,map和timeit有很多瞭解。我是一個開始的程序員,所以在這個時候也許不應該擔心什麼是和不是'pythonic'。 – 2012-08-08 08:11:27

+0

@ user1583728沒問題,我希望你不斷學習和享受python :) – 2012-08-08 08:17:45

+0

@SamyVilar:這個問題被標記爲Python 3.x;你的計時測試看起來是在Python 2.x上運行的,這會在行爲上產生巨大的差異(例如'map' /'zip' /'dict.items'等產生中間列表,而不是生成器和視圖。時間將在此基礎上被懷疑 – ShadowRanger 2015-10-19 13:50:43

0

在Python 3.x中,最好的方法是避免不必要的中間list s。你在所有解決方案中都很接近,但你總是不必要地使用列表理解或構造函數。到兩個最Python的方式扭轉和倒置在Python 3.x的將是:

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

而此略少是Python化,但即使是更快(漸近):

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

這使用生成器表達式從舊版本初始化新的OrderedDict,沒有中間副本(創建vk的反轉tuple並不重要; CPython優化了固定長度tuple的使用,以避免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%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 
+0

注意:因爲示例輸入很小,所以定時並不會告訴你很多,特別是在Python 3.5之前,OrderedDict不是內置的(它被實現爲Python定義的類)以及迭代它並構建它的開銷將會消耗大部分其他成本。 – ShadowRanger 2015-10-19 14:00:27