在Python中迭代字典或列表

15

我刚写了一些很糟糕的代码,用 Python 遍历了一个字典或列表。我感觉这应该不是最好的方法。

问题在于,要遍历字典,需要按照以下约定:

for key in dict_object:
    dict_object[key] = 1

但是,如果在列表上执行相同的操作,则通过键修改对象属性无法奏效:

# Throws an error because the value of key is the property value, not 
#     the list index:

for key in list_object:
    list_object[key] = 1 

我解决这个问题的方法是写了一些糟糕的代码:

if isinstance(obj, dict):
    for key in obj:
        do_loop_contents(obj, key)
elif isinstance(obj, list):
    for i in xrange(0, len(obj)):
        do_loop_contents(obj, i)

def do_loop_contents(obj, key):
    obj[key] = 1
有更好的方法吗? 谢谢!
6个回答

15

我从未需要过这样做。但是如果需要的话,我可能会像这样做:

seq_iter = x if isinstance(x, dict) else xrange(len(x))

例如,以函数形式:

>>> def seq_iter(obj):
...     return obj if isinstance(obj, dict) else xrange(len(obj))
... 
>>> x = [1,2,3]
>>> for i in seq_iter(x):
...     x[i] = 99
... 
>>> x
[99, 99, 99]
>>> 
>>> x = {1: 2, 2:3, 3:4}
>>> for i in seq_iter(x):
...     x[i] = 99
... 
>>> x
{1: 99, 2: 99, 3: 99}

刚刚发现你的答案和我的非常相似。唯一的区别是你使用了 xrange()(这也没问题),但结果是相同或非常相似的。+1 - Tadeck
1
这行代码应该是 return obj if isinstance(obj, dict) else xrange(len(obj)),对吗? - tobias_k
@tobias_k:干得好!Python的作用域遇到了内联编辑。 :^) - DSM

6

这是正确的方法,但如果由于某些原因需要以相同的方式处理这两个对象,则可以创建一个可迭代对象,无论如何都将返回索引/键:

def common_iterable(obj):
    if isinstance(obj, dict):
        return obj
    else:
        return (index for index, value in enumerate(obj))

这将按照您的要求进行操作:

>>> d = {'a': 10, 'b': 20}
>>> l = [1,2,3,4]
>>> for index in common_iterable(d):
    d[index] = 0

>>> d
{'a': 0, 'b': 0}
>>> for index in common_iterable(l):
    l[index] = 0

>>> l
[0, 0, 0, 0]

也许更有效率的方式是使用生成器:
def common_iterable(obj):
    if isinstance(obj, dict):
        for key in obj:
            yield key
    else:
        for index, value in enumerate(obj):
            yield index

这实际上不是一个“迭代器”,它只是将一个listdict转换为一个格式一致的可迭代对象。你可以使用next__iter__来定义一个迭代器工厂,以便实现迭代器协议。 - Silas Ray
@sr2222:好的,它是可迭代的。我也可以将其制作成生成器,可能会提高一些性能。 - Tadeck
你的 common_iterable 的第二个版本是不必要的。第一个版本中的 (index for index, value in enumerate(obj)) 已经是一个生成器,而 for key in obj: yield key 只是在第一个版本的 common_iterable 中增加了额外的开销。 - Justin Johnson

5
为了更符合 Pythonic 和 Duck Typing 的风格,并且遵循“宁愿请求原谅,不要寻求许可”的原则,你可以这样做:
try:
    iterator = obj.iteritems()
except AttributeError:
    iterator = enumerate(obj)
for reference, value in iterator:
    do_loop_contents(obj, reference)

如果你只需要键或索引:

try:
    references = obj.keys()
except AttributeError:
    references = range(len(obj))
for reference in references:
    do_loop_contents(obj, reference)

或者作为一个函数:
def reference_and_value_iterator(iterable):
    try:
        return iterable.iteritems()
    except AttributeError:
        return enumerate(iterable)

for reference, value in reference_and_value_iterator(obj):
    do_loop_contents(obj, reference)

或者只是引用:

def references(iterable):
    try:
        return iterable.keys()
    except AttributeError:
        return range(len(iterable))

for reference in references(obj):
    do_loop_contents(obj, reference)

1
test_list = [2, 3, 4]
for i, entry in enumerate(test_list):
    test_list[i] = entry * 2
print(test_list)  # Gives: [4, 6, 8]

但是您可能想要一个列表推导式:

test_list = [2, 3, 4]
test_list = [entry * 2 for entry in test_list]
print(test_list)  # Gives: [4, 6, 8]

1

你可能只是想根据你要更改的对象是字典还是列表来使用不同的代码。

if type(object)==type([]):
    for key in range(len(object)):
        object[key]=1
elif type(object)==type({}): #use 'else' if you know that object will be a dict if not a list
    for key in object:
        object[key]=1

0

在寻找更好的帖子时,我偶然发现了这篇文章,以下是我的做法。

for row in [dict_or_list] if not type(dict_or_list) is list else dict_or_list:
    for i,v in row.items():
        print(i,v)

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