如何从超类中捕获的异常创建子类实例?

时间:2014-03-13 10:26:34

标签: python oop inheritance

这是我想要实现的目标:

  1. 通过将数据传递给超类SKBusPacket来创建数据包对象。
  2. __init__()中确定数据包的子类型,并根据此数据包的子类型引发异常("请求"或"响应"子类型。
  3. 捕获此异常,然后将数据传递给子类SKBusRequestPacketSKBusResponsePacket
  4. 问题:创建子类的实例将一次又一次地引发上述异常,因为我通过super将初始化args传递给超类(参见代码)。

    建议的解决方案:使用raise检查实例是否是其中一个子类,以避免isinstance()。但我的印象是,这是一种不好的做法,而且不是pythonic。

    我的简化代码:

    class SKBusPacket(Packet):
        def __init__(self, *args, **kwargs):
            super(SKBusPacket, self).__init__(*args, **kwargs)
    
            self.packet_type = self.getSkbPacketType()
    
            if self.packet_type == 'request':
                raise ActuallySKBusReqPacket
            elif self.packet_type == 'response':
                raise ActuallySKBusPRPacket
    
        # Here go some attributes and methods common to all packets...
    
    # Both Request and Response packets inherit from base-class "SKBusPacket"
    class SKBusRequestPacket
        def __init__(self, *args, **kwargs):
            # Pass init arguments to super-class "SKBusPacket".
            super(SKBusRequestPacket, self).__init__(*args, **kwargs)
    
    class SKBusResponsePacket
        def __init__(self, *args, **kwargs):
            # Pass init arguments to super-class "SKBusPacket".
            super(SKBusResponsePacket, self).__init__(*args, **kwargs)
    
    
    def log_skbus(raw_pkt):
        try:
            pkt = SKBusPacket(raw_pkt.data)
        except ActuallyRequestPacket:
            pkt = SKBusRequestPacket(raw_pkt.data)
        except ActuallyResponsePacket:
            pkt = SKBusResponsePacket(raw_pkt.data)
    

1 个答案:

答案 0 :(得分:1)

最好将数据包类型的检测与实例化过程分开。如果有一个函数detect_type,(类似于getSkbPacketType方法)那么

def make_packet(raw_pkt):
    packet_type = detect_type(raw_pkt.data)
    if packet_type == 'request':
        pkt = SKBusRequestPacket(raw_pkt.data)
    elif packet_type == 'response':
        pkt = SKBusResponsePacket(raw_pkt.data)
    return pkt

将是您最干净的选择。


一个不太干净的选项是实例化SKBusPacket,检查packet_type属性,然后实例化正确类型的数据包。请注意,不存在例外情况:

class SKBusPacket(Packet):
    def __init__(self, *args, **kwargs):
        super(SKBusPacket, self).__init__(*args, **kwargs)
        self.packet_type = self.getSkbPacketType()

def log_skbus(raw_pkt):
    pkt = SKBusPacket(raw_pkt.data)
    if pkt.packet_type == 'request':
        pkt = SKBusRequestPacket(raw_pkt.data)
    elif pkt.packet_type == 'response':
        pkt = SKBusResponsePacket(raw_pkt.data)

上述代码的一个问题是SKBusPacket.__init__被调用两次,一次用于确定packet_type,第二次用于实例化SKBusRequestPacket或SKBusResponsePacket。也许呼叫很快,这种双重实例化过程不是主要的瓶颈。在这种情况下,这种解决方法就足够了。

如果没有,您可以通过动态更改实例的类来绕过正常的实例化过程:

def make_packet(raw_pkt):
    pkt = SKBusPacket(raw_pkt.data)
    if pkt.packet_type == 'request':
        pkt.__class__ = SKBusRequestPacket
    elif pkt.packet_type == 'response':
        pkt.__class__ = SKBusResponsePacket
    return pkt

要做到这一点,SKBusPacket必须是一个新式的类。 请注意,如果您以这种方式更改课程,SKBusRequestPacket.__init__SKBusResponsePacket.__init__ 将被调用。它将允许pkt访问这些类的方法,但是您需要在make_packet函数本身中放置任何初始化代码。