[英]Traversing of arbitrary order tree
我知道以前有人问过这个问题,并且我已经看到了答案,但仍然无法弄清楚发生了什么。
我正在尝试根据文件的某些元数据(日期和位置)和一组条件有条件地构建文件夹结构。 例如,为了测试我正在使用这些:
COND = ["Y", "m", "C"]
这意味着在文件夹结构中文件需要首先按年份拆分文件,然后是日历月,然后是原产国。
这是我为测试创建的示例数据:
data = [
["111", dt.datetime(2019, 1, 1), "Aus", "Bri"],
["112", dt.datetime(2019, 1, 5), "Aus", "Bri"],
["113", dt.datetime(2019, 2, 10), "Aus", "Mel"],
["114", dt.datetime(2020, 1, 1), "Aus", "Per"],
["115", dt.datetime(2020, 1, 10), "Aus", "Per"],
["116", dt.datetime(2020, 1, 25), "Aus", "Per"],
["117", dt.datetime(2020, 10, 5), "My", "KL"],
["118", dt.datetime(2020, 11, 6), "Ru", "Led"],
["119", dt.datetime(2020, 12, 1), "Ru", "Mos"],
["120", dt.datetime(2021, 3, 5), "Aus", "Syd"],
["121", dt.datetime(2021, 5, 1), "Aus", "Mel"],
["122", dt.datetime(2021, 6, 1), "Aus", "Per"],
["123", dt.datetime(2021, 11, 1), "Chi", "Bei"],
["124", dt.datetime(2021, 11, 15), "Jp", "Tok"],
["125", dt.datetime(2022, 1, 1), "Aus", "Per"],
["126", dt.datetime(2022, 3, 1), "Aus", "Bri"],
["127", dt.datetime(2022, 3, 5), "Aus", "Per"],
["128", dt.datetime(2022, 3, 11), "My", "KL"],
["129", dt.datetime(2022, 5, 1), "Aus", "Syd"],
["130", dt.datetime(2022, 8, 8), "Aus", "Bri"],
]
这些简单的函数执行过滤:
def filter_year(data: list[list[str | dt.datetime]]) -> list[int]:
return {i[1].year for i in data}
def filter_month(data: list[list[str | dt.datetime]]) -> list[int]:
return {i[1].month for i in data}
def filter_day(data: list[list[str | dt.datetime]]) -> list[int]:
return {i[1].day for i in data}
def filter_country(data: list[list[str | dt.datetime]]) -> list[str]:
return {i[2] for i in data}
def filter_city(data: list[list[str | dt.datetime]]) -> list[str]:
return {i[3] for i in data}
condition_dict = {
"Y": {'fun': filter_year, 'id': 1 },
"m": {'fun': filter_month,'id': 1 },
"d": {'fun': filter_day,'id': 1},
"C": {'fun': filter_country, 'id': 2},
"c": {'fun': filter_city, 'id': 3 }
我正在尝试使用任意顺序树自动构建结构。 节点上的数据拆分工作正常:
from typing import Any
from pathlib import Path
from dataclasses import dataclass, field
@dataclass
class Node:
folder: Path
metadata: list[list[Any]] = field(default_factory=list)
conditions: list[str] = field(default_factory=list)
@property
def children(self) -> list['Node']:
if len(self.conditions) == 0:
return []
current_condition = self.conditions[0]
fun = condition_dict[current_condition]['fun']
fnames: list[int | str] = fun(self.metadata)
children_data = {str(n): {} for n in fnames}
for f in fnames:
children_data[str(f)]['folder'] = self.folder / str(f)
children_data[str(f)]['conditions'] = self.conditions[1:]
if current_condition == 'Y':
children_data[str(f)]['metadata'] = [i for i in self.metadata if i[1].year == f]
elif current_condition == 'm':
children_data[str(f)]['metadata'] = [i for i in self.metadata if i[1].month == f]
elif current_condition == 'd':
children_data[str(f)]['metadata'] = [i for i in self.metadata if i[1].day == f]
elif current_condition == 'C':
children_data[str(f)]['metadata'] = [i for i in self.metadata if i[2] == f]
elif current_condition == 'c':
children_data[str(f)]['metadata'] = [i for i in self.metadata if i[3] == f]
return [Node(**i) for i in children_data.values()]
现在,我正在尝试遍历我使用此处答案的修改版本的树( 遍历非二叉树)
@dataclass
class Tree:
def traverse(self, root: Node):
r = root.children
if not r or len(root.conditions) == 0:
print('The end of subtree:', root.folder)
else:
for child in r:
print('\n'.join(str(i.folder) for i in r))
if isinstance(child, Node):
for x in self.traverse(child):
print(str(x.folder))
else:
print(child)
但是当我在几次正确输出后尝试使用我的数据时,我总是遇到错误NoneType is not iterable
:
n = Node(folder=Path('/home'), metadata=data, conditions=COND)
tree = Tree()
tree.traverse(n)
输出:
/home/2019
/home/2020
/home/2021
/home/2022
/home/2019/1
/home/2019/2
/home/2019/1/Aus
The end of subtree: /home/2019/1/Aus
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/home/pavel/python/photo_manager/temp/tree_test.ipynb Cell 4 in <cell line: 4>()
1 n = Node(folder=Path('/home'), metadata=data, conditions=COND)
3 tree = Tree()
----> 4 tree.traverse(n)
/home/pavel/python/photo_manager/temp/tree_test.ipynb Cell 4 in Tree.traverse(self, root)
45 print('\n'.join(str(i.folder) for i in r))
46 if isinstance(child, Node):
---> 47 for x in self.traverse(child):
48 print(str(x.folder))
49 else:
/home/pavel/python/photo_manager/temp/tree_test.ipynb Cell 4 in Tree.traverse(self, root)
45 print('\n'.join(str(i.folder) for i in r))
46 if isinstance(child, Node):
---> 47 for x in self.traverse(child):
48 print(str(x.folder))
49 else:
/home/pavel/python/photo_manager/temp/tree_test.ipynb Cell 4 in Tree.traverse(self, root)
45 print('\n'.join(str(i.folder) for i in r))
46 if isinstance(child, Node):
---> 47 for x in self.traverse(child):
48 print(str(x.folder))
49 else:
TypeError: 'NoneType' object is not iterable
我不明白为什么会发生这种情况,因为我相信我提防了 NoneType。 出于某种原因,我只到达一个子树的末尾,但没有遍历其他子树。 我在这里做错了什么?
我并没有真正了解整个故事,但是您在这一行中遇到的错误是预料之中的:
for x in self.traverse(child):
问题是self.traverse
没有return
语句,所以这个递归调用返回None
,并且for x in None
没有意义。
我认为您实际上不想从该递归调用中获取一些x
值,因为该递归调用负责自己的业务。 无需再次打印该递归调用已打印的内容。
这里还有第二个问题:
for child in r:
print('\n'.join(str(i.folder) for i in r))
在这里,对于r
中的每个孩子,您在print
调用中再次迭代r
。 那只会打印重复项。 您只需从r
打印当前子项。 这会使它下面的else
块过时:当您刚刚打印child.folder
时,似乎没有必要再次打印child
。
因此,纠正这两个问题,以下至少运行没有错误:
@dataclass
class Tree:
def traverse(self, root: Node):
r = root.children
if not r or len(root.conditions) == 0:
print('The end of subtree:', root.folder)
else:
for child in r:
print(str(child.folder))
if isinstance(child, Node):
self.traverse(child)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.