(我正在开发Python 3.1,所以如果有一些闪亮的新3.x功能我应该知道这个,请告诉我!)
我有一个类(我们只称它为“Packet”),它作为一组子类的父类,代表了我所拥有的传统客户端 - 服务器协议中的几十种数据包类型中的每一种。无控制。 (这些数据包的行为通常有很大的不同,所以我只给每个人自己的课程让我的生活变得更轻松。)
收到数据包时,我会有一个简单的“调度”功能,它会检查数据包标头以确定类型,然后将其交给知道如何处理它的类。
我不想要手工维护查找表 - 它不够优雅,只是在寻找麻烦。相反,我想通过检查Packet的所有子类来构建在运行时创建的表,它将具有指定它们对应的数据包类型的类变量,例如:
class Login(Packet):
type_id = 0x01
当然,我想过关于迭代object.__subclasses__()
,但我发现在使用它这样的事情时的安全性和适当性的观点略有不同,包括它是特定于实现的含义,可能不是在CPython以外的地方工作,将来可能会从CPython中消失。我是不是太偏执了? __subclassess__现在被认为是该语言的“真实”部分吗?
如果没有,有什么好的“pythonic”方法可以解决这个问题?
答案 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的域名与你的相似。