【问题标题】:creating a defaultlist in python在python中创建一个默认列表
【发布时间】:2012-01-05 20:45:29
【问题描述】:

我正在尝试为非常有用的collections.defaultdict 创建一个等效列表。以下设计效果很好:

class defaultlist(list):
    def __init__(self, fx):
        self._fx = fx
    def __setitem__(self, index, value):
        while len(self) <= index:
            self.append(self._fx())
        list.__setitem__(self, index, value)

这是你如何使用它:

>>> dl = defaultlist(lambda:'A')
>>> dl[2]='B'
>>> dl[4]='C'
>>> dl
['A', 'A', 'B', 'A', 'C']

我应该在默认列表中添加什么以支持以下行为?

>>> dl = defaultlist(dict)
>>> dl[2]['a'] = 1
>>> dl
[{}, {}, {'a':1}]

【问题讨论】:

  • 这个问题是this one的后续问题
  • 我假设您只是在寻找为项目访问重载的方法(而不是项目分配)?
  • dl[2] = {'a', 1} 不是更有意义吗?

标签: python list subclassing


【解决方案1】:

在您给出的示例中,您首先尝试检索列表中不存在的值,就像 dl[2]['a'] 一样,Python 首先检索列表中的第三个(索引 2)元素,然后继续获取该元素在该对象上命名为 'a' - 因此您还必须将自动扩展行为实现到 __getitem__ 方法,如下所示:

class defaultlist(list):
    def __init__(self, fx):
        self._fx = fx
    def _fill(self, index):
        while len(self) <= index:
            self.append(self._fx())
    def __setitem__(self, index, value):
        self._fill(index)
        list.__setitem__(self, index, value)
    def __getitem__(self, index):
        self._fill(index)
        return list.__getitem__(self, index)

【讨论】:

    【解决方案2】:

    有一个可用的python包:

    $ pip install defaultlist
    

    添加的索引默认填充为无。

    >>> from defaultlist import defaultlist
    >>> l = defaultlist()
    >>> l
    []
    >>> l[2] = "C"
    >>> l
    [None, None, 'C']
    >>> l[4]
    >>> l
    [None, None, 'C', None, None]
    

    同样支持切片和负索引

    >>> l[1:4]
    [None, 'C', None]
    >>> l[-3]
    'C'
    

    可以通过 lambda 创建简单的工厂函数。

    >>> l = defaultlist(lambda: 'empty')
    >>> l[2] = "C"
    >>> l[4]
    'empty'
    >>> l
    ['empty', 'empty', 'C', 'empty', 'empty']
    

    也可以实现高级工厂功能:

    >>> def inc():
    ...     inc.counter += 1
    ...     return inc.counter
    >>> inc.counter = -1
    >>> l = defaultlist(inc)
    >>> l[2] = "C"
    >>> l
    [0, 1, 'C']
    >>> l[4]
    4
    >>> l
    [0, 1, 'C', 3, 4]
    

    更多详情请参见Documentation

    【讨论】:

    • ModuleNotFoundError: No module named 'defaultlist' 这很奇怪,因为 pip freeze 显示安装了 1.0。
    【解决方案3】:

    可以实现一个defaultlist,它继承自MutableSequence 抽象基类并包裹defaultdict。我在我的 coinflip 包中这样做,并在 coinflip.collections 子模块中公开它。

    需要像这样覆盖 ABC:

    class defaultlist(MutableSequence):
        def __getitem__(self, i):
            ...
    
        def __setitem__(self, i, value):
            ...
    
        def __delitem__(self, i):
            ...
    
        def __len__(self):
            ...
            
        def insert(self):
            ...
    

    我将通过镜像defaultdict(default_factory=None) 方法来初始化这个defaultlist,将default_factory 传递给内部私有defaultdict

    与 c0fec0de 的解决方案一样,我建议默认使用 None 填充索引(即传入“无工厂”方法),否则您将使用未访问的索引获得 KeyError

    访问器方法(获取、设置和删除项目)的索引只是私有 defaultdict 中的键,并在添加和/或删除项目时相应地更新该字典。

    内置 list 有一些较少使用的方面,如果想效仿,需要一些特殊的考虑。我写了更深入的主题here

    【讨论】:

      【解决方案4】:

      创建默认列表的一个非常简单的方法是使用列表推导和范围。

      defaultvalue = "catfood"
      x = [defaultvalue for i in range(0,4)]
      

      这会导致

      ['catfood', 'catfood', 'catfood', 'catfood']
      

      这可能是一个子类,但不是必须的。 它可能只是这样的工厂函数:

      def defaultlist(defaultvalue, elementcount):
          return [defaultvalue for i in range(0, elementcount)]
      

      会像这样工作

      defaultlist(None, 7)
      

      返回这个

      [None, None, None, None, None, None, None]
      

      【讨论】:

      • 我真的不认为这是 OP 所追求的。
      猜你喜欢
      • 1970-01-01
      • 2016-11-05
      • 2021-05-13
      • 2013-04-11
      • 1970-01-01
      • 2016-04-07
      • 1970-01-01
      • 2011-02-27
      • 2021-06-04
      相关资源
      最近更新 更多