2010-10-11 41 views
3

我希望擁有不變類型,理想情況下,它們可以理清他們自己的散列和相等性,但可以很容易地進行子類化。我開始使用namedtuple允許在Python中進行子類化的不可變類型

class Command(namedtuple('Command', 'cmd_string')): 

    def valid_msg(msg): 
    return True 

    def make_command(msg): 
    if self.valid_msg(msg): 
     return '%s:%s' % (self.cmd_string, msg) 
    else: 
     raise ValueError(INVALID_MSG) 

...但這不適用於繼承。直接繼承這意味着元組的名稱保持不變(印刷...不是什麼大不了的),但更重要的是,你不能添加字段:

class LimitedLengthCommand(Command): 

    # I want to have self.length! Where does it go? 

    def valid_msg(msg): 
    return len(msg) <= self.length 

只需在創建另一個名爲元組(如每個文檔)意味着我不會繼承任何方法!

做這樣的事情最簡單和最簡單的方法是什麼?我打算有Command(例如,十六進制,1或0等)的多個子類,但沒有什麼複雜的。和多重繼承玩好並不是必需的。

+0

我從來沒有做到這一點,它目前給我麻煩,但我知道你通過使用__new__而不是__init__來做任何修改。當我弄明白的時候我會發布。 – aaronasterling 2010-10-11 08:05:42

+0

是的,我知道你可以用'__new__'繼承'tuple',但是我也想要命名的字段。 – detly 2010-10-11 08:07:02

+0

你需要能夠使用issubclass測試它們嗎? – aaronasterling 2010-10-11 08:48:10

回答

1

這裏是一個元類來做你想做的(我認爲)。它通過將要繼承的方法存儲在字典中並將其手動插入到新的類字典中來工作。它還存儲傳遞給namedtuple構造函數的屬性字符串,並將其與子類中的屬性字符串合併。然後它將其傳遞給namedtuple,並返回一個類,該類從其結果的namedtuple中繼承,並在其字典中包含所有適當的方法。因爲元類派生自abc.ABCMeta,所以您可以免費獲得工作類型檢查。以下是如何構建幾個類的樣子:

class Foo(object): 
    __metaclass__ = ImmutableMeta 
    _attributes_ = 'a b' 

    def sayhi(self): 
     print "Hello from {0}".format(type(self).__name__) 

class Bar(Foo): 
    _attributes_ = 'c' 

    def saybye(self): 
     print "Goodbye from {0}".format(type(self).__name__) 

這裏的元類:

import collections as co 
import abc 

class ImmutableMeta(abc.ABCMeta): 

    _classes = {} 

    def __new__(meta, clsname, bases, clsdict): 
     attributes = clsdict.pop('_attributes_') 

     if bases[0] is object: 
      # 'new' class 
      methods = clsdict 
     else: 
      # we're 'inheriting' from an existing class 
      base = bases[0] 
      attributes = meta._classes[base]['attributes'] + ' ' + attributes 
      base_methods = meta._classes[base]['methods'].copy() 
      base_methods.update(clsdict) 
      methods = base_methods 

     # construct the actual base class and create the return class 
     new_base = co.namedtuple(clsname + 'Base', attributes) 
     cls = super(ImmutableMeta, meta).__new__(meta, clsname, (new_base,), 
               methods) 

     # register the data necessary to 'inherit' from the class 
     # and make sure that it passes typechecking 
     meta._classes[cls] = {'attributes': attributes, 
           'methods': methods} 
     if bases[0] is not object: 
      base.register(cls) 
     return cls 

下面是一些微不足道的測試代碼。

a = Foo(1, 2) 
a.sayhi() 

b = Bar(1, 2, 3) 
b.sayhi() # 'inherited' from class Foo 
b.saybye() 

try: 
    b.c = 1   # will raise an AttributeError 
except AttributeError: 
    print "Immutable" 

print "issubclass(Bar, Foo): {0}".format(issubclass(Bar, Foo)) 

try: 
    d = {b: 1}  # No problems 
except TypeError: 
    print "Cant put it in a dict" 
else: 
    print "Can put it in a dict" 

希望有所幫助。如果您不希望將每種方法都附加到應該繼承它的每個類,則還可以提供默認的__getattr__,該元素通過元類字典進行查找並找到適當的方法。這需要以某種方式將基類硬編碼到方法中,可能使用閉包。

+0

對不起,我的時間被劫持了,所以給我幾天,我會嘗試一下。但它看起來像一個很好的解決方案:) – detly 2010-10-13 02:07:11

相關問題