Python:树结构和数字代码?
Posted
技术标签:
【中文标题】Python:树结构和数字代码?【英文标题】:Python: tree structure and numerical codes? 【发布时间】:2010-09-20 16:58:25 【问题描述】:我正在使用 Python,我想将一些数据放入树格式并为其分配代码。以下是一些示例数据:
Africa North Africa Algeria
Africa North Africa Morocco
Africa West Africa Ghana
Africa West Africa Sierra Leone
什么是适合该数据的树结构?
另外,有没有一种方法可以让我从这个树结构中检索数字代码,这样我就可以查询数据并获取如下示例的代码?
def get_code(place_name):
# Python magic query to my tree structure
return code
get_code("Africa") # returns 1
get_code("North Africa") # returns 1.1
get_code("Morocco") # returns 1.1.2
感谢您的帮助 - 关于 Python,我还有很多东西要学习 :)
【问题讨论】:
我们对 python 的了解也很多。:) 【参考方案1】:我建议,假设您可以指望名称之间没有重复,例如:
class Node(object):
byname =
def __init__(self, name, parent=None):
self.name = name
self.parent = parent
self.children = []
self.byname[name] = self
if parent is None: # root pseudo-node
self.code = 0
else: # all normal nodes
self.parent.children.append(self)
self.code = len(self.parent.children)
def get_codes(self, codelist):
if self.code:
codelist.append(str(self.code))
self.parent.get_codes(codelist)
root = Node('')
def get_code(nodename):
node = Node.byname.get(nodename)
if node is None: return ''
codes = []
node.get_codes(codes)
codes.reverse()
return '.'.join(codes)
您是否还想查看 Python 代码,了解如何在给定名称的分层序列(例如 ['Africa', 'North Africa', 'Morocco']
)的情况下添加节点?我希望考虑到上述结构,它会很清楚,所以你可能想自己做一个练习,但当然要问你是否更愿意看到一个解决方案;-)。
从文本行(字符串)中获取名称的分层序列取决于分隔符是什么——在您的示例中,它看起来只是出于与排列列相关的纯粹审美原因而添加的一堆空格(如果这是在这种情况下,我会推荐一个简单的基于re
的方法来拆分两个+ 空格的序列),但如果它实际上是(例如)制表符作为分隔符,Python 标准库中的csv
模块会更好地为您服务。我只是无法从您在 Q 中发布的简短示例中看出!-)
编辑:OP 说他们可以很好地获取名称序列,但希望查看添加相关节点的代码 - 所以,这里开始!-)
def addnodes(names):
parent = root
for name in names:
newnode = Node.byname.get(name)
if newnode is None:
newnode = Node(name, parent)
parent = newnode
了解为什么节点名称是唯一的对于使上述类正常工作很重要?由于Node.byname
是每个类的单个dict
,它只能为每个给定名称记录一个“对应节点”——因此,在层次结构中的两个或多个位置重复的名称将“冲突”并且只有一个两个或多个节点中的一个将被正确记录。
但是话又说回来,OP 所说的函数get_code
是如果名称可能模棱两可,整个设备无法按预期工作的主要原因,因为 OP 的规范要求它只返回 one 字符串。所以,一些地理列表,如
America United States Georgia
Europe Eastern Europe Georgia
(其中两个完全不相关的区域恰好都被命名为'Georgia'
——不幸的是,这种事情在现实世界的地理中经常发生,如上例所示!-)会破坏整个方案(取决于 get_code
的规范如何更改以处理不明确的名称参数,当然,类结构肯定可以相应地更改并适应新的、截然不同的规范!)。
将这些设计决策封装在一个类中的好处(尽管在这种情况下有几个附带的函数——当然,它们可以优雅地制成类方法,但是 OP 的规范严格要求 get_code
是一个函数,所以我决定,在这种情况下,addnodes
也可能是一个!-) 是具体的设计决策大多隐藏在其余代码中,因此可以很容易地改变(只要规范当然,永远不要改变——这就是为什么花时间和注意力来定义一个人的 API 规范如此重要的原因,比设计和编码的任何其他部分更重要!-)重构内部行为(例如用于优化、易于调试/测试等),同时保持 API 指定的语义完整,从而使应用程序的所有其他部分保持原始状态(实际上甚至不需要重新测试,当然只要实现的部分API 经过了非常彻底的单元测试——不难做到,因为它们很好地隔离和独立!-)。
【讨论】:
re
的使用有必要吗? csv
可以用skipinitialspace
处理多个空格,但更简单:''.split
可以很好地分割。
这看起来很棒,谢谢! :) 是的,请您举例说明如何在给定列表的情况下添加节点?我可以很好地从字符串中获取列表,但我正在为如何将它变成一个节点而摸不着头脑。
@AP257: 提示:上面的构造函数__init__
将parent
作为参数。所以你可能想要构造父节点,然后是它的子节点,依此类推。
@Muhammad,写下您的第一条评论:按空格、任意空格序列或什至在带有跳过初始空格的 CSV 中分割,例如将West
和Africa
制作成单独的 字符串——难道你不明白OP 打算将'West Africa'
作为一个节点名称是多么彻底的灾难吗?!跨度>
@Alex:是的,我错过了 2+ 个空格。如果简单的字符串方法有效,我将避免使用re
,但这一次我被误导了。对不起! 躲在岩石下【参考方案2】:
一个表示树的临时 POD(“普通旧数据”)类就可以了,类似于:
class Location(object):
def __init__(self, data, parent)
self.data = data
self.parent = parent
self.children = []
现在分配/读取data
属性,或添加/删除子项,可能使用辅助方法:
def add_child(self, child):
self.children.append(child)
现在,要将您的数据实际划分为树级别,一个简单的算法是查看所有具有共同级别数据的地方(例如非洲)并为其分配一个位置,然后递归地获取下一个级别的数据。
因此,对于非洲,您创建一个数据 = 非洲的位置。然后,它将有一个北非、西非等的 Location 子节点。
对于“获取代码”,有一个字典将每个国家/地区映射到其位置节点,并使用节点中的父链接。在每个级别从节点遍历到顶部(直到父节点为无),将代码部分分配为父节点的子节点列表中的索引。
【讨论】:
【参考方案3】:我不确定,如果我做对了。如果我们将每个对象都保存在一个全局字典中,那么它就违背了使用树的目的,树仅用于构建编号方案。 但是基于树的表示看起来像这样:
class Location(object):
allLocation =
def __init__(self, name):
self.name = name
self.parent = None
self.number = "0"
self.children =
def putChild(self, childLocation):
if childLocation.name not in self.allLocation.keys():
# Now adjust the number scheme
#if self.number is "0":
# this is root
numScheme = str(len(self.children) + 1)
childLocation.setNumber(numScheme)
# Add the child
self.children[childLocation.number] = childLocation
self.allLocation[childLocation.name] = childLocation
childLocation.parent = self
return 0
else:
return 1 # Location already a child of the current clocation
def setNumber(self, num):
if self.number is not "0":
# raise an exception, number already adjusted
pass
else:
# set the number
self.number = num
def locateChild(self, numScheme):
# Logic should be to break up the numScheme and pass the token successively
numSchemeList = []
if type(numScheme) is str:
numSchemeList = numScheme.split(".")
else:
numSchemeList = numScheme
if len(numSchemeList) >= 1:
k = numSchemeList.pop()
# if the child is available
if k in self.children.keys():
childReferenced = self.children[k]
# Is child of child required
if len(numSchemeList) >= 1:
return childReferenced.locateChild(numSchemeList)
else:
return childReferenced
else:
# No such child
return None
else:
# The list is empty , search ends here
return None
def getScheme(self, name):
if name in self.allLocation.keys():
locObj = self.allLocation[name]
return locObj.getNumScheme(name, "")
else:
return None
def getNumScheme(self, name, numScheme="0",):
if not self.parent:
return numScheme
if numScheme != "":
return self.parent.getNumScheme(name, self.number + "." + numScheme)
else:
return self.parent.getNumScheme(name, self.number )
root = Location("root")
africa = Location("Africa")
asia = Location("Asia")
america = Location("America")
root.putChild(africa)
root.putChild(asia)
root.putChild(america)
nafrica = Location("North Africa")
africa.putChild(nafrica)
nafrica.putChild(Location("Morrocco"))
obj = root.locateChild("1.1.1")
print obj.name
print root.getScheme("Morrocco")
【讨论】:
【参考方案4】:这段代码可能很可怕。但是,我只是想粘贴它,因为我已经花了一些时间:)
tree = file_to_list_of_tuples(thefile)
d =
i = 1
for continent, region, country in tree:
if continent not in d:
d[continent] = [i, 0, 0]
i += 1
cont_code = d[continent][0]
if region not in d:
max_reg_code = max( [y for x, y, z in d.values() if x==cont_code] )
d[region] = [cont_code, max_reg_code+1 , 0]
reg_code = d[region][1]
if country not in d:
max_country_code = max( [z for x, y, z in d.values() if x == cont_code and y== reg_code] )
d[country] = [cont_code, reg_code, max_country_code+1]
def get_code(x):
print d[x]
get_code 将打印列表,但您可以轻松地将它们以您想要的格式打印。
【讨论】:
【参考方案5】:你可以使用itertree包(我是作者):
from itertree import *
#1. create the tree:
root2=iTree('root')
root2.append(iTree('Africa'))
root2[0].append(iTree('North Africa'))
root2[0].append(iTree('West Africa'))
root2[0][0].append(iTree('Algeria'))
root2[0][0].append(iTree('Morocco'))
item=iTree('Ghana') # keep the item for easier access
root2[0][1].append(item)
root2[0][1].append(iTree('Sierra Leone'))
# get the index path information of an item:
print('"Ghana" item index path:',item.idx_path)
# you can also search for items:
result = root2.find(['**', 'Morroco'])
print('"Morroco" item index path:', result.idx_path)
执行脚本将交付:
"Ghana" item index path: [0, 1, 0]
"Morroco" item index path [0, 0, 1]
除此之外,您可以为每个项目添加额外的数据并进行过滤搜索。
【讨论】:
以上是关于Python:树结构和数字代码?的主要内容,如果未能解决你的问题,请参考以下文章
天天数据结构和算法PHP中trie数据结构的使用场景和代码实例
python环境下使用mysql数据及数据结构和二叉树算法(图)
数据结构与算法(Python)——常见数据结构Part5(二叉搜索树BST和AVL)