Pythonic 双分发来从模型中提取视图信息

46 阅读2分钟

在使用 PySide 的 Python 和 Qt 编写桌面应用程序时,需要显示一个树视图,其中的顶级项目是不同类型的对象,而不是它们的子项。具体来说,顶级项目是一个 Git 仓库,而它的子项是工作树中的目录。

对于仓库,我想显示它的路径和当前检出的分支。对于目录,我只想显示它的名称。

目前,我通过让我的 QAbstractItemModel 子类在底层模型对象(从 internalPointer() 方法检索)上使用 isinstance 来实现这一点,并决定如何格式化结果字符串。

我想知道有没有更 Pythonic (或只是不那么笨拙)的方法来执行这种双重分派。我不想为此目的在我的模型类中定义一个方法,用于 Git 仓库和工作树文件,因为我觉得这会违反 SRP。

欢迎任何想法或建议。另外,如果有谁能为这个问题想出一个不那么笨拙的标题,请告诉我。

2、解决方案

一种更 Pythonic 的方法是使用鸭子类型。鸭子类型是一种设计模式,它允许您在不知道对象的实际类型的情况下调用其方法。这可以通过使用 hasattr() 函数来完成,如下所示:

def get_view_info(model_object):
    if hasattr(model_object, 'get_path'):
        return model_object.get_path()
    elif hasattr(model_object, 'get_name'):
        return model_object.get_name()
    else:
        raise TypeError("Unknown model object type")

此函数将根据所提供模型对象(从 internalPointer() 方法检索)的类型,调用相应的 get_pathget_name 方法。

以下是一个代码示例,演示如何使用此函数:

from PySide2 import QtCore, QtGui, QtWidgets

class GitRepo(QtCore.QObject):
    def __init__(self, path, branch):
        super().__init__()
        self.path = path
        self.branch = branch

    def get_path(self):
        return self.path

    def get_name(self):
        return "Repository: {}".format(self.path)

class WorkTreeFile(QtCore.QObject):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def get_name(self):
        return self.name

class MyModel(QtCore.QAbstractItemModel):
    def __init__(self, data):
        super().__init__()
        self.data = data

    def rowCount(self, parent):
        if parent.isValid():
            return len(self.data[parent.row()])
        else:
            return len(self.data)

    def columnCount(self, parent):
        return 1

    def data(self, index, role):
        if role == QtCore.Qt.DisplayRole:
            return get_view_info(self.data[index.row()])

    def index(self, row, column, parent):
        if parent.isValid():
            return self.createIndex(row, column, parent.child(row, 0))
        else:
            return self.createIndex(row, column)

    def parent(self, index):
        if index.isValid():
            return self.createIndex(index.row(), 0, index.parent())
        else:
            return QtCore.QModelIndex()

if __name__ == "__main__":
    app = QtWidgets.QApplication([])

    repo = GitRepo("/path/to/repo", "master")
    file1 = WorkTreeFile("file1")
    file2 = WorkTreeFile("file2")

    data = [repo, [file1, file2]]

    model = MyModel(data)

    view = QtWidgets.QTreeView()
    view.setModel(model)

    view.show()

    app.exec_()

此代码创建一个 QAbstractItemModel 子类,它使用鸭子类型来确定如何格式化每个模型对象的视图信息。然后将此模型设置为 QTreeView,并显示树视图。

这种方法比使用 isinstance 更加 Pythonic,因为它不需要在模型类中定义任何特殊方法。它还更加灵活,因为您可以添加新的模型类型,而无需更改模型类。