我发现自己试图在Python程序中经常将构造函数参数转换为正确的类型。到目前为止,我一直在使用与此类似的代码,因此我不必重复异常参数:
class ClassWithThreads(object):
def __init__(self, num_threads):
try:
self.num_threads= int(num_threads)
if self.num_threads <= 0:
raise ValueError()
except ValueError:
raise ValueError("invalid thread count")
这是一个好习惯吗?我是否只是不打算在转换时捕获任何异常,并让它们传播给调用者,可能的缺点是缺少有意义且一致的错误消息?
答案 0 :(得分:11)
当我遇到这样的问题时,我会在标准库中搜索我可以为我的代码建模的代码。 multiprocessing/pool.py有一个类似于你的课程:
class Pool(object):
def __init__(self, processes=None, initializer=None, initargs=(),
maxtasksperchild=None):
...
if processes is None:
try:
processes = cpu_count()
except NotImplementedError:
processes = 1
if processes < 1:
raise ValueError("Number of processes must be at least 1")
if initializer is not None and not hasattr(initializer, '__call__'):
raise TypeError('initializer must be a callable')
请注意,它没有说
processes = int(processes)
它只是假设你发送了一个整数,而不是浮点数或字符串,或者其他什么。 它应该是非常明显的,但如果你觉得它不是,我认为只需记录它就足够了。
如果ValueError
确实会引发processes < 1
,它确实会检查initializer
是否可以调用。
因此,如果我们将multiprocessing.Pool
作为模型,那么您的类应该如下所示:
class ClassWithThreads(object):
def __init__(self, num_threads):
self.num_threads = num_threads
if self.num_threads < 1:
raise ValueError('Number of threads must be at least 1')
对于某些人来说,这种方法可能会非常不可预测地失败 条件?
我认为先发制人的类型检查通常违背了Python的内容 (动态,鸭子打字)设计理念。
鸭子打字为Python程序员提供了极强的表现力, 和快速的代码开发,但(有些人可能会说)是危险的,因为它没有 试图捕捉类型错误。 有些人认为逻辑错误比类型更严重和频繁 错误。您需要单元测试来捕获那些更严重的错误。所以即使你 做抢先式检查,它不会增加太多保护。这场辩论存在于意见领域,而不是事实,所以这不是一个可以解决的论点。在篱笆的哪一边 你坐的可能取决于你的经验,你对类型可能性的判断 错误。它可能会被你已经知道的语言所偏向。它可能取决于 你的问题域。
你必须自己决定。
PS。在静态类型语言中,类型检查可以在编译时完成,因此不会妨碍程序的速度。在Python中,类型检查必须在运行时进行。这会使程序慢一点,如果在循环中进行检查,可能会很多。随着程序的增长,类型检查的数量也会增加。不幸的是,许多检查可能是多余的。所以如果你真的相信你需要进行类型检查,你可能应该使用静态类型的语言。
PPS。有(Python 2)和(Python 3)类型检查的装饰器。这会将类型检查代码与函数的其余部分分开,并允许您在将来选择时更容易关闭类型检查。
答案 1 :(得分:4)
您可以使用类型检查装饰器,例如this activestate recipe或this other one for python 3。它们允许您编写如下代码:
@require("x", int, float)
@require("y", float)
def foo(x, y):
return x+y
如果参数不是必需类型,则会引发异常。您可以轻松地扩展装饰器以检查参数是否具有有效值。
答案 2 :(得分:2)
这是主观的,但这是一个反驳论点:
>>> obj = ClassWithThreads("potato")
ValueError: invalid thread count
等等,什么?那应该是TypeError
。我会这样做:
if not isinstance(num_threads, int):
raise TypeError("num_threads must be an integer")
if num_threads <= 0:
raise ValueError("num_threads must be positive")
好的,所以这违反了“鸭子打字”的原则。但是我不会对像int
这样的原始对象使用duck typing。