子类查找

时间:2009-10-20 02:00:03

标签: python python-3.x

(我正在开发Python 3.1,所以如果有一些闪亮的新3.x功能我应该知道这个,请告诉我!)

我有一个类(我们只称它为“Packet”),它作为一组子类的父类,代表了我所拥有的传统客户端 - 服务器协议中的几十种数据包类型中的每一种。无控制。 (这些数据包的行为通常有很大的不同,所以我只给每个人自己的课程让我的生活变得更轻松。)

收到数据包时,我会有一个简单的“调度”功能,它会检查数据包标头以确定类型,然后将其交给知道如何处理它的类。

想要手工维护查找表 - 它不够优雅,只是在寻找麻烦。相反,我想通过检查Packet的所有子类来构建在运行时创建的表,它将具有指定它们对应的数据包类型的类变量,例如:

class Login(Packet):
    type_id = 0x01

当然,我想过关于迭代object.__subclasses__(),但我发现在使用它这样的事情时的安全性和适当性的观点略有不同,包括它是特定于实现的含义,可能不是在CPython以外的地方工作,将来可能会从CPython中消失。我是不是太偏执了? __subclassess__现在被认为是该语言的“真实”部分吗?

如果没有,有什么好的“pythonic”方法可以解决这个问题?

4 个答案:

答案 0 :(得分: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

这应该足以证明“自动发现”从一开始就注定要失败。

正确的解决方案是拥有一个 Factory 类,它体现了正确的子类层次结构,利用了基于 - 手动设计的所有功能。

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 :(得分:3)

__subclasses__是Python语言的一部分,由IronPython和Jython以及CPython实现(现在还没有pypy来测试,但是如果他们破坏了我就会感到惊讶! - ) 。无论是什么给你的印象__subclasses__都有问题?!我看到@gnibbler以同样的方式发表评论,我想挑战一下:您是否可以发布关于__subclasses__不是Python语言关键部分的网址?!

答案 2 :(得分: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 :(得分:1)

我猜你可以使用Python元类(Python≥2.2)在类之间共享这样的信息,这将是非常pythonic。看一下Google Protocol Buffers的实施情况。这是the tutorial显示工作中的元类。顺便说一下,Protocol Buffers的域名与你的相似。