4

I'm wondering if what I'm doing is an appropriate method of assertions. I'm trying to making something both concise and very correct for Python's style guides.

try:
    assert self.port_number == 0
    assert self.handle == None
    assert isinstance(port_number, int) or isinstance(port_number, float)
    assert port_number > 0
except AssertionError:
    return -1

*body of code*

return 0

Above is an excerpt of my code that shows how I handle argument assertions. You can assume that I've covered all of the necessary assertions and the input is port_number. Is this considered good style? Is there a better way?

4

4 回答 4

5

assert语句应该只用于检查程序的内部逻辑,绝不能检查用户输入或环境。引用http://wiki.python.org/moin/UsingAssertionsEffectively的最后两段...

断言应用于测试由于错误的用户输入或操作系统/环境故障而可能发生的故障情况,例如找不到文件。相反,您应该引发异常,或打印错误消息,或任何适当的方式。断言只能用于程序的自测试的一个重要原因是可以在编译时禁用断言。

如果 Python 是使用 -O 选项启动的,那么断言将被剥离并且不被评估。因此,如果代码大量使用断言,但对性能至关重要,那么有一个系统可以在发布版本中关闭它们。(但除非真的有必要,否则不要这样做。科学证明,某些错误仅在客户使用机器时才会出现,我们也希望断言对此有所帮助。)

考虑到这一点,几乎没有理由在用户代码中捕获断言,因为断言失败的全部意义在于尽快通知程序员程序中存在逻辑错误。

于 2013-07-08T16:07:08.690 回答
3

我更喜欢不在函数中捕获断言,而是确保调用者处理任何错误。这也允许调用者检查任何未处理的错误并检查回溯以查看究竟出了什么问题。

您还可以在断言语句中添加错误消息。

assert x > 0, "x must be greater than 0"
于 2013-07-08T15:47:49.960 回答
2

如果调用函数期望成功时输入 0,失败时输入 -1,我会写:

def prepare_for_connection(*args, **kwargs):
    if (self.handle is not None):
        return -1
    if not (isinstance(port_number, int) or isinstance(port_number, float)): 
        return -1
    if port_number < 0:
        return -1

    # function body

    return 0

为非异常行为调用抛出和捕获断言错误的机制开销太大。对于语句应该始终为真的情况,断言更好,但如果不是由于某些错误,您会在该位置大声生成错误,或者最好在该位置处理它(使用默认值)。如果您愿意,可以将多个 if 条件组合成一个巨大的条件语句;我个人认为这更具可读性。此外,python 风格是比较None使用isandis not而不是==and !=

一旦程序离开调试阶段,Python 应该能够优化掉断言。请参阅http://wiki.python.org/moin/UsingAssertionsEffectively

授予从函数返回错误号(-1 / 0)的这种 C 风格的约定并不是特别 Pythonic。我会-1False0替换True并给它一个语义上有意义的名字;例如,调用它connection_prepared = prepare_for_connection(*args,**kwargs),这样connection_prepared会是TrueorFalse并且代码将非常可读。

connection_prepared = prepare_for_connection(*args,**kwargs)
if connection_prepared:
    do_something()
else:
    do_something_else()
于 2013-07-08T15:48:35.980 回答
1
return -1

Python 处理错误的方法与 C 不同。如果提供的数据有问题,只需让其AssertionError通过,或使用自定义错误消息引发TypeErroror 。ValueError带有断言语句的自定义错误消息是最简单的:

assert port_number > 0, "Invalid port number"

可以在编译时禁用断言语句这一事实可能是重新考虑是否要在您的情况下使用断言语句的原因。通常的做法是不使用断言语句来验证函数用户的输入,而仅用于内部健全性检查。另一方面,健全性检查和验证之间的界限并不明确。没有断言语句的部分代码示例:

if port_number <= 0:
    raise ValueError('Invalid port number')
if not isinstance(port_number, (int, float)):
    raise TypeError('Port number must be some kind of number')

我个人使用断言语句来验证数据,如果无效,无论如何迟早都会导致崩溃(请参阅“duck-typing”)。我还在开发过程中大量使用 assert 语句,以像使用静态类型语言一样检查我的数据。如果我强烈怀疑自己代码的稳定性和可靠性,我只会使用这些类型的断言。

下一行:

assert self.handle == None

如果我没记错的话,PEP8 说你应该写assert self.handle is None. 至少它得到了比我聪明的人的认可。

assert isinstance(port_number, int) or isinstance(port_number, float)

如果你真的需要这个,它可以写成isinstance(port_number, (int, float)). 但事实证明你没有。你不应该关心是否有人传递了一个数字原始类型或一些重载所有比较运算符的自制类。

也许您可以做的一件事是尝试将端口转换为整数并查看它是否可用:

try:
    port_number = int(port_number)
except ValueError:
    raise ValueError("Invalid port number")

而且在这种情况下,您可以直接ValueError通过,但是对于新手来说,该消息的信息量会减少。

于 2013-07-08T15:49:23.603 回答