【问题标题】:How to create a sub-class instance from exception caught in super-class?如何从超类中捕获的异常创建子类实例?
【发布时间】:2014-03-13 22:49:37
【问题描述】:

这是我想要实现的目标:

  1. 通过将数据传递给超类SKBusPacket 来创建数据包对象。
  2. 数据包的子类型在__init__() 中确定,并根据该数据包的子类型(“请求”或“响应”子类型)引发异常。
  3. 这个异常被捕获,数据被传递给子类SKBusRequestPacketSKBusResponsePacket

问题:创建子类的实例会一次又一次地引发上述异常,因为我通过super 将初始化参数传递给超类(参见代码)。

建议的解决方案:通过检查实例是否是子类之一来避免使用raise,使用isinstance()。但我的印象是这是不好的做法,而且不符合 Python 风格。

我的简化代码:

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)

【问题讨论】:

  • 这种方法看起来很奇怪。为什么不能创建一个只构造所需类型对象的工厂函数?
  • @WGH 我不反对。我想遵循最好的设计原则,所以我会去阅读什么是工厂功能。谢谢你的建议。

标签: python oop inheritance


【解决方案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 函数本身。

【讨论】:

  • 我选择了第一个选项。非常感谢您的帮助:)
猜你喜欢
  • 1970-01-01
  • 2019-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-29
  • 1970-01-01
相关资源
最近更新 更多