检查参数类型的 Pythonic 方法

Posted

技术标签:

【中文标题】检查参数类型的 Pythonic 方法【英文标题】:Pythonic Way To Check for A Parameter Type 【发布时间】:2014-01-07 12:19:12 【问题描述】:

我正在为工作做一个小项目,并制作了一些类和方法。其中一个类别代表我们库存中的一个货架,另一个类别代表货架上的每个垃圾箱。我有一种方法可以将新箱添加到架子上,并且我添加了一些逻辑以确保在将其添加到架子之前将其传递给 Location 对象(现在我在将所有内容移动到数据库之前在开发时使用列表)。

但是,我刚刚在一本 Python 书籍中读到,通常最好在出现异常时处理它们,而不是添加额外的代码。我删除了逻辑以查看我会得到什么错误,但我没有得到任何允许使用字符串代替 Location 对象的内容。

有没有更 Pythonic 的方式来强制参数是什么类型?

我的书架上有什么:

class CrossDock:
locations = []

def add_location(self, location):
    if isinstance(location, Location): #if this is commented out it will take what ever is passed to it
        self.locations.append(location)
    else:
        print("location parameter must be of type: Location. Parameter is of type" + str(type(location)))

有没有办法使用 try/except 块来做到这一点?

【问题讨论】:

注意:您的 locations 字段是类字段,而不是实例字段。所有CrossDock 实例将共享同一个副本。要解决此问题,请定义一个 __init__ 方法并在其中设置 self.locations 只有一个 CrossDock 实例,我还应该将位置字段放在 init 方法中吗? 是的。它在概念上属于那个实例;如果您要制作更多 CrossDocks,您会希望它们有单独的列表。 【参考方案1】:

向调用者传播异常。当他们滥用该类时,这会强制您的类的用户修复无效类型。打印没有用,因为它不强制执行接口契约。

class CrossDock(object):
    def __init__(self):
        self.locations = []

    def add_location(self, location):
        if isinstance(location, Location):
            self.locations.append(location)
        else:
            raise TypeError("location must be Location, got: " +
                            repr(type(location)))

【讨论】:

我认为您有一些 Java 泄漏,并且您在 __init__ 上缺少括号。此外,这不是一个错误,但我建议让 CrossDock 继承自 object,所以它是一个新型类。 我已根据@user2357112 的评论使其成为有效的 Python,并对其进行了更改以提出更惯用的TypeError。随意回滚【参考方案2】:

最pythonic的方法是不检查类型...一切都是鸭子类型的。

What's the canonical way to check for type in python?

【讨论】:

【参考方案3】:

当您尝试使用 Location 实例但在那里发现了其他东西时,您最终可能会在代码中的其他地方发生异常。当参数来自不可靠的来源时,检查参数并没有错。这会将异常置于问题的根源,而不是在代码中可能更难诊断的次要位置。

你可能会做你所拥有的,只是引发一个异常而不是打印错误。

def add_location(self, location):
    if not isinstance(location, Location):
        tmpl = "location parameter must be of type: Location, got %s"
        raise TypeError(tmpl % str(type(location)))
    ... do other processing here after guarding check ...

如果您无法控制调用代码,这种类型的检查是最合适的。如果你只是想捕捉你自己犯的一个编程错误,你可以使用一个断言:

def add_location(self, location):
    assert isinstance(location, Location), "location must be of type: Location"
    ... do other processing here

反对进行参数类型检查的建议是为了让您的代码具有最大的灵活性,例如如果有人想传入一个与 location 具有相同方法的对象。即使代码可以工作,检查硬编码类型也会引发异常。

【讨论】:

【参考方案4】:

在 try/except 块中使用 assert

class Location():
    pass

class CrossDock:
    locations = []

    def add_location(self, location):
        try:
            assert(isinstance(location, Location))
            self.locations.append(location)
            print("added")
        except AssertionError:
            print("error: Parameter is of type" + str(type(location)))


c = CrossDock()
loc = Location()
c.add_location("2")
c.add_location(loc)

第一次调用add_location 会失败

location parameter must be of type: Location. Parameter is of type<type 'str'>

added

【讨论】:

断言严格用于调试;它们不应该用于验证作为公共接口一部分的方法的先决条件,因为它们可以被禁用。此外,捕获异常并打印错误消息远不如让它传播有用。 OP 要求尝试/例外,这就是我发现异常的原因。打印是为了演示,符合 OP 问题中的操作。 我有什么作品,但我不确定这是否是阅读后的最佳方式。所以我有一些可行的方法,但我想知道它是否/什么是最好的前进方式。

以上是关于检查参数类型的 Pythonic 方法的主要内容,如果未能解决你的问题,请参考以下文章

类型检查对象参数的正确方法[重复]

python限定方法参数类型返回值类型变量类型等

避免空列表的默认参数的pythonic方法是啥?

是否可以在 IntelliJ 中为私有方法禁用“可选用作字段或参数类型”检查?

将类类型作为参数传递并针对 Kotlin 中的另一个对象检查类型

应用三参数函数“减少”列表的pythonic方法是啥?