【问题标题】:Python assertion stylePython 断言风格
【发布时间】:2013-07-08 15:38:06
【问题描述】:

我想知道我正在做的是否是一种适当的断言方法。我正在尝试为 Python 的样式指南做一些既简洁又非常正确的东西。

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

以上是我的代码摘录,展示了我如何处理参数断言。您可以假设我已经介绍了所有必要的断言,并且输入是 port_number。这被认为是好的风格吗?有没有更好的办法?

【问题讨论】:

  • 如果这段代码是函数的主体,我会编辑它以显示它。

标签: python styles


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

    assert x > 0, "x must be greater than 0"
    

    【讨论】:

      【解决方案3】:

      如果调用函数期望成功时输入 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 样式是使用isis not 而不是==!=None 进行比较。

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

      这种从函数返回错误号 (-1 / 0) 的 C 风格约定并不是特别符合 Python 风格。我会将-1 替换为False,将0 替换为True,并给它一个语义上有意义的名称;例如,称它为connection_prepared = prepare_for_connection(*args,**kwargs),所以connection_prepared 将是TrueFalse,并且代码可读性很强。

      connection_prepared = prepare_for_connection(*args,**kwargs)
      if connection_prepared:
          do_something()
      else:
          do_something_else()
      

      【讨论】:

        【解决方案4】:
        return -1
        

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

        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 通过,但是对于新手来说,该消息的信息量较少。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-08-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多