【问题标题】:Python: Duck-typing vs. Exception HandlingPython:鸭子类型与异常处理
【发布时间】:2016-01-19 18:03:37
【问题描述】:

关于 idomatic Python 的问题。假设我有一个函数:

def a_function(list_of_things):
    for item in list_of_things:
        process_item(item)

现在假设我很难确定输入参数是一个列表。即使是现在,我也能听到甜美的 pythonista 合唱恳求我:“使用鸭子打字和例外!”

def a_function(list_of_things):
    try:
        for item in list_of_things:
            process_item(item)
    except:
        pass # . . . or something

这很好,除非我传入一个映射、一个字符串或任何其他可迭代的方式,我发现对于这个特定的应用程序是“错误的”。

我没有写大量的 Python,但我设法经常遇到这种情况和相关情况,以至于它经常让我烦恼。更一般地说,我想使用鸭子类型和异常,这似乎是 Python 中的约定,但是在很多情况下,可能会得到一个不正确的输入类型来传递我的异常;如此之多,以至于异常似乎常常是错误的答案(或至少经常如此)。

即使异常确实看起来是正确的答案,但当我使用它们时我仍然害怕,因为如果我只是没有想到坏类型会通过的极端情况怎么办,离开我的代码,因为没有更好的术语,“躲避”。

所以我最终求助于typeisinstance 之类的东西。根据我的阅读,似乎显式类型检查在 Python 中被认为是邪恶的,但还能做什么呢?请注意,我几乎从不在我的 Python 中编写类层次结构,所以我没有子类和类型检查的问题,但其他人可能会。

我想到了四个可能的答案,在我看来,所有这些答案的可能性都一样:

  1. 您应该使用异常,但您做错了。像这样做 。 . .
  2. 您应该使用异常。如果错误的类型通过了您的检查,则表明您的设计中存在一些问题(什么问题?)
  3. 您做对了。在这些情况下,最好进行显式类型检查。
  4. 你不知道<python thing for this situation>?做一些研究!

是其中之一,还是其他?

注意 - 我在 SO 上发现了很多关于这个问题的绕圈子,但如果可以的话,我正在寻找人们谈论这个特定“类型”的问题。

【问题讨论】:

  • 为什么地图、字符串或其他可迭代对象会“错误地”可迭代?您的示例函数仅依赖于可迭代性。无论如何,如果你需要一个序列,你可以在 Python 3 中尝试isinstance(collections.Sequence)isinstance(collections.abc.Sequence),但通常的方法是假设调用者做了正确的事情并依靠单元测试来确保你的代码有效。
  • 仅检查错误类型是否导致错误但有效的答案(如果您想要单个字符序列,则字符串是有效参数)。其他任何事情都取决于调用者。 “bug”不是沉默的,因为你有测试,要检查结果是否正确。
  • 这只是一个例子,还有其他情况,例如add - 多么模棱两可的函数名称,看起来你可以用集合做数学......
  • 当使用duck-typing...不要使用异常来试图保存调用者。您编写了一个处理序列的函数并使用列表对其进行大量测试。如果用户传递的其他东西不能按照您认为的方式工作,那是在调用者身上。如果你的代码的内部因为一个时髦的数据类型而爆炸......那也在调用者身上。如果它像鸭子一样嘎嘎叫,但结果却是一头大象……那就是大象问题。
  • 在高风险的环境中,在知道您已经消除了代码的一些可能用途的情况下进行繁重的类型检查是有意义的。因此,当您遇到恐怖时,请远离!对我来说,我的较低数据层往往是偏执狂,但例如,而不是 isinstance(x, int)int(x) 并让异常发生。此外,调用者应该一直在测试代码(并阅读被调用函数的文档)......你只需要决定谁穿大男孩裤子。

标签: python types exception-handling duck-typing


【解决方案1】:

最佳做法是与类型无关。 for-loop 会为任何不可迭代的类型引发异常。文档应该清楚地说明,需要一个可迭代的类型。使用你的函数的函数应该有单元或集成测试,以便发现意外行为。

【讨论】:

    【解决方案2】:

    类型检查- 您正确地提到类型检查在 Python 中被认为是邪恶的。 我们应该关心一个对象做了什么,而不是它是什么。 例如 - 如果我对输入进行迭代,那么我的代码应该适用于所有可迭代的并且我永远不应该检查它的类型。

    异常处理 - 让它抛出异常,如果我们尝试处理它,我们将捕获所有类型的异常,或者我们将错过任何异常。

    让我们验证 python 内置的行为,我们可以遵循相同的 -

    1. 哈希(输入) 如果输入不能散列,它将抛出异常-

      哈希([1,2])

      TypeError: unhashable type: 'list'

    Dictionary 和 Set 都使用 hash() 函数。如果您尝试使用可变键创建字典,它也会抛出相同的异常

    {[1,2]:"sample"}
    TypeError: unhashable type: 'list'
    

    现在试试 set

    set(([1,2], 5,6))
    TypeError: unhashable type: 'list'
    
    1. filter(function_object, any_iterable)`

      过滤器(布尔,1245)

      它会抛出 TypeError: 'int' 对象不可迭代

      现在将 iter 应用于 int 对象

      iter(1245)

      TypeError: 'int' 对象不可迭代

    所以过滤器内部使用 iter() 并抛出从 iter() 得到的相同异常

    `

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多