Python不鼓励检查类型。但在许多情况下,这可能很有用:
检查构造函数参数。例如检查敌人布尔值,字符串,字典等。如果我不这样做并将对象的成员设置为参数,它将在以后引起问题。
检查函数参数。
在属性中。如果有人设置了错误的值或不同的类型,我应该快速回复。
答案 0 :(得分:11)
答案几乎总是“不”。 Python,Ruby和其他一些语言中的一般概念称为“Duck Typing”。你不应该关心什么是什么,只关心它是如何运作的。换句话说,“如果你想要的只是嘎嘎叫的东西,你就不需要检查它实际上是一只鸭子。”
在现实生活中,放入所有类型检查的问题是无法用替代实现替换输入。您可以检查dict,但我可能想传递一些不是dict的东西,但是实现了dict API。
类型检查仅检查代码中的许多可能错误之一。例如,它不包括范围检查(至少在Python中不包括)。对需要进行类型检查的断言的现代响应是,开发单元测试更有效,不仅确保类型正确,而且功能正确。
另一种观点是,您应该将您的API用户视为同意成人,并信任他们正确使用API。当然有时候输入检查会有所帮助,但这种情况并不像你想象的那么常见。一个例子是来自不受信任来源的输入,例如来自公共网站。
答案 1 :(得分:10)
简单的答案是否,使用多态,异常等。
如果构造函数参数的类型错误,则在执行依赖于特定类型参数的代码时将抛出异常。如果它是一个奇怪的,域特定的东西,提出你自己的例外。环绕的代码块,可能会因try-except和handle错误而失败。因此最好使用异常处理。 (功能参数相同)
在属性中,适用相同的参数。如果要验证接收到的值,请使用断言检查其范围等。如果值类型错误,则无论如何都会失败。然后,处理AssertionError。
在Python中,您将程序员视为智慧生物!只需很好地记录您的代码(使事情变得明显),在适当的地方引发异常,编写多态代码等。将构造中的异常处理(仅适用于它)/错误留给客户端代码。
警告强>
将异常处理留给客户并不意味着你应该在不知情的用户身上丢掉很多垃圾错误。如果可能的话,处理由于构造错误或代码本身的任何其他原因而可能发生的异常。您的代码应该是健壮的。如果您无法处理错误,请礼貌地通知用户/客户端代码程序员!
注意强>
一般来说,构造函数的错误参数不是我担心的太多。
答案 2 :(得分:4)
检查所有你喜欢的,你只需要明确。以下示例是标准库中a module的构造函数 - 它检查extrasaction
arg:
class DictWriter:
def __init__(self, f, fieldnames, restval="", extrasaction="raise",
dialect="excel", *args, **kwds):
self.fieldnames = fieldnames # list of keys for the dict
self.restval = restval # for writing short dicts
if extrasaction.lower() not in ("raise", "ignore"):
raise ValueError, \
("extrasaction (%s) must be 'raise' or 'ignore'" %
extrasaction)
self.extrasaction = extrasaction
self.writer = writer(f, dialect, *args, **kwds)
答案 3 :(得分:1)
这通常是件好事。检查显式类型在Python中可能没那么有用(正如其他人所说),但检查合法值可能是一个好主意。这是一个好主意的原因是软件将更接近错误的来源(它遵循失败快速原则)。此外,检查作为其他程序员和您自己的文档。更好的是,它是“可执行文档”,这很好,因为它的文档不能说谎。
检查参数的快速而肮脏但合理的方法是使用assert:
def my_sqrt(x):
assert x >= 0, "must be greater or equal to zero"
# ...
断言你的论点是一种穷人的契约设计。 (您可能希望查看按合同设计;这很有趣。)
答案 4 :(得分:1)
AFAIU,您希望确保某些对象在比实际使用时更早的时候表现(“跟随界面”)。在您的示例中,您希望知道对象在实例创建时是合适的,而不是在实际使用它们时。
请记住我们在这里谈论Python,我不会建议assert
(当程序运行时,如果python -O
或环境变量PYTHONOPTIMIZE设置为1会怎么样?)或检查特定类型(因为这会不必要地限制您可以使用的类型),但我会建议尽早测试功能,这就是:
def __init__(self, a_number, a_boolean, a_duck, a_sequence):
self.a_number= a_number + 0
self.a_boolean= not not a_boolean
try:
a_duck.quack
except AttributeError:
raise TypeError, "can't use it if it doesn't quack"
else:
self.a_duck= a_duck
try:
iter(a_sequence)
except TypeError:
raise TypeError, "expected an iterable sequence"
else:
self.a_sequence= a_sequence
我在此建议中使用了try… except… else
因为我想在测试成功时将实例成员设置为,即使代码已更改或扩充。显然,你没必要这样做。
对于函数参数和设置属性,我不会提前做这些测试,我只是使用提供的对象并对抛出的异常起作用,除非在长时间的过程之后将使用可疑对象。
答案 5 :(得分:0)
“如果我没有并将对象的成员设置为参数,则稍后会导致问题。”
请详细说明将在以后引起的“问题”的确切清单。
它根本不起作用吗?尝试/除了块是什么。
它会表现得“奇怪”吗?这种情况非常罕见,仅限于“险兆”类型和运营商。标准的例子是除法。如果你期望整数,但得到浮点数,那么除法可能不会做你想要的。但这是由//,vs. / division运算符修复的。
这是完全错误的,但似乎仍然完整?这是非常罕见的,需要使用标准名称的非常精心设计的类型,但需要非标准的东西。例如
class MyMaliciousList( list ):
def append( self, value ):
super( MyMaliciousList, self ).remove( value )
除此之外,很难让事情“导致以后出现问题”。请使用“问题”的具体示例更新您的问题。
答案 6 :(得分:0)
正如达尔克所说,答案几乎总是“不”。在Python中,您通常不关心参数是某种类型,而是行为类似于某种类型。这被称为“Duck Typing”。有两种方法可以测试参数是否像给定类型一样:(1)您可以像使用它一样使用它,并在/如果没有或(2)时抛出异常)您可以定义一个界面来描述该类型应该的行为并测试该界面的一致性。
zope.interface是我首选的Python界面系统,但还有其他几个。使用其中任何一个,您可以定义一个接口,然后声明给定类型符合该接口,或者定义一个适配器,将您的类型转换为符合该接口的类型。然后,您可以断言(或根据需要进行测试)参数提供(在zope.interface术语中)该接口。