子类查找

0 投票
4 回答
707 浏览
提问于 2025-04-15 15:12

(我正在使用Python 3.1进行开发,如果有什么新的3.x特性我应该知道的,请告诉我!)

我有一个类(我们就叫它“Packet”),它是几个子类的父类,这些子类代表了一个老旧的客户端-服务器协议中的几种数据包类型,而我对这个协议没有控制权。(这些数据包的行为差异很大,所以我给它们每一个都创建了自己的类,这样我工作起来更轻松。)

在接收到一个数据包时,我会有一个简单的“分发”函数,它会检查数据包的头部来确定类型,然后把它交给知道如何处理它的类。

不想手动维护查找表——这样做不优雅,而且很容易出错。相反,我希望在运行时通过检查所有Packet的子类来构建这个表,这些子类会有类变量来指定它们对应的数据包类型,例如:

class Login(Packet):
    type_id = 0x01

我当然考虑过使用object.__subclasses__()来遍历子类,但我发现关于使用它的安全性和适当性有不同的看法,包括它可能是特定于实现的,可能在CPython以外的地方不工作,以及未来可能会从CPython中消失。我是不是太过敏感了?现在__subclasses__算不算是语言的“真正”部分?

如果不是,那有什么好的“Pythonic”方式来处理这个问题呢?

4 个回答

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'>
3

“我想在运行时通过检查所有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 )

而且当你添加子类时,要把它们添加到实际使用的子类列表中。

3

__subclasses__ 是 Python 语言的一部分,它在 IronPython、Jython 和 CPython 中都有实现(目前没有 pypy 可以测试,但我会很惊讶如果他们把这个搞坏了!)。你是从哪里得出 __subclasses__ 有问题的印象的呢?我看到 @gnibbler 也有类似的评论,我想对此提出质疑:你能提供一些链接,证明 __subclasses__ 不是 Python 语言中重要的一部分吗?

撰写回答