迭代 Python 中的字典或列表
Posted
技术标签:
【中文标题】迭代 Python 中的字典或列表【英文标题】:Iterate over a dict or list in Python 【发布时间】:2012-09-01 18:43:29 【问题描述】:刚刚编写了一些讨厌的代码,在 Python 中迭代 dict
或 list
。我有一种感觉,这不是最好的方法。
问题是为了迭代一个字典,这是惯例:
for key in dict_object:
dict_object[key] = 1
但是如果在一个列表上做同样的事情,通过key修改对象属性是行不通的:
# 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
有没有更好的方法来做到这一点?
谢谢!
【问题讨论】:
【参考方案1】:我在寻找更好的帖子时偶然发现了这篇文章,这就是我的做法。
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)
【讨论】:
【参考方案2】:您可能只想根据您要更改的对象是字典还是列表来使用不同的代码。
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
【讨论】:
【参考方案3】:我从来不需要这样做,从来没有。但如果我这样做了,我可能会这样做:
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
该行不应该是return obj if isinstance(obj, dict) else xrange(len(obj))
吗?
@tobias_k:很好的收获! Python 的作用域与内联编辑相得益彰。 :^)【参考方案4】:
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]
【讨论】:
【参考方案5】:要成为pythonic和ducktype-y,并且要遵循“请求原谅而不是许可”,您可以执行以下操作:
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)
【讨论】:
【参考方案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
【讨论】:
这实际上不是iterator
,它只是将list
或dict
转换为格式一致的可迭代对象。不过,您可以使用 next
和 __iter__
使用迭代器协议定义一个迭代器工厂。
@sr2222:好的,它是可迭代的。我也可以把它做成一个生成器,可能会稍微提高性能。
您的common_iterable
的第二个版本是不必要的。第一个版本中的(index for index, value in enumerate(obj))
已经是一个生成器,而for key in obj: yield key
只是在common_iterable
的第一个版本中完全正常的情况下增加了开销以上是关于迭代 Python 中的字典或列表的主要内容,如果未能解决你的问题,请参考以下文章
Python 字典items返回列表,iteritems返回迭代器