意见 - Python:多重继承或组合

时间:2015-04-26 16:13:01

标签: python inheritance

我目前正在重新设计Python中与加密货币项目相关的一些对象。在我的设计中,我倾向于在任何有意义的地方使用合成和依赖注入。

transaction对象是一个数据结构,其字段可以拆分并与字符串连接在一起(序列化/反序列化)。在加密的世界中,存在这种数据结构的变体,其中可以以不同的顺序添加,移除或序列化字段。

我有基本的transaction课程:

class CTransaction(object):
    def __init__(self):
        #Basics
        self.nVersion = 1
        self.vin = []
        self.vout = []
        self.nLockTime = 0
        self.sha256 = None

    def deserialize(self, f):
        self.nVersion = struct.unpack("<i", f.read(4))[0]
        self.vin = deser_vector(f, CTxIn)
        self.vout = deser_vector(f, CTxOut)
        self.nLockTime = struct.unpack("<I", f.read(4))[0]
        self.sha256 = None

    def serialize(self):
        r = ""
        r += struct.pack("<i", self.nVersion)
        r += ser_vector(self.vin)
        r += ser_vector(self.vout)
        r += struct.pack("<I", self.nLockTime)
        return r

此变体可以是支持消息的事务。我使用继承并添加其他消息字段。它只是在序列化过程中附加到末尾:

class CTransactionMessage(CTransaction):
    def __init__(self, Tx_Message):
        super(CTransactionMessage, self).__init__()
        # Support for Transaction message is version 2
        self.nVersion = 2
        self.strTxComment = Tx_Message

    def deserialize(self, f):
        super(CTransactionMessage, self).deserialize(f)
        self.strTxComment = deser_string(f)

    def serialize(self):
        r = super(CTransactionMessage, self).serialize()
        r += ser_string(self.strTxComment)
        return r

在另一种变体中,您可能有POS类型货币的交易。

class CPosTransaction(CTransaction):
    def __init__(self, ntime):
        super(CPosTransaction, self).__init__()
        # POS blocks have an 'nTime' field
        self.nTime = ntime

    def deserialize(self, f):
        self.nVersion = struct.unpack("<i", f.read(4))[0]
        self.nTime = struct.unpack("<i", f.read(4))[0]
        self.vin = deser_vector(f, CTxIn)
        self.vout = deser_vector(f, CTxOut)
        self.nLockTime = struct.unpack("<I", f.read(4))[0]
        self.sha256 = None

    def serialize(self):
        r = ""
        r += struct.pack("<i", self.nVersion)
        r += struct.pack("<i", self.nTime)
        r += ser_vector(self.vin)
        r += ser_vector(self.vout)
        r += struct.pack("<I", self.nLockTime)
        return r

在POS情况下,必须重新定义serialize / deserialize方法,因为新字段被添加到中间的某个位置。

最后,您可以使用支持邮件的POS类型货币进行交易。

我可以复制很多代码并编写一个新类:

class CPosTransactionMessage(CTransaction):
    def __init__(self, Tx_Message, ntime):
        super(CPosTransactionMessage, self).__init__()
        # Support for Transaction message is version 2
        self.nVersion = 2
        self.strTxComment = Tx_Message
        # POS blocks have an 'nTime' field
        self.nTime = ntime

    def deserialize(self, f):
        self.nVersion = struct.unpack("<i", f.read(4))[0]
        self.nTime = struct.unpack("<i", f.read(4))[0]
        self.vin = deser_vector(f, CTxIn)
        self.vout = deser_vector(f, CTxOut)
        self.nLockTime = struct.unpack("<I", f.read(4))[0]
        self.sha256 = None
        self.strTxComment = deser_string(f)

    def serialize(self):
        r = ""
        r += struct.pack("<i", self.nVersion)
        r += struct.pack("<i", self.nTime)
        r += ser_vector(self.vin)
        r += ser_vector(self.vout)
        r += struct.pack("<I", self.nLockTime)
        r += ser_string(self.strTxComment)
        return r

或者我可以使用多重继承。 (注意:我不确定我是否正确这样做)

class CPosTransactionMessage(CPosTransaction, CTransactionMessage):
    def __init__(self, Tx_Message, ntime):
        CPosTransaction.__init__(self, ntime)
        CTransactionMessage.__init__(self, Tx_Message)

    def deserialize(self, f):
        CPosTransaction.deserialize(self, f)
        CTransactionMessage.deserialize(self, f)

    def serialize(self):
        r = CPosTransaction.serialize(self)
        r += CTransactionMessage.serialize(self)
        return r

我应该使用合成并将serialize / deserialize方法分成更多对象吗?或者我在思考/过度复杂化了吗?

2 个答案:

答案 0 :(得分:0)

首先:super是你的朋友,特别是如果你正在做菱形继承。

其次:考虑不这样做。 Python支持它,但是尽可能坚持使用mixin通常是个好主意。 所以在你的情况下,它将是:

class CTransaction(object):
    def serialize(self)
        "do stuff"


class PosMixin(object):
    def serialize(self, *args, **kwargs):
        super(PosMixin, self).serialize(*args, **kwargs)
        # do my stuff

class AnotherMixin(object):
    def serialize(self, *args, **kwargs):
        super(AnotherMixin, self).serialize(*args, **kwargs)
        # do some more stuff

class TransactionPos(CTransaction, PosMixin): pass

class TransactionAnotherPos(CTransaction, PosMixin, AnotherMixin): pass

另一种方法是手动注册回调,然后调用它们。这是你的依赖注入。但除非你在飞行中产生很多这些类的变化,否则没什么意义。

答案 1 :(得分:0)

如果序列化格式是免费的并且不严格依赖于数据结构(在您的示例中TxMessage附加在末尾,而nTime插入中间),您可以使用的是用于描述格式的DSL,然后在基类中编写通用代码,用于处理使用该配方的序列化/反序列化:

class CTransaction(object):
    format = [('nVersion',  'i'),
              ('vin',       'v', CTxIn),
              ('vout',      'v', CTxOut),
              ('nLockTime', 'I')]

    def __init__(self):
        ... your usual code ...

    def deserialize(self, f):
        for field in self.__class__.format:
            setattr(self, field[0],
                    deserializers[field[1]](f, *field[1:]))
        self.sha256 = None

    def serialize(self):
        res = ""
        for field in self.__class__.format:
            res += serializers[field[1]](getattr(self, field[0]),
                                         *field[1:])
        return res

使用这种方法,您永远不会重新实现serialize / deserialize,例如CPosTransaction类实现为

class CPosTransaction(CTransaction):
    format = [('nVersion',  'i'),
              ('nTime',     'I'),
              ('vin',       'v', CTxIn),
              ('vout',      'v', CTxOut),
              ('nLockTime', 'I')]

    def __init__(self, ntime):
        ...

CPosTransactionMessage实现为

class CPosTransactionMessage(CTransaction):
    format = [('nVersion',     'i'),
              ('nTime',        'I'),
              ('vin',          'v', CTxIn),
              ('vout',         'v', CTxOut),
              ('nLockTime',    'I'),
              ('strTxComment', 's')]

    def __init__(self, Tx_Message, ntime):
        ...

如果DRY非常重要,您甚至可以在导入时构建食谱

class CPosTransaction(CTransaction):
    # Insert nTime after first field
    format = (CTransaction.format[:1] +
              [('nTime',     'I')] +
              CTransaction.format[1:])

    def __init__(self, ntime):
        ...

class CPosTransactionMessage(CPosTransaction):
    # Append strTxComment at the end
    format = (CPosTransaction.format +
              [('strTxComment', 's')])

    def __init__(self, Tx_Message, ntime):
        ...

但是当然这会让你在阅读时更难理解代码。