如何在Python中覆盖一个列表属性的Append()方法

69 阅读2分钟

我有一个用例,我需要创建一个类,它的属性是一个Python列表。看上去很简单,对吗?让它变得复杂的部分是,当任何东西被附加到该属性时,我需要做一些特别的事情。观察属性变化对列表的作用和对字符串的作用不一样。

我尝试了很多不同的解决方案,但大多数要么不起作用,要么使代码非常难以理解。

最后,Real Python Slack 频道中有人提到了对collections.UserList进行子类化,并重写append()方法,以便在列表对象被追加时执行回调。

这里是代码的一个非常简化的版本。

import datetime

from collections import UserList
from dataclasses import dataclass, field
from typing import Callable, Dict, List, Tuple


class ListWithCallback(UserList):
    """
    Create a class that emulates a Python list and supports a callback
    """
    def __init__(self, callback: Callable, *args: Tuple, **kwargs: Dict
                 ) -> None:
        super().__init__(*args, **kwargs)
        self.callback = callback

    def append(self, item) -> None:
        super().append(item)
        self.callback()


@dataclass
class Media:
    channels: List = field(default_factory=list)
    
    def update(self) -> None:
        now = datetime.datetime.today()
        print(f"{now:%B %d - %H:%m:%S}")
        
    def __post_init__(self) -> None:
        self.channels = ListWithCallback(self.update)  # type: ignore
        
        
if __name__ == "__main__":
    import time
    impl = Media()
    impl.channels.append("Blah")
    time.sleep(2)
    impl.channels.append("Blah")

这里感兴趣的主要类是Media,它是一个数据类。它有一个单一的属性,channel,它是一个 Python列表。为了使其工作,你使用_post_init_ ()将channels设置为你的自定义类ListWithCallback 的一个实例。这个类接收一个函数或方法,当一个项目被追加到你的特殊列表中时调用。

在这种情况下,每当一个项目被追加时,你就会调用Mediaupdate()方法。为了测试这个功能是否有效,你在代码的底部导入了时间模块,并在列表中追加了两个字符串,在它们之间有一个sleep()

收尾工作

如果你发现自己需要对一个 Python 内置类进行子类化,请查看 Python 的集合模块。它有几个类,通常推荐使用,而不是直接从内置模块本身进行子类化。在这个例子中,你使用了collections.UserList

集合模块子类化是很简单的。你会学到很多东西,你的代码甚至可能因为你这样做而变得更好。