从零打造属于你的Python容器类型:全流程图解+实战案例

27 阅读2分钟

在日常python开发中,内置的list、dict、set等容器类型极大地方便了我们的数据管理。但如果业务出现特殊需求,标准容器无法胜任怎么办?此时就需要自定义容器,既能像内置类型一样用,也能实现个性化行为!今天带你由浅入深地掌握Python“自定义容器”的全部方法和核心原理。


一、什么是自定义容器?

自定义容器指的是:实现特定“容器协议”的自定义类,对象能像list/dict一样支持索引、for循环、len()、in、切片等操作。

你可以实现:

  • 定长队列(缓存)
  • 有效期缓存字典
  • 只读/自定义校验的有序集合
  • 支持自定义索引的数据结构
  • “伪”list/“伪”dict等任意自定义数据行为

二、最基础的容器协议:__len____getitem____setitem__

示例:仿列表的自定义容器

class MyList:
    def __init__(self, data=None):
        self._data = list(data) if data else []
        
    def __len__(self):
        return len(self._data)
    
    def __getitem__(self, idx):
        print(f"访问索引 {idx}")
        return self._data[idx]
    
    def __setitem__(self, idx, value):
        print(f"将{value}赋值到索引{idx}")
        self._data[idx] = value
    
    def append(self, value):
        self._data.append(value)
ml = MyList([1, 2, 3])
print(len(ml))        # 3
print(ml[1])          # 访问索引 1  \n 2
ml[1] = 100           # 将100赋值到索引1
ml.append(200)
print(list(ml._data)) # [1, 100, 3, 200]

三、进阶协议:__iter__, __contains__和切片支持

  • __iter__ 让你的容器支持 for ... in ...
  • __contains__ 支持 in 判断
class MyList:
    def __init__(self, data=None):
        self._data = list(data) if data else []
        
    def __len__(self):
        return len(self._data)
    
    def __getitem__(self, idx):
        print(f"访问索引 {idx}")
        return self._data[idx]
    
    def __setitem__(self, idx, value):
        print(f"将{value}赋值到索引{idx}")
        self._data[idx] = value
    
    def append(self, value):
        self._data.append(value)

    def __iter__(self):
        return iter(self._data)
    
    def __contains__(self, item):
        return item in self._data

ml = MyList([1, 2, 3])
print(len(ml))        # 3
print(ml[1])          # 访问索引 1  \n 2
ml[1] = 100           # 将100赋值到索引1
ml.append(200)
print(list(ml._data)) # [1, 100, 3, 200]

for i in ml:
    print(i)
print(100 in ml)  # True

支持切片

要支持切片,需要同时处理 indexslice

   def __getitem__(self, idx):
        if isinstance(idx, slice):
            print(f"访问切片 {idx}")
            return MyList(self._data[idx])
        return self._data[idx]

四、实现字典型容器协议:__setitem____getitem____delitem__

自定义dict-like结构:

class MyDict:
    def __init__(self):
        self._d = {}

    def __setitem__(self, key, value):
        self._d[key] = value

    def __getitem__(self, key):
        return self._d[key]

    def __delitem__(self, key):
        del self._d[key]

    def __contains__(self, key):
        return key in self._d

    def keys(self):
        return self._d.keys()

    def items(self):
        return self._d.items()
        
    def values(self):
        return self._d.values()

五、更高阶设计:自定义行为与约束

比如:只读容器、自动过滤、缓存等

class ReadOnlyList:
    def __init__(self, data):
        self._data = list(data)
        
    def __getitem__(self, idx):
        return self._data[idx]
        
    def __len__(self):
        return len(self._data)
        
    def __setitem__(self, idx, value):
        raise NotImplementedError("只读容器!")
        
    def append(self, value):
        raise NotImplementedError("只读容器!")

六、魔法方法一览表(小抄)

魔法方法作用
__len__len(obj)
__getitem__obj[key]
__setitem__obj[key]=value
__delitem__del obj[key]
__iter__for x in obj
__contains__x in obj
__reversed__reversed(obj)

更多见Python数据模型官方文档


如果你喜欢这篇文章,欢迎点赞、收藏和转发,更多Python干货内容敬请关注!