观点-Python:多重继承还是组合

观点-Python:多重继承还是组合,python,inheritance,Python,Inheritance,我目前正在用Python重新设计一些与加密货币项目相关的对象。在我的设计中,我喜欢在任何有意义的地方使用组合和依赖注入 transaction对象是一种数据结构,其中的字段被拆分并与字符串连接在一起(序列化/反序列化)。在加密世界中,这种数据结构有多种变体,其中字段可以以不同的顺序添加、删除或序列化 我有基本的交易类: class CTransaction(object): def __init__(self): #Basics self.nVersion

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

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
在另一个变体中,您可以进行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

我应该改为使用组合并将序列化/反序列化方法分离为更多对象吗?还是我想得太多/太复杂了?

首先:
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

另一种方法是手动注册回调,然后调用它们。那是你的依赖注入。但是,除非您动态生成这些类的大量变体,否则没有什么意义。

如果序列化格式是自由的,并且不严格依赖于数据结构(在您的示例中,
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
使用这种方法,您永远不会重新实现
序列化
/
反序列化
,例如
CPosTransaction
类被实现为

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

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

    def __init__(self, Tx_Message, ntime):
        ...
CPosTransactionMessage
实现为

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

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

    def __init__(self, Tx_Message, ntime):
        ...
如果干燥非常重要,您甚至可以在导入时构建配方

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):
        ...
但这当然会使阅读代码时更难理解代码

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):
        ...