首页 > 解决方案 > 如何在调用 __init__ 之前检查参数类型

问题描述

我需要检查__init__(). 我是这样做的:

class Matrix:

    def __init__(self, matrix):
        """
        if type(matrix) != list raise argument error
        """
        if type(matrix) != list:
            raise TypeError(f"Expected list got {type(matrix)}")
        self.matrix = matrix


a = 5
new_matrix = Matrix(a)

但是当TypeError被提升时,该__init__()方法已经被调用。我想知道如何在它被调用之前执行此操作。我认为这可以metaclass在某些时候使用“拦截它”并引发错误来完成,但我不知道如何。

标签: pythonclassmetaclasstypechecking

解决方案


第一:通常不会在 Python 中进行此类运行时检查——除非绝对必要。这个想法是,在这种情况下传递给的任何东西都__init__将表现得与列表足够相似,以便在其位置使用。这就是“鸭子打字”的想法。

第二:如果确实有必要进行此检查,那么if就像您所做的那样,在函数或方法体中的语句就是执行此操作的方法。“方法已运行,并且在其中引发错误”并不重要——这就是 Python 中动态类型的工作方式。

第三:实际上有一种方法可以防止您的程序__init__使用不正确的参数类型调用,那就是使用静态类型检查。当您运行检查器时,您的程序将在准备步骤上出错,例如“mypy” - 这与某些静态语言会引发错误的时间大致相同:当它们在运行之前在显式步骤中编译时。静态类型检查可以增加你认为需要的安全性——但它是一个全新的 Python 代码的样板和官僚主义世界。“python 静态类型检查”的网络搜索可以列出一些要点,如果您可以开始了解这一点 - 我看到的第二个链接相当有趣:https ://realpython.com/python-type-checking/

第四:如果你选择基于 if 的检查,你应该检查你得到的对象是否是“足够的列表”,而不是“type(a) != list”。这将禁止列表的子类。not isinstance(a, list)将接受列表子类,但会阻止其他几种可能正常工作的对象类型。根据您的需要,您的代码将适用于任何“序列”类型。在这种情况下,您可以导入collections.abc.Sequence并检查您的参数是否是该参数的实例 - 这将允许您的方法的用户使用任何具有长度的类并且可以按顺序检索元素。

而且,再重复一遍:在方法内部进行这个检查绝对没有问题。它可以通过创建一个可以进行类型检查的复杂装饰器来分解 - 实际上有 Python 包可以使用类型注释,就像它们被静态类型检查工具使用一样,并进行运行时检查。但这不会为您赢得任何执行时间。静态类型检查将在运行之前进行,但由此获得的资源可以忽略不计。

最后,不,这与元类无关。可以使用元类将装饰器添加到您的所有方法中,并让这些装饰器执行运行时检查 - 但您可能只是显式地使用装饰器。


推荐阅读