我刚读过"isinstance() considered harmful",看起来很合理。简而言之,它主张避免使用这个功能。
好吧,刚才我正在编写一个程序,它将输入结构化为树,并且需要树的结构信息。没有时间实现GUI,我强加给用户将其写入配置文件(我知道这是一个糟糕的接口,但时间表非常紧张)。我的用户非常技术,但不一定知道python。我选择该文件将包含表示输入树的列表(列表列表等)列表,最终元素是树的叶节点。我认为这比在用户上强加字典的合成要好得多。
我打算递归地解析列表如下(省略树的结构的使用,让我们简化并说必须在每个叶子节点上调用treatLeafNode()):
def parseTree(input):
if isinstance (input, list):
for item in input:
parseTree(item)
else:
treatLeafNode(item)
根据这篇文章,我想知道是否有一种简单的方法可以在不使用isinstance()的情况下进行递归...
有人知道吗?
答案 0 :(得分:10)
您的情况是我使用isinstance
的情况之一。您的数据结构受到很好的约束,您需要区分列表而不是列表。使用isinstance
询问它是否为列表。你没有说,但是我想象字符串可能在树的叶子中,并且它们可以像列表那样迭代,所以用鸭子打字方式来区分它们是非常繁琐的。
答案 1 :(得分:5)
您可以使用
def parseTree(input):
try:
for item in input:
parseTree(item)
except TypeError:
treatLeafNode(item)
请注意,这也会迭代字符串。
答案 2 :(得分:2)
更好的方法是使用Node对象封装树结构,Node对象可以包含值和子列表:
class Node(object):
def __init__(self, children=[], value=None):
self.children = children
self.value = value
def isLeaf(self):
return len(self.children) == 0
现在一个节点显式地是带有值的叶子或带子节点的元素(在技术上,非叶子节点在这个例子中也可以有值,但是你的应用程序代码可以选择强制非叶子节点永远不会有值)。 parseTree
可以写成:
def parseTree(node):
if node.isLeaf():
treatLeafNode(node)
else:
for child in node.children:
parseTree(child)
请注意,这是树上的深度优先搜索。
可能有更好的方法来解决这个问题,因此parseTree
是Node
的方法,但这应该会给你一个想法。当然,您仍然遇到问题,即您要求用户编写Python代码,这些代码是列表作为输入,并将其解析为上面的树结构,您需要使用isinstance
。也许yaml会更好地选择描述语言,因为用户不能在输入中注入任意Python代码?
答案 3 :(得分:0)
如何使用yaml?您也不必亲自进行验证和解析逻辑。
树可能看起来像
- [[aMAN],[sTACK, OVER],[FLOW]]
- [[aMAN1],[sTACK1, OVER1],[FLOW1]]
- [[aMAN2],[sTACK2, OVER2],[FLOW2]]
解析它的代码
import yaml
f= open('test.yml')
test = yaml.load(f.read())
print test
输出:
[[['aMAN'], ['sTACK', 'OVER'], ['FLOW']], [['aMAN1'], ['sTACK1', 'OVER1'], ['FLOW1']], [['aMAN2'], ['sTACK2', 'OVER2'], ['FLOW2']]]
答案 4 :(得分:0)
您是否有理由选择列表列表作为首选树结构?我可以想到在配置文件中编写一个更好的方法。假设您正在尝试编码:
a
|-- b
| |-- c
| |-- d
| | |-- e
| | `-- f
| `-- g
| `-- h
|-- i
`-- j
`-- k
怎么样
a: b, i, j
b: c, d, g
d: e, f
g: h
j: k
您可以非常轻松地将其解析为字典,并将其连接到树中。事实上,我认为ConfigParser
已经为你做了。
或者,怎么样:
a
----b
--------c
--------d
------------e
------------f
--------g
------------h
----i
----j
--------k