2012-10-19 26 views
5

當工作基本上是一個自定義枚舉類型的實現,我遇到了在那裏出現我,因爲他們在Python是不同的類別從兩個intlong獲得獨立又幾乎相同的子類的情況。這似乎有點諷刺意味,因爲它們的實例通常可以互換使用,因爲它們大部分都是在需要時自動創建的。避免有兩個不同的數字子類(int和long)?

我的工作很好,但在乾的精神(不要重複自己),我不禁要問,如果沒有更好的,或至少更簡潔的方式來實現這一點。目標是讓子類可以在任何地方使用 - 或儘可能接近 - 可以使用它們的基類的實例。理想情況下,這應該自動發生,類似於內置的int()實際上返回long時,只要它檢測到需要。

這裏是我當前的實現:

​​

回答

2

這裏是你如何可以通過多重繼承解決DRY問題。不幸的是,它不能很好地與__slots__(它導致編譯時TypeError s),所以我不得不離開了。希望__dict__值不會爲您的用例浪費太多內存。

class Named(object): 
    """Named object mix-in. Not useable directly.""" 
    def __setattr__(self, name, value): 
     if hasattr(self, name): 
      raise AttributeError(
       "%r object attribute %r is read-only" % 
       (self.__class__.__name__, name)) 
     else: 
      raise AttributeError(
       "Cannot add attribute %r to %r object" % 
       (name, self.__class__.__name__)) 

    def __new__(cls, name, *args): 
     self = super(Named, cls).__new__(cls, *args) 
     super(Named, self).__setattr__('_name', name) 
     return self 

    def __str__(self): # override string conversion to be name 
     return self._name 

    __repr__ = __str__ 

class NamedInt(Named, int): 
    """NamedInt class. Constructor will return a NamedLong if value is big.""" 
    def __new__(cls, name, *args): 
     value = int(*args) # will raise an exception on invalid arguments 
     if isinstance(value, int): 
      return super(NamedInt, cls).__new__(cls, name, value) 
     elif isinstance(value, long): 
      return NamedLong(name, value) 

class NamedLong(Named, long): 
    """Nothing to see here.""" 
    pass 
+0

很好的答案,但不處理'NamedInt('HexBased','deadbeef',16)'。 – martineau

+0

嗯,好點。我認爲它可以用可變參數修復。我會編輯來做到這一點。 – Blckknght

+0

感謝您的修復。在這個和@ekksun的回答之間很難做出決定,因爲兩者都很好地解決了DRY問題 - 但最終選擇了這一個,因爲它是最直接,最容易理解的恕我直言。順便說一句,有可能爲'NamedInt'子類添加'__slots__'屬性(正如eryksun所做的那樣),這似乎滿足了對可能更常見的'int'情況的需求(並且_is_是用於預期用途的重要特徵)。 – martineau

2

重寫the allocator會讓你返回適當類型的對象。

class NamedInt(int): 
    def __new__(...): 
    if should_be_NamedLong(...): 
     return NamedLong(...) 
    ... 
+0

誠然,這將消除對'NamedWholeNumber'類的需要(也可能是值得做的事),但似乎大部分的代碼已被在它最終會移動到'NamedInt .__ new __()'方法,我仍然需要一個單獨的'NamedLong'subclass - 或者我錯過了什麼? – martineau

+0

這聽起來是正確的,但請記住,無論傳遞給int()的值是多少,Python 2.x都會分開'int'和'long'類。 –

2

這裏的一類裝飾版本:

def named_number(Named): 

    @staticmethod 
    def __new__(cls, name, value, base=None): 
     value = int(value) if base is None else int(value, base) 
     if isinstance(value, int): 
      NamedNumber = Named # NamedInt/NamedLong 
     else: 
      NamedNumber = cls = NamedLong 
     self = super(NamedNumber, cls).__new__(cls, value) 
     super(NamedNumber, self).__setattr__('_name', name) 
     return self 

    def __setattr__(self, name, value): 
     if hasattr(self, name): 
      raise AttributeError(
       "'%r' object attribute %r is read-only" % (Named, name)) 
     else: 
      raise AttributeError(
       "Cannot add attribute %r to '%r' object" % (name, Named)) 

    def __repr__(self): 
     return self._name 

    __str__ = __repr__ 

    for k, v in locals().items(): 
     if k != 'Named': 
      setattr(Named, k, v) 

    return Named 

@named_number 
class NamedInt(int): 
    __slots__ = '_name' 

@named_number 
class NamedLong(long): pass 
+0

絕對領先的包恕我直言。我試圖在它和@ Blckknght之間做出決定。我喜歡這樣一個事實,即你的支持'__slots__'優化,非基於十進制的字符串值,只需要單一繼承。 – martineau

+0

不幸的是我已經決定和@ Blckknght的回答一起,因爲我在其中添加了一條評論。這是一個艱難的選擇,然而,儘管如此,你的方法似乎完全可行,而且大多數措施都是好的。我還發現你實現類裝飾器的方法非常聰明 - 並從中學到了一些新技術。謝謝! – martineau

相關問題