不使用isinstance()对嵌套列表进行递归处理
我刚刚读了“isinstance()被认为是有害的”这篇文章,觉得说得很有道理。简单来说,这篇文章建议我们尽量不要使用这个函数。
现在我正在写一个程序,这个程序需要处理树形结构的输入,并且需要知道树的结构信息。因为没有时间去做图形界面,我就要求用户把这些信息写到一个配置文件里(我知道这样不好,但时间真的很紧)。我的用户都是技术人员,但不一定都懂Python。我决定让这个文件包含嵌套的列表,来表示输入的树,最后的元素是树的叶子节点。我觉得这样比强迫用户使用字典的语法要好得多。
我打算递归地解析这些列表,下面是一个简化的示例(为了简单起见,假设每个叶子节点都需要调用一次处理函数 treatLeafNode()):
def parseTree(input):
if isinstance (input, list):
for item in input:
parseTree(item)
else:
treatLeafNode(item)
考虑到这篇文章,我在想有没有简单的方法可以递归处理这些列表,而不使用 isinstance() 呢……
有没有人知道呢?
5 个回答
可能更好的方法是用一个节点对象来封装你的树结构,这个节点对象可以存储一个值和一组子节点:
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 代码到你的输入中了?
你可以使用
def parseTree(input):
try:
for item in input:
parseTree(item)
except TypeError:
treatLeafNode(item)
注意,这样做也会遍历字符串哦。
你的情况正是我会使用 isinstance
的那种。你的数据结构很明确,你需要区分一个东西是不是列表。可以用 isinstance
来检查它是否是一个列表。你没有说,但我想你的树结构里可能有字符串,而字符串和列表一样都是可迭代的,所以在用鸭子类型的方法来区分它们时会有点麻烦。