【问题标题】:How to safeguard with dynamic typing如何使用动态类型进行保护
【发布时间】:2012-09-08 15:55:12
【问题描述】:

我们的项目变得有点大,前几天我遇到了这个问题,当我试图向一个函数添加一个简单的错误消息时:

def create_report(id):
    report = new_report(id)
    if not report:
        raise api_error('Could not find report with id %d' % (id,))
    ...

问题? id 是一个字符串,当它尝试将其格式化为数字时它崩溃了。我不是该函数的原作者,并错误地认为id 是一个数字。相反,它应该是一个字符串。哎呀。

如果这是一种强类型语言,我会立即从编译器中得到一个错误;处理这类事情的最佳方法是什么?我应该检查每个参数的类型(似乎需要做很多工作),还是应该将所有内容都放在try: 块中?也许我们应该在每个函数中写一个注释来描述它的参数?或者我只是应该知道的更好?

【问题讨论】:

  • 不,你使用单元测试和集成测试来发现这些问题。
  • 非常糟糕的例子。在大多数流行的静态类型编程语言中,等效函数(sprintfString.Format 等)也没有类型,至少不会以任何方式允许编译器决定它的某些使用是否正确。
  • @delnan,很抱歉我在静态类型语言方面的经验有限。 FWIW,Xcode 给了我一个“格式指定类型'int',但参数有类型'NSString *'警告(但不是错误)。
  • 在这里检查类型也无济于事。你以为你得到的是一个 int,所以你会断言 id 是一个 int。该断言会引发异常。但这就是这里发生的事情:它引发了一个异常。你将一无所获。
  • @speg 是的,如果格式字符串是恒定的并且工具会费心,这样的分析是可能的。但这并不总是可能的,并且与静态类型无关,因为它不需要/使用感兴趣的函数的类型,仅用于参数。如果格式字符串是动态的,或者您已经丢失了有关参数的确切类型的信息(例如,在包装函数或通用代码中),这将变得不可能。

标签: python dynamic-typing


【解决方案1】:

那个格式字符串应该使用%s,不是因为id是一个字符串,而是因为它通常是最好的选择。 %d 需要数字类型,但%s 会在必要时将其他类型转换为字符串。只有在需要更改数字格式时才应使用%d

【讨论】:

    【解决方案2】:

    如果你真的想确保字符串打印函数不会崩溃,你可能最好使用 %s,它使用 pythons str() 函数将积分值转换为字符串。 看看你在说什么是一个 ID,我假设它是 int 类型的——在这种情况下,这个解决方案应该可以正常工作(如果你使用浮点数,我不太确定 str() 有多少位数会保留)。

    编辑 哦,男孩,我很慢......

    【讨论】:

    • 直到 python 3.2: str(afloat) 给你 12 位有效数字(所以 12345678.9012 小数点在 some 位置)。 Python 3.3 为您提供全面的精度。
    【解决方案3】:

    使此类事情在参数类型方面更加健壮的一种可能策略是编写:

    def create_report(id):
        report = new_report(id)
        if not report:
            raise api_error('Could not find report with id %s' % (id,))
        ...
    

    它并不总是看起来不错,但至少它不会轻易坏。

    但一般来说,为函数制定一些明确的合同总是更好。文档字符串是正确的地方。

    【讨论】:

    • %s 暗示str() 调用。
    • 对,我一直忘记这一点。感谢您指出。无论如何,“显式优于隐式”;)
    • 嗯,它明确表示“字符串表示形式”或“插入为字符串”。 :-)
    【解决方案4】:

    尝试使用 %s,然后使用 str(variable)。这将确保所有内容都转换为字符串(甚至是列表和元组)并且不会发生 TypeError

    def create_report(id):
        report = new_report(id)
        if not report:
            raise api_error('Could not find report with id %s' % (str(id),))
        ...
    

    【讨论】:

    • 不需要将 %s 与 str() 一起使用,%s 自己执行 str()。
    • 当然可以:"%s" % (x,) 在 x 是元组时工作得很好。
    • 哦,我试过 "%s"%(x) 但没有用。感谢您让我知道其中的区别
    【解决方案5】:

    甚至比其他答案“%s”更好,如果该消息是针对开发人员的眼睛,我发现使用%r 是最好的。这将帮助您区分细微的情况。例如,如果您使用'12 ' 作为id 调用此函数,则%s 消息将不会显示尾随空格。 %r 使用值的 repr(),因此将包含引号,帮助您查看精确值。

    【讨论】:

    • 没错,我完全同意,但这更像是一个评论而不是一个答案,不是吗?
    【解决方案6】:

    我认为这里的问题是,当发生意外情况时,您应该在代码部分中断言应该执行的值。也许更好的方法是更改​​ new_report(id) 以在出现问题时引发异常 - 指定它是值错误还是找不到 id。那么你的代码应该是:

    def create_report(id):
        report = new_report(id)
    
    ...
    
    def new_report(id):
        try:
           # find the report by id
           # if couldn't find raise api_error
        except ValueError:
           # explain that id is the wrong type
    

    【讨论】:

      猜你喜欢
      • 2020-09-16
      • 2020-06-15
      • 2020-12-24
      • 2010-09-26
      • 2016-08-17
      • 1970-01-01
      • 1970-01-01
      • 2021-05-03
      • 2018-12-03
      相关资源
      最近更新 更多