2009-10-20 56 views
0

(我開發在Python 3.1,所以如果有一些閃亮的新3.X的功能,我應該知道這一點,請讓我知道!)子類查找

我有一個類(我們」我只是稱之爲「數據包」),它作爲一個代表傳統客戶機 - 服務器協議中的幾十個數據包類型中的每一個的子類的父代,我無法控制它。 (這些數據包通常具有非常不同的行爲,所以我只是給他們分配了他們自己的類,以使我的生活更輕鬆。)

當收到一個數據包時,我會有一個簡單的「dispatch」函數,確定類型,然後將其交給知道如何處理它的類。

我做不是想手工維護查找表 - 它不雅,只是要求麻煩。相反,我想在運行時建立的表通過檢查所有數據包的子類,這將有類變量指定的是一包什麼類型它們對應,如:

class Login(Packet): 
    type_id = 0x01 

我想,當然,關於迭代通過object.__subclasses__(),但我發現在這種情況下使用它的安全性和適當性方面略有不同,包括它是特定於實現的並且可能無法在CPython以外的地方使用,並且可能會從CPython中消失未來。我太偏執了嗎? __subclassess__現在是否被認爲是該語言的「真正」部分?

如果不是,那麼採用什麼樣的「pythonic」方法呢?

+0

你是正確的,'__subclasses__'是不是有供您使用,它是一個實現細節 – 2009-10-20 02:42:59

+1

@gnibbler,無論給你的想法?網址。我們完全打算製作'__subclasses__'作爲Python語言的一部分,我想把任何網站都說成是小的碎片...... ;-)。 – 2009-10-20 03:04:01

+0

@Alex,也許我過時了,我以爲'__subclasses__'具有不可能被刪除的狀態。很高興看到它成爲語言的官方部分 – 2009-10-20 03:11:43

回答

3

東西__subclasses__是Python語言的一部分,並通過IronPython和Jython的以及CPython的(無pypy手頭實施現在進行測試,但如果他們打破了這一點,我會感到驚訝!)。無論給你什麼印象__subclasses__都有問題?!我以同樣的方式看到@gnibbler發表的評論,我想挑戰一下:你能發佈有關__subclasses__的網址,它們不是Python語言的重要組成部分嗎?

+0

pypy確實有'__subclasses__' – 2009-10-20 03:13:06

+0

我想這是隻有我誤解這個http://www.python.org/doc/3.1/library/stdtypes.html#class.__subclasses__ – 2009-10-20 03:22:04

+0

@gnibbler,看起來像我 - - 你提到的URL說:「每個新風格的類都保留一個對其直接子類的弱引用列表,這個方法返回所有這些引用仍然存在的列表。」 (而在Python 3中,根本不存在_只有新風格的類,所以文檔當然應該在這裏簡化 - 但所有其他實現都是2.5或更新,所以這不是密切相關的;-)。看起來像Python語言的典型部分** ** ** ...! – 2009-10-20 04:29:39

3

「我想通過檢查所有數據包的子類在運行時建表,」

這是導致無休止的問題保證。這種事情給你的子類帶來了一個奇怪的限制。您不能使用任何抽象超類來簡化事情。

下面是一個具體的例子,說明如果你「自動」發現子類,那將不起作用。

class MessagePacket(object): 
    """superclass. Ignore me, I'm abstract.""" 
class Type_01(MessagePacket): 
    type_id = 0x01 
class Type_023(MessagePacket): 
    """superclass with common features for type 2 and 3. Ignore me, I'm abstract.""" 
class Type_02(Type_023): 
    type_id = 0x02 
class Type_03(Type_023): 
    type_id = 0x03 
class Type_04(MessagePacket): 
    """superclass for subtypes of 4. Ignore me, I'm abstract.""" 
    type_id = 0x04 
class Type_04A(Type_04): 
    discriminator = lambda x: x[23] == 'a' or x[35] == 42 
class Type_04B(Type_04): 
    discriminator = lambda x: True 

這應該足以表明「自動發現」從一開始就註定要失敗。

正確的解決方案是有一個工廠類,體現了正確的子類層次結構,利用基於良好手動設計的所有功能。

class Factory(object): 
    def __init__(self, *subclass_list): 
     self.subclass = dict((s.type_id,s) for s in subclass_list) 
    def parse(self, packet): 
     if packet.type_id == 04: 
      # special subclass logic 
     return self.subclass[packet.type_id](packet) 

它似乎並不過於繁重的負擔,包括以下內容:

factory= Factory(Subclass1, Subclass2, ... SubclassN) 

而當你添加子類,添加到正在使用實際上子類的列表。

+1

+1。但是當您檢查子類可能會導致教育問題時,您能否提供更多示例? – 2009-10-20 04:22:12

1

我想你可以使用Python元類(Python≥2.2)在類之間共享這樣的信息,這將是pythonic。看看谷歌的Protocol Buffers的實施。這裏是the tutorial顯示工作中的元類。順便說一下,協議緩衝區的領域與您的相似。

2

>>> class PacketMeta(type): 
...  def __init__(self,*args,**kw): 
...   if self.type_id is not None: 
...    self.subclasses[self.type_id]=self 
...   return type.__init__(self,*args,**kw) 
... 
>>> class Packet(object): 
...  __metaclass__=PacketMeta 
...  subclasses={} 
...  type_id = None 
... 
>>> class Login(Packet): 
...  type_id = 0x01 
... 
>>> class Logout(Packet): 
...  type_id = 0x02 
... 
>>> 
>>> Packet.subclasses 
{1: <class '__main__.Login'>, 2: <class '__main__.Logout'>} 
>>> 

如果你喜歡使用__subclasses__()你可以像這樣

>>> class Packet(object): 
...  pass 
... 
>>> class Login(Packet): 
...  type_id = 0x01 
... 
>>> class Logout(Packet): 
...  type_id = 0x02 
... 
def packetfactory(packet_id): 
    for x in Packet.__subclasses__(): 
     if x.type_id==packet_id: 
      return x 
... 
>>> packetfactory(0x01) 
<class '__main__.Login'> 
>>> packetfactory(0x02) 
<class '__main__.Logout'> 
+0

我非常喜歡元類的實現。我也用它來做一個非常類似的應用程序。我會改變一些東西: (a)將子類字典的創建移動到元類.__ init__(即,如果不是hasattr(self,'subclasses'):self.subclasses = {})。這保持了封裝的功能。 (b)如果沒有必要,允許Packet類省略type_id。即代替「如果self.type_id不是無」,則使用「如果getattr(self,'type_id',None)不是None」。 這也有同樣的效果,但是實施者犯錯誤的地方更少。 – 2010-03-03 20:45:39