如果不在另一个列表中,如何删除列表项 - Python

3

这是我的情况。 我有一个Person对象列表。

class Person():
    def __init__(self, name="", age=):
        self.name = name
        self.uid = str( uuid.uuid4( ) )
        self.age = age

我的用户界面包含一个树形视图,显示这些项目。在某些情况下,如果用户需要,他们可以有同一个人的实例。我使用加粗来突出显示那些相同的人,以便让用户知道。

![enter image description here

问题 当用户删除树节点时,我需要知道是否应该从列表中删除实际对象。但是如果另一个实例正在使用该对象,则不应将其删除。
我的解决方案思路。 在执行删除操作之前(仅删除树节点项),我会收集在ui中使用的所有人员。
接下来,我将继续删除树视图项。
然后再收集在ui中使用的其他对象集合。
最后比较两个列表并删除未出现在第二个列表中的人员。
如果我采用这种解决方案,我是否最好进行测试?
for p in reversed(original_list):
    if p not in new_list:
        original_list.remove(p)

或者我应该仅收集uid号码进行比较,而不是整个对象?

这些列表可能相当庞大。

这里是我的第一次尝试处理删除操作的代码。在关闭应用程序时它会保存一个json文件。

https://gist.github.com/JokerMartini/4a78b3c5db1dff8b7ed8

这是我执行删除的函数。
def delete_treewidet_items(self, ctrl):
        global NODES
        root = self.treeWidget.invisibleRootItem()

        # delete treewidget items from gui
        for item in self.treeWidget.selectedItems():
            (item.parent() or root).removeChild(item)

        # collect all uids used in GUI
        uids_used = self.get_used_uids( root=self.treeWidget.invisibleRootItem() )

        for n in reversed(NODES):
            if n.uid not in uids_used:
                NODES.remove(n)

你的 UI 添加代码是什么?创建一个人并使用同一个实例的代码是什么? - Mr_and_Mrs_D
我明白了 - 所以问题实际上是你有“项目”,但你无法从中获取人员 - 这是 UI 列表控件的常见问题。你的控件是否允许你以某种方式将项目连接到人员?也就是说,你能否从“项目”获取“人员”?编辑:当然可以 - 你有 CustomTreeNode,它基本上包装了一个人(为什么称一个人为人数据)?根据我的答案重构你的代码并回复 - 基本上你可以删掉几行... - Mr_and_Mrs_D
3个回答

2

不必过多担心列表的运行时间或大小,您可以使用集合运算:

for p in set(original_list) - set(new_list):
    original_list.remove(p)

或者筛选列表:

new_original_list = [p for p in original_list if p in new_list]

然而,回过头来看,为什么要查看整个列表 - 当删除一个项目(甚至是树中的非叶节点)时,你知道哪个项目被删除了,因此你可以将搜索限制在该项目上。


1
但是仅限于删除的项目会导致问题,如果该子节点有许多子叶节点。仅删除已删除的节点并不以任何方式表示其子节点也已被删除。这意味着我的列表仍将包含未使用的节点。 - JokerMartini

2

您没有贴足够的代码,但从我所了解的情况来看:

import collections
import uuid

class Person():
    def __init__(self, name="", age=69):
        self.name = name
        self.uid = str( uuid.uuid4( ) )
        self.age = age

    def __eq__(self, other):
        return isinstance(other, Person) and self.uid == other.uid
    def __ne__(self, other): return self != other # you need this

    def __hash__(self):
        return hash(self.uid)

# UI --------------------------------------------------------------------------
persons_count = collections.defaultdict(int) # belongs to your UI class
your_list_of_persons = []  # should be a set

def add_to_ui(person):
    persons_count[person] += 1
    # add it to the UI

def remove_from_ui(person):
    persons_count[person] -= 1
    if not persons_count[person]: your_list_of_persons.remove(person)
    # remove from UI

因此基本上:
在执行删除操作之前,即仅删除树节点项之前,我会收集在ui中使用的所有人员。
不是 - 您始终可以将此信息作为模块变量在您的UI中使用 - 如上面的persons_count。这样,您就不必复制列表。
剩下的代码是创建人员的代码 - 然后应更新您的列表(其中包含distinct人员,因此应该是一个set)。如果在add_to_ui中完成此操作(有意义),则应进行修改:
def add_to_ui(name, age):
    p = Person(name, age)
    set_of_persons.add(p) # if already there won't be re-added and it's O(1)
    persons_count[person] += 1
    # add it to the UI

要更进一步 - 你实际上不需要原始列表 - 那只是persons_count.keys(),你只需要修改:
def add_to_ui(name, age):
    p = Person(name, age)
    persons_count[person] += 1
    # add it to the UI

def remove_from_ui(person):
    persons_count[person] -= 1
    if not persons_count[person]: del persons_count[person]
    # remove from UI

那么你已经有了一个大致的了解

编辑:下面是我最新版本中删除的内容:

def delete_tree_nodes_clicked(self):
    root = self.treeWidget.invisibleRootItem()
    # delete treewidget items from gui
    for item in self.treeWidget.selectedItems():
        (item.parent() or root).removeChild(item)
        self.highlighted.discard(item)
        persons_count[item.person] -= 1
        if not persons_count[item.person]: del persons_count[item.person]

我已经在这个问题的代码链接中发布了我的解决方案(对于第一个问题的代码进行了改写): https://github.com/Utumno/so_34104763/commits/master。它是一个很好的重构练习 - 可以查看提交信息。特别是我在这里引入了字典:https://github.com/Utumno/so_34104763/commit/074b7e659282a9896ea11bbef770464d07e865b7 还需要更多的工作,但我认为它是朝着正确方向迈出的一步 - 在大多数操作中应该更快并节省内存。

我在这里发布帖子之前,已经把代码发布了第一次尝试。看看吧,希望能有所帮助。 - JokerMartini
@JokerMartini:你应该提供与你问题相关的部分 - 没有人会免费阅读和调试300行代码 ;) - Mr_and_Mrs_D
更新问题以便重点显示有关删除的位。我在def tk explain中放置了注释。今天到电脑前时,我会尝试您的解决方案。如果我的片段需要进一步解释,请告诉我。 - JokerMartini
@JokerMartini:我在这里建立了一个仓库来进行破解:https://github.com/Utumno/so_34104763/commits/master。我仍然需要一些激励-因为需要重写。 - Mr_and_Mrs_D
是的,我完全理解。我正在清理我现在拥有的代码。然后我可以与您分享,以了解您实施“重写”的成本。我认为使用我的更干净的代码会更容易。 - JokerMartini
显示剩余6条评论

0

您可以使用以下方法比较对象:

  1. 对象标识
  2. 对象相等性

要比较对象标识,您应该使用内置函数id()或关键字is(它使用id())。来自文档:

id function

返回对象的“标识”。这是一个整数(或长整数),保证在其生命周期内对于此对象是唯一且恒定的。两个生命周期不重叠的对象可能具有相同的id()值。

is operator

运算符is和is not用于测试对象标识:当且仅当x和y是同一对象时,x is y为true。x is not y产生相反的真值。

例如:

>>> p1 = Person('John')
>>> p2 = Person('Billy')
>>> id(p1) == id(p2)
False
>>> p1 is p2
False

要比较对象的相等性,您可以使用 == 运算符。 == 运算符使用 eq 方法来测试相等性。 如果类没有定义这样的方法,则会回退到比较对象的标识。

所以对于:

还是我应该收集uid号码而不是整个对象进行比较?

由于您没有在类中定义 eq ,因此您将执行相同的操作。

在迭代列表时不要修改列表,这很糟糕。 猜猜会打印什么:

>>> a = [1, 2, 3]
>>> b = [1, 2]
>>> for item in a:
...     if item in b:
...         a.remove(item)
>>> a
[2, 3]

如果你想安全地进行迭代,请从列表的末尾开始,像这样:

>>> a = [1, 2, 3]
>>> b = [1, 2]
>>> for i in xrange(len(a) - 1, -1, -1):
...     if a[i] in b:
...         a.pop(i)
2
1
>>> a
[3]

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