Python中检查传递参数是否有效的最佳方法是什么?

4
我过去一年一直在使用Java,其中我使用assert来确保传递给方法的参数满足某些前提条件。 我希望在Python中也能这样做,但我在这里读到异常比断言更好用。
以下是我目前的代码:
if type(x) == List:
    x = np.array(x)
else:
    err = ("object passed for x is not a numpy.ndarray or a list")
    assert type(x) is np.ndarray, err
err = ("object passed for n is not an integer")
assert type(n) is IntType, err
err = ("the size of the object passed for x does not equal "
       "n squared")
assert x.size is n**2, err

有更优雅/更符合Python习惯的方法来处理这个问题吗?我应该编写自己的异常类来在传递无效参数时引发异常吗?

什么是参数?函数应该做什么?为什么不直接使用 try: x = np.array(x) - jonrsharpe
1
为什么要检查呢?直接抛出异常或者捕获并在那里处理即可。 - kylieCatt
更Pythonic的做法可能是使用try except逻辑。 - NendoTaka
2
这并不是真正回答你的问题,但是不要使用 is 来比较数值类型的变量,就像你在最后一行所做的那样。即使 a == b 为 True,a is b 也有可能为 False。 - Kevin
我会在Python和Java中使用if检查和抛出异常。顺便说一句:永远不要在Java的生产代码中使用asserts。在Java运行时,断言检查默认是禁用的。 - Peter Paul Kiefer
Pythonic 的方式通常是清晰地记录函数所需的内容,如果调用者使用不当,则让代码自行中断。 - bruno desthuilliers
4个回答

2

不要限制自己

有时你会惊讶地发现,很多函数、类等在输入类型比原始作者期望的更加多样化时,仍会产生有用的输出,只要它们与原始类型足够相似。不要限制代码后期的未曾预料到的用途。这被称为EAFP方法

因此,仅在你知道代码会产生无意义的结果时进行测试。

assert并不总是有效

另外,当你禁用断言(python -O)时,assert将不起任何作用。


1
首先,您应该使用例如 if isinstance(x, list) 来检查对象的类型,而不是使用 type(x)。这样可以更好地处理继承(请参见Python中isinstance()和type()之间的区别)。
其次,如果 x 是列表实例,真的很重要吗?元组可以吗?自定义列表子类?任何 Sequence object ?任何可以成为 np.array 的东西?Python 使用强但动态的 "鸭子类型";具体类型并不像它具有正确行为那样重要。
其中一个选项可能是:
from collections import Sequence

def some_func(x, n):
    if not isinstance(x, (Sequence, np.ndarray)):
        raise TypeError('x must be a sequence or numpy array')
    x = np.ndarray(x)
    if x.size != n ** 2:  # possible TypeError if n**2 doesn't make sense
        raise ValueError('x must be {} long'.format(n ** 2))
    ...

这将引发TypeErrorxn的参数类型错误)或ValueErrorx是正确的类型但大小不对),这比无论发生什么都得到一个AssertionError更容易有效地处理。

0
在Python中,检查参数是相当昂贵的。通常的方法是确保错误类型的参数最终会破坏代码,并希望异常能够给开发人员足够的线索来确定问题所在。
有些人更进一步,编写单元测试以确保代码以预期的方式崩溃。
请记住,Python不是Java。在Java中,开发人员受到恐惧的驱动(某些类型可能是错误的,或者有人可能看到/使用他们不应该看到/使用的东西)。Python不支持这些概念的大部分,因为我们认为它们对于小型项目来说是浪费时间,只有傻瓜才会把时间花在庞大的项目上,而他们可以通过一堆小型项目实现同样的(甚至更多的)目标。
这就是为什么我们很少检查参数类型。积极的一面是,如果这些类型表现良好,这允许您将更多内容传递到函数/方法中,超出了任何人的意图。

0

Python的设计理念是

宁愿请求原谅,也不要事先获得许可

(EAFP)

因此,你应该采用try: except:的方式,而不是检查类型。

https://docs.python.org/2/glossary.html

我知道这听起来很奇怪,尤其是在从静态类型语言转换过来的情况下。我之前也做了多年的C/C++,感觉也是一样的 :)

或许可以尝试像下面这样做。很难猜测你真正想要从这个numpy数组中得到什么。在我看来,try catch应该放在你对数组执行的操作周围,而不是它的初始化。

import numpy as np

def my_np_array(l, n):
    # if you want to catch exceptions outside of the function 
    # I would just not check 'l' and not catch anything, np.array accepts 
    # pretty much anything anyway
    try:
        x = np.array(l)
    except TypeError:
        print 'Parameter l is invalid'
        return None

    if x.size != n**2:
        raise TypeError("n^2 must be equal to x.size")

    return x

在这种情况下,你会如何使用try except?你能举个例子吗? - Aaron Digulla
我添加了一个例子,但它确实取决于您想对numpy数组做什么。 - Maresh
我认为在任何情况下 x = np.array(l) 都不会引发 TypeError - 如果你传递的是无法迭代的对象,它最终只会成为一个包含该对象的数组(例如参见 np.array(None))。 - jonrsharpe
是的,我在注释中提到了。在我看来,try catch 应该放在你对数组应用操作的周围,而不是它的初始化,但我不知道他想做什么... - Maresh
啊,好的,这很有道理。我稍后要用这个数组进行一些矩阵运算,所以我应该在第一个运算周围使用try catch。谢谢! - Matthias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接