我知道,在Python中,类型检查函数参数通常是不受欢迎的,但是我想我已经找到了这样做有意义的情况。
在我的项目中,我有一个抽象基类Coord
,它有一个子类Vector
,它有更多的特性,如旋转、改变幅度等。数字的列表和元组也将返回True,因为isinstance(x, Coord).
我还有很多函数和方法接受这些协调类型作为参数。我已经设置了decorators来检查这些方法的参数。以下是一个简化版本:
class accepts(object):
def __init__(self, *types):
self.types = types
def __call__(self, func):
def wrapper(*args):
for i in len(args):
if not isinstance(args[i], self.types[i]):
raise TypeError
return func(*args)
return wrapper
这个版本很简单,仍然有一些错误。只是为了说明这一点。它的用途如下:
@accepts(numbers.Number, numbers.Number)
def add(x, y):
return x + y
注意:我只检查抽象基类的参数类型。
这是个好主意吗?有没有更好的方法来做到这一点,而不必在每个方法中重复类似的代码?
编辑:
如果我要做同样的事情,但不是在decorator中预先检查类型,而是在decorator中捕获异常:
class accepts(object):
def __init__(self, *types):
self.types = types
def __call__(self, func):
def wrapper(*args):
try:
return func(*args)
except TypeError:
raise TypeError, message
except AttributeError:
raise AttributeError, message
return wrapper
这样更好吗?
你的品味可能会有所不同,但Python(Pythonic,tm)的风格是,只需继续使用你需要的对象。如果它们不支持您正在尝试的操作,则会引发异常。这就是所谓的duck typing。
支持这种样式有几个原因:首先,它允许您在现有代码中使用新类型的对象,从而启用多态性,只要新对象支持正确的操作。其次,它通过避免大量检查简化了成功的途径。
当然,使用错误参数时得到的错误消息在类型检查中比duck类型更清楚,但正如我所说,您的品味可能会有所不同。
如果这是规则的例外,没关系。但是,如果项目的工程/设计围绕着类型检查每个函数(或大多数函数),那么也许您不想使用Python,那么用C#代替它怎么样?
根据我的判断,你制作一个用于类型检查的decorator通常意味着你将大量使用它。因此,在这种情况下,虽然将公共代码分解成decorator是pythonic,但用于类型检查的事实并不是非常pythonic。
Python中鼓励使用Duck类型的原因之一是,有人可能会包装您的一个对象,然后它看起来会是错误的类型,但仍然有效。
下面是包装对象的类的示例。
LoggedObject
在所有方面都像它包装的对象一样,但是当您调用LoggedObject
时,它会在执行调用之前记录调用。如果显式测试
isinstance(a, A)
,它将失败,因为a
是LoggedObject
的实例。如果你让鸭子打字就行了,这就行了。如果有人错误地传递了错误类型的对象,则会引发
AttributeError
之类的异常。如果显式地检查类型,异常可能会更清楚,但我认为总体来说,这种情况是duck类型的胜利。有时候你真的需要测试这个类型。我最近学到的一点是:当你在编写与序列一起工作的代码时,有时你真的需要知道你是否有一个字符串,或者它是任何其他类型的序列。考虑一下:
这应该返回序列的最长长度,或者任何嵌套在其中的序列。它起作用:
但是如果调用
llen("foo")
,它将永远递归,直到堆栈溢出。问题是字符串有一个特殊的属性,即使从字符串中取最小的元素,它们总是像一个序列;一个字符串仍然是一个序列。因此,如果没有字符串的显式测试,我们就不能编写llen()。
相关问题 更多 >
编程相关推荐