如何在调用 __init__ 之前检查参数类型

问题描述 投票:0回答:1

我需要检查

__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
在某个时刻“拦截它”并引发错误来完成,但我不知道如何实现。

python class metaclass typechecking
1个回答
3
投票

第一: 人们通常不会在 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
并检查您的参数是否是该参数的实例 - 这将允许您的方法的用户使用任何具有长度并且可以按顺序检索 itens 的类。

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

最后,不,这与元类无关。可以使用元类向所有方法添加装饰器,并让这些装饰器执行运行时检查 - 但无论如何您都可以显式地使用装饰器。

© www.soinside.com 2019 - 2024. All rights reserved.