2012-10-10 77 views
10

關於如何在Python中實現枚舉,有幾個問題。大多數解決方案最終會被或多或少相當於是這樣的:沒有枚舉的pythonic設計

class Animal: 
    DOG=1 
    CAT=2 

別人有建議興建枚舉更復雜的方式,但ultimatly的傾向,當一切都說過和做過,看起來像這樣的例子。

基於我在Java和C#中的經驗,我可以想到這樣一個習慣用法的各種用法。然而,它似乎並不是Pythonic。事實上,似乎每當有人問你爲什麼在Python中沒有枚舉時,你往往會發出一些呻吟聲,關於如何沒有理由嘗試和用像Python這樣的語言來強化編譯時類型安全性,或者需要枚舉的設計如何在Python中產生不好的氣味。

我的問題不是如何在Python中實現枚舉,而是一般人如何解決問題的方法,這些問題可以用Pythonic的方式枚舉。換句話說,您如何解決一個問題,該問題有助於將數據類型與一組可能的值分離開來,而無需將Java/C#解決方案移植到Python。

+2

我不明白爲什麼你給的例子是非pythonic。如果我正在實現一個狀態機,我將利用函數作爲對象並將我的狀態定義爲函數。 –

+0

我不知道這個枚舉是如何實現的,但是我只是覺得Python社區中的一些人認爲需要枚舉的解決方案首先是非Pythonic。你的建議是,使用第一類函數作爲狀態來實現狀態機是一個很好的開始,但答案是! – jlund3

回答

4

PEP 435剛剛被接受,它將enum包與Enum類以及其他衍生物(如IntEnum)一起添加到標準庫。這意味着從Python 3.4開始,在設計中使用枚舉的「pythonic」方法就是使用這個包。它會是這個樣子:

>>> from enum import Enum 
>>> class Color(Enum): 
... red = 1 
... green = 2 
... blue = 3 

>>> print(Color.red) 
Color.red 

>>> print(Color.red.name) 
red 

>>>> for color in Color: 
....  print(color) 
Color.red 
Color.green 
Color.blue 

這種設計的主要特點是,鍵可以拒絕比較,它沒有任何意義(不像在其他的答案提出的字符串鍵),允許按鍵都還印刷與鍵值無關,並且通過定義Enum子類上的方法給出鍵值特殊屬性,以及用戶嘗試使用Enum類中的非法鍵時引發顯式和可理解錯誤的屬性。

+0

只有缺點是:在Python 3.4之前是行不通的,到現在爲止(2013年10月)還沒有大的發行版。所以,讓我們等着喝茶。 – nerdoc

+1

用於Python 2.4+的PEP 435的backport可以在PyPI上作爲[enum34](https://pypi.python.org/pypi/enum34/)獲得。雖然它是非官方的,但它是由PEP 435合着者Ethan Furman撰寫的。 – tawmas

1

不是來自C#或Java背景我還是不太明白這個對枚舉的興趣。

根據我的理解,我可能會回到django風格,將任何必要的常量放在設置文件中並在必要時導入。

settings.py/animals.py

DOG = 1 
CAT = 2 

其他文件:

from animals import CAT, DOG 

甚至...

settings.py

ANIMALS = { "DOG": 1, "CAT": 2} 

其他的文件:

from settings import ANIMALS 

my_animal = "FROG" 
if my_animal not in ANIMALS.keys(): 
    print "new species discovered!" 
+2

'如果my_animal不在ANIMALS.keys()中:'實際上(a)更長和(b)效率更低,比簡單檢查'if my_animal not in ANIMALS'。 – Amber

+2

我認爲這個例子強調了爲什麼接觸Java/C#的人想要在Python中枚舉。接受鴨子打字是一回事,凡是知道如何庸俗的東西都可以接受。對於一個Java/C#人來說,這是一個完全不同的事情,說你的語言允許簡單的拼寫錯誤來把你搞砸。例如,假設我輸入my_animal =「dog」而不是my_animal =「DOG」並以某種方式發現了一個新物種?有一個枚舉,你可以說my_animal = Animal.DOG爲你節省點痛苦,因爲你會在運行時得到一個AttributeError在你的臉上,而不是發現的物種錯誤。 – jlund3

+0

的確,我只是想明確地表明正在做什麼,因爲新來者不清楚替代方案正在做什麼。 – monkut

3

因爲Python的字符串是不可變的(和Python實習生他們在其認爲必要的),有沒有一個真正的特定優勢,使用數字枚舉換套的東西。相反,您可以簡單地使用字符串的frozensettuple(取決於您是否在意訂購)。

另一方面,如果你關心的是命名空間,使一個類的東西屬性工作得很好。

正如在評論中提到的,如果你正在尋找類似於狀態機實現的東西,那麼一流的功能就是你的朋友。

+0

..如果你關心命名空間*和*你正在使用3.3,那麼這個配方:http://code.activestate.com/recipes/578279-using-chainmap-for-embedded-namespaces/?in=lang -python –

+1

如果你使用3.3,你可以使用['types.SimpleNamespace'](http://docs.python.org/py3k/whatsnew/3.3.html#simplenamespace):) –

-1

我不相信C#中的枚舉。對於國家機器,你有戰略模式。而不是看對象的狀態並決定要做什麼。封裝對象本身要做什麼的邏輯。

這是Python中的一種自然方法,向我引入枚舉只是鼓勵糟糕的代碼。

+0

這個問題不是專門談論狀態機。我同意在C#和Python枚舉中都不是實現狀態機的方法。然而,我們正在談論的是代表一種具有一小組命名值的類型。對於涉及這種數據類型的問題,枚舉非常有用。 – jlund3

+0

對我來說,這聽起來像繼承。一種具有一小組命名值的類型。同樣,python第一次沒有包含枚舉就正確了。 –

+0

使用繼承來創建具有相同父類的多個數據類型與使用具有一組可能值的單個數據類型是不一樣的... – jlund3