【问题标题】:Making return type of python optional使python的返回类型可选
【发布时间】:2018-08-20 15:10:30
【问题描述】:

在 JVM 语言中有一种称为 Optional 的数据类型,它表示值可以是 Null/None。通过使用函数的数据类型作为选项(例如 Option(Int))。函数的调用语句可以采取行动。

如何在 Python 中实现类似的方法。 我想创建一个函数,函数的返回值应该告诉我 1.函数成功,所以我返回了一个值。 2.函数没有成功,所以没有返回任何东西。

【问题讨论】:

  • tl;dr 使用例外。
  • @mkrieger,感谢您的评论。在某些情况下,我不希望调用函数导致异常。特别是当我创建一个将由批处理应用程序使用的公共库时。然后我希望调用站点处理错误(或者有时是不保证异常的特定意外行为)并采取措施而不是使长时间运行的批处理过程失败。
  • 在调用点处理异常和在调用点检查返回值没有太大区别。唯一的区别是异常强制你处理它们,而返回值可以被忽略,所以看起来你想要异常。

标签: python-3.x


【解决方案1】:

我也想解决这个问题,并创建了一个名为 optional.py 的库来解决这个问题。它可以使用pip install optional.py 安装。它已全面测试并支持 python2 和 python3。希望得到一些反馈、建议和贡献。

为了解决另一个答案的担忧,并阻止任何仇恨者,是的,引发异常是 python 的惯用语,但是它会导致控制流异常和实际异常原因异常之间的歧义。

关于防止防御性编程掩盖了应用程序的核心逻辑和对话双方的聪明人的大量讨论。我个人的偏好是使用选项,所以我提供了库来支持这种偏好。例外处理(或返回None)是可以接受的替代方案,但这不是我的偏好。

【讨论】:

    【解决方案2】:

    对于您最初的问题:引发异常而不是返回 None。这是 Python 中的一个习惯用法,因此很容易被用户理解。例如

    def get_surname(name):
        if name == "Monty":
            return "Python"
        else:
            raise KeyError(name)
    

    你会在 Python 中经常看到这种使用模式:

    try:
        print(get_surname("foo"))
    except KeyError:
        print("Oops, no 'foo' found")
    

    根据您的反馈,您似乎还想确保实际使用了某些返回值。这很棘手,我认为在 Python 中没有一种优雅的方式来强制执行此操作,但我会尝试一下。

    首先,我们将返回值放在一个属性中,以便我们可以跟踪它是否确实已被读取。

    class Result(object):
        def __init__(self, value):
            self._value = value
            self.used = False
    
        @property
        def value(self):
            self.used = True # value was read
            return self._value
    

    其次,我们要求必须在with-block 中检索Result 对象。当块退出时(__exit__),我们检查该值是否已被读取。为了处理用户不使用with-statement 的情况,我们还检查在垃圾收集时是否已读取该值(__del__)。出现的异常将转换为警告。不过我通常会回避__del__

    class RequireUsage(object):
        def __init__(self, value):
            self._result = Result(value)
    
        def __enter__(self):
            return self._result
    
        def __exit__(self, type, value, traceback):
            if type is None: # No exception raised
                if not self._result.used:
                    raise RuntimeError("Result was unused")
    
        def __del__(self):
            if not self._result.used:
                raise RuntimeError("Result was unused (gc)")
    

    要使用这个系统,只需将返回值包装在RequireUsage 中,如下所示:

    def div2(n):
        return RequireUsage(n/2)
    

    要使用这些函数,请将它们包装在一个块中并提取value

    with div2(10) as result:
        print(result.value)
    

    如果你不在任何地方调用result.value,你现在会得到一个异常:

    with div2(10) as result:
        pass # exception will be thrown
    

    同样,如果你只调用div2 而没有with,你也会得到一个异常:

    div2(10) # will also raise an exception
    

    我不会特别推荐在实际程序中使用这种确切的方法,因为我觉得它在添加很少的情况下使使用变得更加困难。此外,还有一些未涵盖的边缘情况,例如您只需执行 res = div2(10),因为保留的引用会阻止调用 __del__

    【讨论】:

    • 谢谢,我目前正在使用 None 选项来实现这一点。但有时我忘记在呼叫现场处理 None 条件。这有时会导致运行时错误。我想知道是否有某种方法可以在呼叫站点强制处理无案例检查以避免运行时错误。当我使用 Option 作为返回类型时,作为编译语言的 JVM 语言会强制执行此操作。 Python 有类似的东西吗?
    • Python 不能做静态类型检查,所以不要忘记处理None
    • @Abin 如果没有有意义的结果,则引发异常会强制您处理它。
    • 感谢 cmets。
    • @Abin:我已经对这个答案进行了大量编辑,现在似乎有点离题了。在你原来的情况下,我只是提出一个异常而不是返回 None,因为(正如 mkrieger1 所说)它需要用户处理这种情况。
    【解决方案3】:

    一种方法是返回()(<type>,)(在您的情况下为int)。然后你可以想出一些像这样的辅助函数:

    def get_or_else(option: tuple, default):
      if option:
        return option[0]
      else:
        return default
    

    这种方法的一个好处是您不需要依赖第三方库来实现它。作为对手,你基本上必须自己做一个小图书馆。

    【讨论】:

      猜你喜欢
      • 2017-02-10
      • 1970-01-01
      • 1970-01-01
      • 2019-01-23
      • 1970-01-01
      • 2015-07-04
      • 1970-01-01
      • 2020-02-29
      • 2020-09-23
      相关资源
      最近更新 更多