根据值从列表中删除 namedtuple

Remove namedtuple from list based on value

考虑以下扩展 list 并附加 orderedtuple 的 class:

from collections import namedtuple

Order = namedtuple('Order', ['id', 'value'])


class Orders(list):
    def __init__(self, *args):
        super(Orders, self).__init__()
        self.extend(args)

    def add(self, id, value):
        self.append(Order(id, value))

...添加一些元素...

order = Orders()

order.add(1, 'alpha')
order.add(2, 'beta')
order.add(3, 'lambda')
order.add(4, 'omega')

...我们还剩下...

print order
[Order(id=1, value='alpha'), Order(id=2, value='beta'), Order(id=3, value='lambda'), Order(id=4, value='omega')]

假设我想通过 id 删除任意元素。例如:

order.remove(id=2) # ideal function call to remove by `id`
print order
[Order(id=1, value='alpha'), Order(id=3, value='lambda'), Order(id=4, value='omega')]

是否有相当简单的方法来完成此操作?

你可以慢慢来:

def remove(self, id=None, value=None):
    for elem in self:
        if (id is not None and elem.id == id or
                value is not None and elem.value == value):
            super(Orders, self).remove(elem)
            break

您可以向您的 class 添加一个索引,用于跟踪特定索引的地图 ID 和/或值,但您需要在操作包含的列表时保持该索引为最新命令。它看起来像这样:

def __init__(self, *args):
    # ...
    self._ids = {}

def append(self, id, value):
    if id in ids:
        raise ValueError('This order already exists!')
    super(Orders, self).append(Order(id, value))
    self._ids[id] = len(self) - 1

并且,如果您还调整了所有其他可以改变列表和更改顺序等的方法,您就可以通过它们的 id 快速找到订单:

def remove(self, id):
    if id not in self._ids
        raise ValueError('No such order exists!')
    del self[self._ids[id]]

您可以构建自定义 remove 方法来为您执行此操作:

def remove(self, id):
    for index, item in enumerate(self):
        if item.id == id:
            break
    else:
        raise ValueError("id not found")

    del self[index]

演示:

>>> class Orders(list):
...     def __init__(self, *args):
...         super(Orders, self).__init__()
...         self.extend(args)
...     def add(self, id, value):
...         self.append(Order(id, value))
...     def remove(self, id):
...         for index, item in enumerate(self):
...             if item.id == id:
...                 break
...         else:
...             raise ValueError("id not found")
...         del self[index]
...
>>> order = Orders()
>>> order.add(1, 'alpha')
>>> order.add(2, 'beta')
>>> order.add(3, 'lambda')
>>> order.add(4, 'omega')
>>> order
[Order(id=1, value='alpha'), Order(id=2, value='beta'), Order(id=3, value='lambda'), Order(id=4, value='omega')]
>>> order.remove(2)
>>> order
[Order(id=1, value='alpha'), Order(id=3, value='lambda'), Order(id=4, value='omega')]
>>> order.remove(5)  # Nonexistent id
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 12, in remove
ValueError: id not found
>>>

请注意,该函数只传递一次数据,就像正常的 list.remove 一样。

此解决方案的主要优点是简单。您可以在不更改 class 定义的任何其他部分的情况下实现 remove 方法。也就是说,它仍然以 O(n) 复杂度运行。如果性能是您的主要关注点,那么您应该花时间实施@MartijnPieters 提出的 O(1) id-lookup 方法。

[namedtuple for namedtuple in list if namedtuple.id != 'id']