【问题标题】:How to avoid type checking arguments to Python function如何避免对 Python 函数进行类型检查参数
【发布时间】:2014-08-07 02:41:41
【问题描述】:

我正在创建类 Foo 的实例,并且我希望能够以通用方式从各种类型中实例化这些实例。您不能将 Foo 传递给字典或列表。请注意,Foo 来自第 3 方代码库 - 我无法更改 Foo 的代码。

我知道 Python 中的类型检查函数参数被认为是错误的形式。有没有更 Pythonic 的方式来编写下面的函数(即没有类型检查)?

def to_foo(arg):
  if isinstance(arg, dict):
    return dict([(key,to_foo(val)) for key,val in arg.items()])
  elif isinstance(arg, list):
    return [to_foo(i) for i in arg]
  else:
    return Foo(arg)

编辑:可以使用 try/except 块。例如,您可以这样做:

def to_foo(arg):
  try:
    return Foo(arg)
  except ItWasADictError:
    return dict([(key,to_foo(val)) for key,val in arg.items()])
  except ItWasAListError:
    return [to_foo(i) for i in arg]

我对此并不完全满意,原因有两个:首先,类型检查似乎更直接地解决了所需的功能,而这里的 try/except 块似乎到达了同一个地方,但不那么直接。其次,如果错误没有像这样清晰地映射怎么办? (例如,如果传递 list 或 dict 会引发 TypeError)

编辑:我不是 try/except 方法的忠实粉丝的第三个原因是我需要去查找 Foo 在这些情况下会抛出哪些异常,而不是能够预先对其进行编码。

【问题讨论】:

  • 一般来说,我认为这是三个完全独立的函数。它们都返回没有通用接口的不同类型,所以我看不到一个地方你不会先验地知道你将要从中构造Foo 的类型,因为你需要知道如何处理返回Foolist(Foo)dict((str, Foo))
  • 另外请记住,python 中的任何类型检查都不沿继承链查看,仅查看直接实例类型。这意味着您的类型检查对于 行为类似于 lists(例如,一个集合或任何真正的可迭代对象)或 行为类似 dicts 的事物将始终失败,例如 OrderedDictdefaultdictsingledispatch 稍微好一点,因为它会沿着继承链寻找一个共同的祖先,但仍然无法通过 acts-like 测试。
  • 我同意可以事先知道传递的对象是列表还是字典,并进行适当处理。我希望的是一个能够优雅地处理我感兴趣的大多数情况的函数,即单个实例、列表、字典、列表列表等。看起来 try/except 块可以做到这一点,但是不能保证对于您可能有兴趣支持的每种类型的例外都是唯一的。

标签: python types typechecking


【解决方案1】:

如果您使用的是 python 3.4,则可以使用 functools.singledispatcha backport for a different python version

from functools import singledispatch

@singledispatch
def to_foo(arg):
    return Foo(arg)

@to_foo.register(list)
def to_foo_list(arg):
    return [Foo(i) for i in arg]

@to_foo.register(dict)
def to_foo_dict(arg):
    return {key: Foo(val) for key, val in arg.items()}

这对于 python 来说是一个相当新的结构,但在其他语言中是一个常见的模式。我不确定你是否称它为 pythonic,但它确实感觉比到处写isinstances 更好。但实际上,singledispatch 可能只是在内部为您进行 isinstance 检查。

【讨论】:

  • +1 - 我认为我更支持 isinstance,但它感觉比必须定义 3 个不同的函数更干净、更直接。
  • @user1336934 对于这个特殊的用例,我同意你的看法。如果各个主体中有更多的代码和逻辑,我想我更喜欢 singledispatch。
  • @JoshSmeaton “虽然,在实践中,singledispatch 可能只是在内部为您进行 isinstance 检查”,从源代码看来,singledispatch 只是维护一个函数字典,其中键是类型传递给register 调用。所以从技术上讲,它是一个哈希表查找,而不是一个isinstance 检查。
  • @ebarr 感谢您提及这一点。我很好奇,自己去查了源,看看他们用了什么方法,但忘了更新答案。
【解决方案2】:

处理您的问题的pythonic方法是继续并假设(首先)argFoo 并且除了任何错误:

try:
    x = Foo(arg)
except NameError:
    #do other things

这个想法的短语是"duck typing",它是python中的一个流行模式。

【讨论】:

    猜你喜欢
    • 2020-10-19
    • 1970-01-01
    • 2019-11-22
    • 2011-11-24
    • 1970-01-01
    • 1970-01-01
    • 2022-05-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多