目标要求
解析“中国地名表.json”,并实现如下功能:
- 根据给定的中国地名,判断该地名是否存在;若地名不存在,返回None;若地名存在,则给出该地名所属的上级地名和该地名包含的下级地名。例如,给出“秦皇岛市”,返回其上级地名“河北省”以及其下级地名“海港区”、“山海关区”等。
- 对于给定的中国地名A,判断该地名是否存在;若地名不存在,返回None;若地名存在,则返回该地名所属的各级上级地名。例如,给出“海港区”,返回其所有上级地名“中国”、“河北省”、“秦皇岛市”。
(若出现重复地名则默认保留更高等级的地名)
目标实现
构造地名节点类
根据目标要求,我们构造节点(Node对象)来存储地名,节点(Node对象)包括四个属性,分别为节点地名(name)、节点地名等级(level)、父节点对象(father)和子节点对象列表(children),这样就构造成了地名节点树。当我们需要当前地名(节点)的上级地名时,只需要返回父节点的name属性即可;当我们需要当前地名(节点)的下级地名时,只需要返回所有子节点的name属性即可。
当我们构造地名节点树时,从根节点(最高级的地名)开始构造,而后所有新节点都在上层节点中构造(add_child方法)。
class Node: # 地名节点类
def __init__(self, name, level, father=None):
self._name = name
self._level = level
self._father = father
self._children = []
def add_child(self, name): # 添加子节点
child_node = Node(name, self)
self._children.append(child_node)
return child_node
def pedigree(self): # 获取所有父节点列表(包括当前节点)
if self.father() is not None:
return self.father().pedigree() + [self]
else:
return [self]
def father(self):
return self._father
def children(self):
return self._children
def level(self):
return self._level
def __str__(self):
return self._name
其中add_child
方法用于构造地名节点树;father
方法、children
方法是为了实现给出当前地名的上级地名和下级地名;pedigree
方法是为了实现给定当前地名的所有上级地名。
但是,仅依靠地名节点树,我们仍无法方便地实现判断一个地名是否存在的需求。
构造地名节点树
为了实现判断地名是否存在,并快速找到地名,我们需要构造一个以key为地名,value为地名节点(Node对象)的字典。但是,地名中存在类似“中国-北京市-北京市-海淀区”的情况,对此,我们希望key为”北京市“的地名,对应的value是等级较高节点,而不是等级较低节点。
为此,我们构造了地名节点树(Tree对象)来管理地名节点,并使用区分节点等级的列表(node_list),来存储各个等级地名和地名节点的对应字典(node_list中的元素)。
class Tree: # 地名节点树
def __init__(self, root: str, level_grade: int):
"""
:param root: <str> 根节点名称
:param level_grade: <int> 节点总层级数
"""
self.root_node = Node(root, 0)
self.node_list = [{} for _ in range(level_grade)]
self.node_list[0][root] = self.root_node
def add_child(self, name: str, node: Node = None):
if node is None:
node = self.root_node
child_node = node.add_child(name)
self.node_list[node.level() + 1][name] = child_node
return child_node
def get_node(self, name: str, level: int = None):
if level is None:
for level_node_list in self.node_list:
if name in level_node_list:
return level_node_list[name]
else:
if name in self.node_list[level]:
return self.node_list[level][name]
return None
其中,add_child
方法用于构造地名节点树时添加子节点,get_node
用于在地名节点树中查询地名是否存在并返回对应节点(Node对象)。
解析“中国地名表.json”
“中国地名表.json”的结构大体如下,每个层级的对象有两个键,第一个键(name)为当前层级的地名,第二个键(province、city或area)为当前地名包含的下层地名;若当前地名只包含一个下层地名,则第二个键的值为对象,否则第二个键的值为数组。
{
"root": {
"name": "中国",
"province": [
{
"name": "北京市",
"city": {
"name": "北京市",
"area": [...]
}
}, {
"name": "天津市",
"city": {...}
}, {
"name": "河北省",
"city": [
{
"name": "石家庄市",
"area": [
{
"name": "长安区"
}, {
"name": "桥东区"
},
......
具体的,我们使用如下代码进行解析:
def load_place():
"""
载入中国地名表并转换为节点树的形式
:return: <dict>省级节点; <dict> 地级节点; <dict> 县级节点
"""
# 载入“中国地名表.json”
with open("中国地名表.json", encoding="GBK") as file:
place_json = json.loads(file.read())
def add_city(city_json): # 处理市级结构
city_node = node_tree.add_child(city_json["name"], node=province_node)
if "area" in city_json:
if type(city_json["area"]) == list: # 处理包含多个县级市的地级市
for area in city_json["area"]:
node_tree.add_child(area["name"], node=city_node)
else: # 处理仅包含一个县级市的地级市
node_tree.add_child(city_json["area"]["name"], node=city_node)
# 将Json形式转换为节点树形式
node_tree = Tree(place_json["root"]["name"], 4) # 构建地名节点树
for province in place_json["root"]["province"]:
province_node = node_tree.add_child(province["name"])
if type(province["city"]) == list: # 处理省级行政区
for city in province["city"]:
add_city(city)
else: # 处理直辖市
add_city(province["city"])
return node_tree
运行测试
最后,我们测试10个不同级别的地名,检查是否完成了需求中的要求。
if __name__ == "__main__":
PLACE_TREE = load_place() # 载入中国地名表并转换为节点树的形式
demo_place = ["海淀区", "铜梁县", "成都市", "枣阳市", "孝感市", "绥化市", "察哈尔右翼中旗", "四川省", "宾川县", "阿拉尔市"]
for place_name in demo_place:
if (place_node := PLACE_TREE.get_node(place_name)) is not None:
print(place_node, place_node.father(), str([str(child) for child in place_node.children()]))
print(place_node, "-".join([str(node) for node in place_node.pedigree()]))
else:
print("地名不存在")
运行结果:
海淀区 北京市 []
海淀区 中国-北京市-北京市-海淀区
铜梁县 重庆市 []
铜梁县 中国-重庆市-重庆市-铜梁县
成都市 四川省 ['锦江区', '青羊区', '金牛区', '武侯区', '成华区', '龙泉驿区', '青白江区', '金堂县', '双流县', '温江县', '郫县', '新都县', '大邑县', '蒲江县', '新津县', '都江堰市', '彭州市', '邛崃市', '崇州市']
成都市 中国-四川省-成都市
枣阳市 襄樊市 []
枣阳市 中国-湖北省-襄樊市-枣阳市
孝感市 湖北省 ['孝南区', '孝昌县', '大悟县', '云梦县', '应城市', '安陆市', '汉川市']
孝感市 中国-湖北省-孝感市
绥化市 黑龙江省 ['绥化市', '安达市', '肇东市', '海伦市', '望奎县', '兰西县', '青冈县', '庆安县', '明水县', '绥棱县']
绥化市 中国-黑龙江省-绥化市
察哈尔右翼中旗 乌兰察布盟 []
察哈尔右翼中旗 中国-内蒙古-乌兰察布盟-察哈尔右翼中旗
四川省 中国 ['成都市', '自贡市', '攀枝花市', '泸州市', '德阳市', '绵阳市', '广元市', '遂宁市', '内江市', '乐山市', '南充市', '宜宾市', '广安市', '达川地区', '雅安地区', '阿坝藏族羌族自治州', '甘孜藏族自治州', '凉山彝族自治州', '巴中地区', '眉山地区', '资阳地区']
四川省 中国-四川省
宾川县 大理白族自治州 []
宾川县 中国-云南省-大理白族自治州-宾川县
阿拉尔市 省直辖行政单位 []
阿拉尔市 中国-新疆-省直辖行政单位-阿拉尔市