假设我有一个元素为列表的字典。就像
{ label1:[ el1 ,el2,el3], label2:[el4,el5]}
我把它放在一个变量对象中。我的目标是到达元素el1, el2等,并对它们进行一些操作。
我可以通过
if isinstance(object,dict):
for k,v in object.items():
print(v) #<--this is the list
for el in v:
apply_op(el)
这一切都很好,但如果我想让这个算法适用于不同的水平。例如,我用
代替上面的字典{ particle: { label1:[ el1 ,el2,el3], label2:[el4,el5]}}
正如你所看到的,这是更深一层。
我希望有一个算法仍然找到元素并应用操作,无论这些元素有多深或多浅。如果不为每个关卡编写特殊情况,我该如何做到这一点?
我想你应该在这里使用递归,就像这样。
def fun(d):
if isinstance(d, dict):
res = ""
for key, val in d.items():
res += fun(val)
return res
else:
return "-".join(d)
加入可以替换为您想要执行的操作。
d = {'label1': ['el1', 'el2', 'el3'], 'label2': ['el4', 'el5']}
x = {'particle': {'label1': ['el1', 'el2', 'el3'], 'label2': ['el4', 'el5']}}
y = y = {"something": {'particle': {'label1': ['el1', 'el2', 'el3'], 'label2': ['el4', 'el5']}, "more": {"nums": ["num1", "num2"]}}}
fun(d) # el1-el2-el3el4-el5
fun(x) # el1-el2-el3el4-el5
fun(y) # el1-el2-el3el4-el5num1-num2
正如建议的那样,递归似乎是一个很好的策略:
def apply(foo, d):
for k, v in d.items():
if isinstance(v, dict):
apply(foo, v)
else:
foo(v)
因此,无论何时发现dict
,都会对apply
进行新的调用,无论深度如何。例如:
def append999(l):
l.append(999)
d = {'adict': {'1':[1], '2':[2], 'bdict':{3:[3]}}}
apply(append999, d)
print(d)
结果:
{'adict': {'1': [1, 999], '2': [2, 999], 'bdict': {3: [3, 999]}}}
如果内部列表包含可变对象(不像int
),那么你可以在列表的所有元素上进行循环,从而改变元素。下面是这样一个示例,其中apply
也被修改为允许将*args
和**kwargs
传递给应用的函数:
def apply(foo, d, *args, **kwargs):
for k, v in d.items():
if isinstance(v, dict):
apply(foo, v, *args, **kwargs)
else:
# now we are looping over the assumed lists
for i in v:
# and applying with extra arguments
foo(i, *args, **kwargs)
# here is a basic mutable object
class Bar:
def __init__(self, x):
self.x = x
def __repr__(self):
return f"Bar({self.x})"
# here is a function to increase our mutable object
def increase(bar, amount):
bar.x += amount
d = {'adict': {'1':[Bar(1)], '2':[Bar(2)], 'bdict':{3:[Bar(3)]}}}
apply(increase, d, amount=10)
print(d)
结果:
{'adict': {'1': [Bar(11)], '2': [Bar(12)], 'bdict': {3: [Bar(13)]}}}
这里必须使用递归。假设您希望对映射和列表或任何其他可迭代对象中任何深度的标量值(包括unicode和字节字符串)应用一些处理,您可以这样写:
import collections.abc
def deep_process(obj):
# scalars, including unicode and byte strings
if isinstance(obj, (str, bytes))or not isinstance(obj, collections.abc.Iterable):
apply_op(obj)
# mappings
elif isinstance(obj, collections.abc.Mapping):
for v in obj.values():
deep_process(v)
# other iterables like lists or tuples
else:
for v in obj:
deep_process(v)
Demo (withapply_op = print
)
deep_process({'particle': {'label1': ['el1', 'el2', 'el3'], 'label2': ['el4', 'el5']}})
el1
el2
el3
el4
el5
有一个有趣的工具叫做透镜,用于遍历和操作嵌套的数据结构。
from lenses import lens
def apply_op(n):
return n + 1
# create a lens object that will find all lists recursively
find_lists = lens.Recur(list)
data = {'particle': {'label1': [1, 2, 3], 'label2': [4, 5]}}
# apply the operation to each item of all lists
new_data = find_lists.Each().modify(apply_op)(data)
结果:
>>> new_data
{'particle': {'label1': [2, 3, 4], 'label2': [5, 6]}}
原data
在此操作后保持完整:
>>> data
{'particle': {'label1': [1, 2, 3], 'label2': [4, 5]}}
如果您想就地修改data
,您必须使用collect
方法访问单个列表并自行更改它们:
for list_ in find_lists.collect()(data):
for i, item in enumerate(list_):
list_[i] = apply_op(item)
结果:
>>> data
{'particle': {'label1': [2, 3, 4], 'label2': [5, 6]}}