从Python字典中删除“第一个”项目

7

下午好。

如果我的问题看起来很蠢,或者已经发布过了(我搜索了一下,但好像没找到。如果我错了,请告诉我:我是新来的,可能不擅长搜索正确的问题) ,非常抱歉。

我想知道在Python中是否可以从字典中弹出(pop)一个通用项目。

该想法来自以下练习:

编写一个函数,查找给定字典中值的总和。

显然有许多方法可以实现:对dictionary.values()求和,为总和创建一个变量,并遍历字典并更新它等等。 但我试图使用递归来解决它,类似于:

def total_sum(dictionary):
    if dictionary == {}:
        return 0 
    return dictionary.pop() + total_sum(dictionary) 

这个想法的问题在于我们事先不知道字典中哪一个键是“第一个”,因为它是无序的。如果这是一个列表,那么索引0就会被使用,它将会起作用。 既然我不关心弹出项的顺序,只要有一种删除任何项(“通用”项)的方法就足够了。你认为这样的解决方案可能存在吗?还是必须使用一些辅助变量,失去递归使用的整个优点,其优点是代码非常简洁和简单?
实际上,我找到了以下解决方案,尽管代码更复杂、更难以阅读:我认为,如果有一些内置的、简单而直接的解决方案来解决删除字典“第一个”项的特定问题,那么它仍然可能是有趣和有用的,尽管可以找到许多“人造”的替代解决方案。
def total_sum(dictionary):
    if dictionary == {}:
        return 0
    return dictionary.pop(list(dictionary.keys())[0]) + total_sum(dictionary)

如果您想进行一些简单的测试,我将为您提供一个简单的示例字典来应用该函数。

ex_dict = {"milk":5, "eggs":2, "flour": 3}

1
这个问题的更大问题是在过程中破坏了字典。大多数情况下,这是不可接受的。 - Mark
是的,但是你可以通过向函数传递一个你真正感兴趣的字典的副本来避免这种情况。现在的新问题是,在大规模的环境下(数据科学等),这可能会非常低效。无论如何,这更多是一种好奇心,而且无论如何,这个特定的函数有点像是一个“借口”,以突出与“pop”具有相同特征的更“通用”的问题,但是即使不知道它的键也可以弹出第一个项目。 - Raffaele de Matteis
在许多算法中,破坏性迭代既有用又完全可以接受。例如,在跟踪深度优先迭代时,一个字典会跟踪必须处理的节点。当字典变为空时,迭代完成。 - Ken Williams
@KenWilliams 当然,在某些情况下可能需要这样做。但是,一个旨在提供集合总和的函数,同时毫无理由地破坏该集合,仍然是不可接受的。大多数情况下,当输入参数发生变化时,如果这不是特定函数的工作,那么对调用者来说会是一个惊喜。这与破坏性迭代并不相关,而是关于函数及其与调用者的契约。 - Mark
5个回答

11
(k := next(iter(d)), d.pop(k))

将从dict对象中删除最左边(第一个)项目(如果存在)。

如果您想要删除字典中最右边/最近的值

d.popitem()

我认为这个回答更准确地回答了所问的问题,尤其是这个从句:_这个想法的问题在于我们不知道字典中哪个键可能是“第一个”_。 - Parapluie

3

ex_dict.popitem()

这个方法会从字典中删除最后一个(最近添加的)元素。


1
正确。它返回一个元组(键,值)。因此,为了让函数正常工作,您应该引用元组的第二个元素:return dictionary.popitem()[1] + total_sum(dictionary) - Ronald
谢谢。一开始它给了我一个错误,因为与“pop”不同,它返回一个元组中的整个键/值对,而我正好像使用“pop”一样使用它,而“pop”只返回值。现在一切都很好! - Raffaele de Matteis
7
答案误导性地声称popitem()返回第一个元素。根据文档:成对的元素按照LIFO(后进先出)顺序返回。此外,这仅适用于从版本3.7开始更改现在保证LIFO顺序。在之前的版本中,popitem()将返回任意的键/值对. - Egor B Eremeev
@EgorBEremeev 我已经在答案中纠正了这个错误。 - Ken Williams

0

你可以从字典中弹出元素,但这样会破坏它。如果你想要找到字典中值的总和,最简单的方法可能是使用列表推导。

sum([v for v in ex_dict.values()])

1
谢谢您的回答,但正如我在评论中对马克所说的那样,这更像是对“pop”的“默认行为”的好奇,即使不知道关键字,它也会默认获取第一个项目。这里的特定上下文价值总和有点意味着提供背景信息以便情境化问题并提供实际示例。 - Raffaele de Matteis

0

不要考虑弹出值的方式,更符合Python风格(在这里递归是Python风格)的方法是使用迭代器。您可以将字典的值转换为迭代器,并将其用于递归。这将具有内存效率,并为您的递归提供非常清晰的停止条件:

ex_dict = {"milk":5, "eggs":2, "flour": 3}

def sum_rec(it):
    if isinstance(it, dict):
        it = iter(it.values())
    try:
        v = next(it)
    except StopIteration:
        return 0
    return v + sum_rec(it)

sum_rec(ex_dict)
# 10

这并没有真正回答关于弹出值的问题,但那不应该是一个选项,因为您不能销毁输入字典,并且仅为获取总和而进行复制可能非常昂贵。
使用 popitem() 几乎是相同的代码。 您只需捕获不同的异常并期望从弹出中得到元组即可。(当然要理解清空字典是作为副作用发生的)
ex_dict = {"milk":5, "eggs":2, "flour": 3}

def sum_rec(d):
    try:
        k,v = d.popitem()
    except KeyError:
        return 0
    return v + sum_rec(d)

sum_rec(ex_dict)
# 10

-2

我们可以使用:

dict.pop('keyname')

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