Python中子类化列表的Pythonic方法

4
这是我的一般问题空间:
我使用I2C与设备进行字节/位协议通信。
我有一个“数据库”,用于完全描述所有位字段类型、值和枚举的命令。
我有一个类来消费这个数据库,以及一个i2c驱动程序/事务处理器,这样我就可以调用命令并获得响应。
MyProtocol = Protocol('database.xml',I2CDriver())
theStatus = MyProtocol.GET_STATUS()

创建适当的字节流以用于GET_STATUS命令,将其发送到I2C并返回响应作为当前字节数组。我可以在GET_STATUS()实现中使响应漂亮地打印出来,但我想将该行为移至返回对象,而不是在命令中。

我希望我的返回对象是“智能”的:theStatus需要具有字节列表/数组以及其字段定义的引用。

我希望theStatus的行为像一个列表/字节数组,这样我就可以直接检查字节。如果切片不是字节列表或字节数组之类的东西,我也不在乎。一旦它们被切片出来,它们就只是字节。

我希望可以打印'theStatus'print(theStatus),并将其漂亮地打印出所有状态字段。一旦我确定了一个可行的数据结构,允许我访问字节和数据库,我就可以做到这一点。

我希望能够通过字段名称检查theStatus,例如theStatus.FIELDNAME或者theStatus['FIELDNAME']'。同样的事情:一旦我有了一个可行的数据结构,其中字节数组和数据库作为成员,我就可以做到这一点。

问题在于我不知道“正确”的数据结构会引起最少的问题。

有关实现这一目标的最pythonic方法的任何建议?我的最初想法是子类化list并将字段定义添加为成员,但似乎python根本不喜欢这个想法。

组合似乎是下一个选择,但让它像一个适当的list似乎可能需要大量工作才能使其“正确”。

3个回答

9
你真正想要的是实现一个新的序列类型,也许是可变的。你可以通过实现模拟容器类型所需的特殊方法从头开始创建一个,或者你可以使用适当的collections.abc集合ABC作为基础。
后者可能是最简单的路径,因为ABC提供了许多方法的实现作为依赖于你必须实现的一些抽象方法的基本版本。
例如,(不可变的)Sequence ABC只需要你提供__getitem____len__的实现;基本ABC实现提供其余部分:
from collections.abc import Sequence

class StatusBytes(Sequence):
    def __init__(self, statusbytes):
        self._bytes = statusbytes

    def __getitem__(self, idx_or_name):
        try:
            return self._bytes[idx_or_name]
        except IndexError:
            # assume it is a fieldname
            return FIELDNAMES[idx_or_name]

    def __len__(self):
        return len(self._bytes)

如果您确实需要完整的列表实现,包括支持丰富比较(list_a <= list_b)、排序(list_a.sort())、复制[list_a.copy()]和乘法(list_a * 3),那么还有collections.UserList() class。这个类继承自collections.abc.MutableSequence,并添加了额外的功能,这些功能是list在基本序列ABC上提供的。如果您不需要这些额外的功能,请使用基本的ABC。

我已经完成了这个,它运行得很好。实际上,出于某种原因(可能是扩展?之类的),我选择了UserList。出于好奇,为什么在__init__中没有调用super()呢? - Russ Schultz
1
@RussSchultz:在UserList__init__或者我在示例类中定义的__init__中?super().__init__()并不总是合适的,并且要求MRO中的所有__init__方法都能处理相同数量的参数。这使得使用它并不那么简单明了。collections.abc中的抽象基类没有__init__方法,除非您想支持多重继承的进一步子类化,否则在这里不需要使用super().__init__() - Martijn Pieters

1

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接