Python实践:有没有更好的方法来检查构造函数参数?

时间:2012-01-19 01:28:09

标签: exception python

我发现自己试图在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")

这是一个好习惯吗?我是否只是不打算在转换时捕获任何异常,并让它们传播给调用者,可能的缺点是缺少有意义且一致的错误消息?

3 个答案:

答案 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 recipethis 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。