子类查找
(我正在使用Python 3.1进行开发,如果有什么新的3.x特性我应该知道的,请告诉我!)
我有一个类(我们就叫它“Packet”),它是几个子类的父类,这些子类代表了一个老旧的客户端-服务器协议中的几种数据包类型,而我对这个协议没有控制权。(这些数据包的行为差异很大,所以我给它们每一个都创建了自己的类,这样我工作起来更轻松。)
在接收到一个数据包时,我会有一个简单的“分发”函数,它会检查数据包的头部来确定类型,然后把它交给知道如何处理它的类。
我不想手动维护查找表——这样做不优雅,而且很容易出错。相反,我希望在运行时通过检查所有Packet的子类来构建这个表,这些子类会有类变量来指定它们对应的数据包类型,例如:
class Login(Packet):
type_id = 0x01
我当然考虑过使用object.__subclasses__()
来遍历子类,但我发现关于使用它的安全性和适当性有不同的看法,包括它可能是特定于实现的,可能在CPython以外的地方不工作,以及未来可能会从CPython中消失。我是不是太过敏感了?现在__subclasses__
算不算是语言的“真正”部分?
如果不是,那有什么好的“Pythonic”方式来处理这个问题呢?
4 个回答
>>> 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'>
“我想在运行时通过检查所有Packet的子类来构建这个表。”
这样做肯定会引发无尽的问题。这种做法对你的子类施加了奇怪的限制。你不能使用任何抽象的父类来简化事情。
这里有一个具体的例子,说明如果你“自动”发现子类,会出现什么问题。
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 )
而且当你添加子类时,要把它们添加到实际使用的子类列表中。
__subclasses__
是 Python 语言的一部分,它在 IronPython、Jython 和 CPython 中都有实现(目前没有 pypy 可以测试,但我会很惊讶如果他们把这个搞坏了!)。你是从哪里得出 __subclasses__
有问题的印象的呢?我看到 @gnibbler 也有类似的评论,我想对此提出质疑:你能提供一些链接,证明 __subclasses__
不是 Python 语言中重要的一部分吗?