不使用isinstance()对嵌套列表进行递归处理

5 投票
5 回答
2005 浏览
提问于 2025-04-16 16:58

我刚刚读了“isinstance()被认为是有害的”这篇文章,觉得说得很有道理。简单来说,这篇文章建议我们尽量不要使用这个函数。

现在我正在写一个程序,这个程序需要处理树形结构的输入,并且需要知道树的结构信息。因为没有时间去做图形界面,我就要求用户把这些信息写到一个配置文件里(我知道这样不好,但时间真的很紧)。我的用户都是技术人员,但不一定都懂Python。我决定让这个文件包含嵌套的列表,来表示输入的树,最后的元素是树的叶子节点。我觉得这样比强迫用户使用字典的语法要好得多。

我打算递归地解析这些列表,下面是一个简化的示例(为了简单起见,假设每个叶子节点都需要调用一次处理函数 treatLeafNode()):

def parseTree(input):
    if isinstance (input, list):
        for item in input:
            parseTree(item)
    else:
        treatLeafNode(item)

考虑到这篇文章,我在想有没有简单的方法可以递归处理这些列表,而不使用 isinstance() 呢……

有没有人知道呢?

5 个回答

2

可能更好的方法是用一个节点对象来封装你的树结构,这个节点对象可以存储一个值和一组子节点:

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 代码到你的输入中了?

5

你可以使用

def parseTree(input):
    try:
        for item in input:
            parseTree(item)
    except TypeError:
        treatLeafNode(item)

注意,这样做也会遍历字符串哦。

10

你的情况正是我会使用 isinstance 的那种。你的数据结构很明确,你需要区分一个东西是不是列表。可以用 isinstance 来检查它是否是一个列表。你没有说,但我想你的树结构里可能有字符串,而字符串和列表一样都是可迭代的,所以在用鸭子类型的方法来区分它们时会有点麻烦。

撰写回答