我正在试图弄清楚如何映射包含字典和列表的递归结构,到目前为止我已经得到了这个:
import collections
def rec_walk(l):
for v in l:
if isinstance(v, list):
yield from rec_walk(v)
else:
yield v
def rec_map(l, f):
for v in l:
if isinstance(v, collections.Iterable):
if isinstance(v, list):
yield list(rec_map(v, f))
elif isinstance(v, dict):
yield dict(rec_map(v, f))
else:
yield f(v)
a = ["0", ["1", "2", ["3", "4"]], [[[[["5"]]]]]]
print(list(rec_map(a, lambda x: x + "_tweaked")))
b = {
'a': ["0", "1"],
'b': [[[[[["2"]]]]]],
'c': {
'd': [{
'e': [[[[[[["3"]]]]]]]
}]
}
}
print(dict(rec_map(b, lambda x: x + "_tweaked")))
输出:
[[[]], [[[[[]]]]]]
{}
正如您所看到的,上面示例的问题是rec_map没有返回正确映射的结构,我想要获得的是正确映射的相同结构或新的克隆映射结构,例如,类似这样:
a = ["0", ["1", "2", ["3", "4"]], [[[[["5"]]]]]]
rec_map(a, lambda x: x + "_tweaked")
应将a
转换为:
["0_tweaked", ["1_tweaked", "2_tweaked", ["3_tweaked", "4_tweaked"]], [[[[["5_tweaked"]]]]]]
和
b = {
'a': ["0", "1"],
'b': [[[[[["2"]]]]]],
'c': {
'd': [{
'e': [[[[[[["3"]]]]]]]
}]
}
}
print(dict(rec_map(b, lambda x: x + "_tweaked")))
成:
b = {
'a': ["0_tweaked", "1_tweaked"],
'b': [[[[[["2_tweaked"]]]]]],
'c': {
'd': [{
'e': [[[[[[["3_tweaked"]]]]]]]
}]
}
}
答案 0 :(得分:1)
这是由于yield from
。您应该使用yield list()
代替。
从每个元素一次一个地生成每个元素,但是你想要的是产生整个列表而不是它的每个元素。
what's the difference between yield from and yield in python 3.3.2+这个问题解释了差异。
以下修改后的代码版本会生成您想要的行为:
def rec_walk(l):
for v in l:
if isinstance(v, list):
yield list(rec_walk(v))
else:
yield v
def rec_map(l, f):
for v in l:
if isinstance(v, list):
yield list(rec_map(v, f))
else:
yield f(v)
a = ["0", ["1", "2", ["3", "4"]], [[[[["5"]]]]]]
print('-' * 80)
print(list(rec_walk(a)))
print('-' * 80)
print(list(rec_map(a, lambda x: x + "_tweaked")))
答案 1 :(得分:1)
您正在创建一个生成器,然后使用yield from
,它基本上变平。相反,你需要实现生成器而不是从它产生:
In [1]: def rec_map(l, f):
...: for v in l:
...: if isinstance(v, list):
...: yield list(rec_map(v, f))
...: else:
...: yield f(v)
...:
In [2]: a = ["0", ["1", "2", ["3", "4"]], [[[[["5"]]]]]]
...:
In [3]: list(rec_map(a, lambda x: x + "_tweaked"))
Out[3]:
['0_tweaked',
['1_tweaked', '2_tweaked', ['3_tweaked', '4_tweaked']],
[[[[['5_tweaked']]]]]]
您遇到的问题是使用生成器执行此操作要困难得多,因为您必须仔细策划返回的内容。老实说,看起来你甚至不需要发电机,只需使用:
In [16]: def rec_map(l, f):
...: if isinstance(l, list):
...: return [rec_map(v, f) for v in l]
...: elif isinstance(l, dict):
...: return {k:rec_map(v, f) for k,v in l.items()}
...: else:
...: return f(l)
...:
In [17]: rec_map(b, lambda x: x + '_tweaked')
Out[17]:
{'a': ['0_tweaked', '1_tweaked'],
'b': [[[[[['2_tweaked']]]]]],
'c': {'d': [{'e': [[[[[[['3_tweaked']]]]]]]}]}}
此外,请勿使用collections.Iterable
,请明确检查您正在处理的ypes。注意:
In [18]: isinstance('I am a string but I am iterable!', collections.Iterable)
Out[18]: True