2016-01-13 32 views
1

我需要使用Python 3到一個列表排序,有可能是stringsintegersfloatstuplesPython的排序不同類型的列表

我目前正在使用key參數一樣做出正確使用sort功能這

data.sort(key=gen_key) 

... 

def gen_key(self, value): 
     if is_number(value): 
      return str(value) 

     if isinstance(value, str): 
      return value 
    return '___' + type(value).__name__ 

但問題是,數字現在將自然排序。雖然我想命令數字和浮動仍然像數字和浮動,而不是將它們作爲字符串進行威脅。

該行爲是由return str(value)部分引起的。但我不能返回不同的類型的字符串,因爲這將引發異常,如蟒蛇3串不會用數字排序的,就像他們在蟒蛇2.做的異常以下

unordarable types: int() < str() 

有什麼建議?

+2

你期待什麼結果?你期望如何排序字符串和元組? – jprockbelly

+1

你會怎麼樣?''''與'13'排序?您需要提出明確的排序順序。一旦你完成了,你幾乎已經完成了。 –

回答

2

執行此操作最簡單的方法是將其用作排序鍵的對象,該對象在其比較方法中包含所需的排序行爲。 Python排序所需的唯一比較方法是__lt__(),所以這相當簡單。例如,下面是一個大致實現了Python 2排序啓發式(在具有可比性的對象組內進行排序)的類。你當然可以實施你喜歡的任何其他規則。由於排序將爲列表中的每個項目創建這些對象中的一個,我通過使用__slots__和實習所有類型字符串來保持每個對象的大小盡可能低。

from sys import intern 

class Py2Key: 

    __slots__ = ("value", "typestr") 

    def __init__(self, value): 
     self.value = value 
     self.typestr = intern(type(value).__name__) 

    def __lt__(self, other): 
     try: 
      return self.value < other.value 
     except TypeError: 
      return self.typestr < other.typestr 

用法:

seq = ["Z", 3, "Y", 1, "X", 2.5, False] 
sorted(seq, key=Py2Key) 
>>> [False, 1, 2.5, 3, 'X', 'Y', 'Z'] 

不幸的是,實現的Python 2的在Python 3排序行爲將是緩慢和內存密集型比Python 2,特別是因爲我們正在採取例外的優勢處理。您的應用程序是否可以接受取決於您。

2

訣竅是讓你的key函數在第一個索引中返回一個保證可比類型的元組,並在後續索引中返回不同類型的元組。

雖然不是100%相同的都是Python 2呢,爲你的具體情況「號碼前,一切由類型名稱相比,」你可以用一個合理有效key功能做到這一點:

>>> from numbers import Number 
>>> seq = ['Z', 3, 'Y', 1, 'X', 2.5, False, (1, 2), [2, 3], None] 
>>> sorted(seq, key=lambda x: (x is not None, '' if isinstance(x, Number) else type(x).__name__, x)) 
[None, False, 1, 2.5, 3, [2, 3], 'X', 'Y', 'Z', (1, 2)] 

這裏的key函數使得key的第一個元素簡單地爲bool,迫使None在其他所有事情(Py2做同樣的事情)之前排序,然後通過使用空字符串爲鍵的第二部分首先排序所有數字類型,其中一切都使用他們的類型名稱(也像Py2)。一旦你超過了前兩個指數,剩下的就是相同的類型,並且應該比較好。

這裏的主要缺陷是setfrozenset等可比較的非數字類型不會相互比較,它們將僅按類型名排序(使用異常的自定義鍵類可以處理此問題)。

它也不會處理遞歸的情況;如果序列包含[2, 3]['a', 'b'],它將有一個TypeError比較2'a',但是沒有什麼可以用一個可笑的關鍵類來處理。

如果這不是問題,這是運行便宜,相對簡單。

不同於涉及的自定義類與定義執行比較__lt__解決方案,該方法具有產生內置鍵,其與所述排序中的Python的高級代碼的最小有效地執行比較的優點。

時序:

# Multiply out the sequence so log n factor in n log n work counts for something 
>>> seq = ['Z', 3, 'Y', 1, 'X', 2.5, False, (1, 2), [2, 3], None] * 100 

# Verify equivalence 
>>> sorted(seq, key=Py2Key) == sorted(seq, key=lambda x: (x is not None, '' if isinstance(x, Number) else type(x).__name__, x)) 
True 

# Timings in seconds for the fastest time (of 3 trials) to run the sort 1000 times: 
>>> import timeit 

# Py2Key class 
>>> min(timeit.repeat('sorted(seq, key=Py2Key)', 'from __main__ import seq, Py2Key', number=1000)) 
5.251885865057375 

>>> min(timeit.repeat('sorted(seq, key=lambda x: (x is not None, "" if isinstance(x, Number) else type(x).__name__, x))', 'from __main__ import seq, Number', number=1000)) 
1.9556877178131344 

基本上,避免動態的Python級__lt__的開銷是由剛剛超過60%減少的運行時間。它似乎沒有算法上的改進(一個seq具有相同的運行時間比率的100倍),只是減少了固定開銷,但這是一個不小的縮減。