2014-02-19 73 views
1

創建的類我正在滾動我自己的Enum類,因爲我無法使__str____repr__正常工作,我做錯了什麼?不能動態綁定__repr __/__ str__到類型

In [2]: x = Enum(X=1, Y=2) 

In [3]: x 
Out[3]: common.utils.Enum 

In [4]: str(x) 
Out[4]: "<class 'common.utils.Enum'>" 

In [5]: x.__repr__ 
Out[5]: <bound method type.reprfun of <class 'common.utils.Enum'>> 

In [6]: x.__repr__() 
Out[6]: 'Enum(Y=2, X=1)' 

代碼本身:

def Enum(*args, **kwargs): 
    enums = dict(zip(args, range(len(args))), **kwargs) 
    def reprfun(self): 
     res = 'Enum(' +\ 
      ', '.join(map(lambda x: '{}={}'.format(x[0],x[1]), enums.items())) +\ 
      ')' 
     return res 

    reverse = dict((value, name) for name, value in enums.items()) 
    typedict = enums.copy() 
    typedict['name'] = reverse 
    instance = type('Enum',(), typedict) 
    instance.__repr__ = types.MethodType(reprfun, instance) 
    instance.__str__ = types.MethodType(reprfun, instance) 
    return instance 
+2

你應該綁定'__repr__'到類,而不是實例。 – Bach

+2

你在使用內建枚舉(http://docs.python.org/3.4/library/enum.html),這是一個還是不同的實現的後端? - 納米 - 我看到你在滾動你自己的。你應該看看python源代碼中的實際枚舉類的實現。 – underrun

回答

5

特殊方法必須被添加到類,而不是實例。任何特殊的方法總是在類型上查找。

如果Python沒有像那樣工作,那麼就不能使用repr(ClassObj),因爲那樣會將self作爲第一個參數,稱爲ClassObj.__repr__方法。所以Python改爲調用type(obj).__repr__(obj)

datamodel documentation

對於新樣式類,特殊的方法調用隱含只能保證,如果一個對象的類型定義,而不是在對象的實例字典才能正常工作。

[...]

這種行爲背後的基本原理在於一些特殊的方法,例如哈希()和再版()由所有對象,包括類型對象實現。

>>> 
>>> 1 .__hash__() == hash(1) 
True 
>>> int.__hash__() == hash(int) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: descriptor '__hash__' of 'int' object needs an argument 

以下工作:

typedict = enums.copy() 
typedict.update({ 
    'name': reverse, 
    '__repr__': reprfun, 
    '__str__': reprfun, 
}) 

instance = type('Enum',(), typedict) 
return instance 

你要小心。如果這些方法隱含查找使用的傳統的查找過程,類型對象本身的調用時會失敗與命名雖然; instance這裏綁定到一個類對象,而不是一個實例。這個名字因此具有誤導性。您可能需要使用clsclassobj或類似的代替instance

演示:

>>> x = Enum(X=1, Y=2) 
>>> x 
<class '__main__.Enum'> 
>>> x.__repr__ 
<unbound method Enum.reprfun> 
>>> x() 
Enum(Y=2, X=1) 
>>> str(x()) 
'Enum(Y=2, X=1)' 
>>> repr(x()) 
'Enum(Y=2, X=1)' 
+0

我嘗試了你的建議,但仍然不起作用,repr沒有調用我提供的函數。 – piotr

+1

@piotr:我測試過,它確實有效。你確定你正在創建你返回的類的*實例嗎? –

+0

所以我調用的實例是類的類型。非常感謝,你可以編輯你的答案,這樣可以減少誤導。 – piotr