在 setdefault 后向字典中的列表追加内容

7

我有以下代码,我正在尝试在输入中每次出现该元素时将1附加到其哈希值上。

def test(Ar):
    hash_table = {}
    for elem in Ar:
        if elem not in hash_table:
            hash_table.setdefault(elem,[]).append(1)
        else:
            hash_table[elem] = hash_table[elem].append(1)
    print(hash_table)

Ar = (1,2,3,4,5,1,2)
test(Ar)

输出:

{1: None, 2: None, 3: [1], 4: [1], 5: [1]}

预期输出:

{1: [1,1], 2: [1,1], 3: [1], 4: [1], 5: [1]}

我很困惑为什么None会被添加到列表中。请解释一下发生了什么。
注意:
在键入else部分时,
hash_table[elem] = hash_table[elem].append(1) # the append() was not suggested at all by the IDE. I forcibly put it, hoping things will work.
2个回答

11

list.append 是一个原地操作。因此,它只修改了列表对象,不返回任何东西。这就是为什么默认情况下从 list.append 返回 None,并且您将其存储到该行对应的键中。

hash_table[elem] = hash_table[elem].append(1)
在您的情况下,根本不需要使用 if 条件。
def test(Ar):
    hash_table = {}
    for elem in Ar:
        hash_table.setdefault(elem, []).append(1)
    print(hash_table)

因为setdefault会首先在其中查找键elem,如果找到了相应的值,则返回该值。如果没有找到,则创建键elem并将传递给它的第二个参数用作值,然后返回该值。


你可以使用collections.defaultdict代替这种方法,像这样:

from collections import defaultdict
def test(Ar):
    hash_table = defaultdict(list)
    for elem in Ar:
        hash_table[elem].append(1)
    print(hash_table)

这个做法跟使用setdefault版本几乎是一样的。


看起来你正在尝试计算元素频率。在这种情况下,你可以直接使用collections.Counter

from collections import Counter
Ar = (1, 2, 3, 4, 5, 1, 2)
print Counter(Ar)
# Counter({1: 2, 2: 2, 3: 1, 4: 1, 5: 1})

这将给出可迭代对象中每个元素出现的次数。


还要注意,defaultdict 有一个 default_factory 属性,当设置为 None 时,默认字典的行为就像普通字典一样。这对于希望使用 defaultdict 行为构建字典,但又想返回像普通字典一样的东西的函数特别有用。 - mgilson

1
这个 bug 在这一行:
hash_table[elem] = hash_table[elem].append(1)

list.append(x) 返回 None,你将其赋值给 hash_table[elem]

else 部分是不必要的,但为了获得正确的结果,删除 else 部分中的赋值。


请注意,None 仅适用于出现两次的元素。如果任何元素存在三次,则会出现错误。

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