【问题标题】:Don't call function if None Value returned如果 None 返回值,则不要调用函数
【发布时间】:2014-02-15 06:13:52
【问题描述】:

假设你有一个函数可以返回一些对象或无:

def foobar(arg):
   if arg == 'foo':
       return None
   else:
       return 'bar'

现在你调用这个方法并且你想对对象做一些事情,对于这个例子我得到一个str,所以我可能想调用upper()函数。 现在有两种情况会发生,第二种会失败,因为 None 没有方法upper()

foobar('sdfsdgf').upper()
foobar('foo').upper() 

当然,这现在很容易解决:

tmp = foobar('foo')
if tmp is not None:
    tmp.upper()
# or
try:
    foobar('foo').upper()
except ArgumentError:
    pass
# or
caller = lambda x: x.upper() if type(x) is str else None
caller(foobar('foo'))

但是异常处理可能不够具体,并且可能会发生我捕获可能很重要的异常(= 调试变得更加困难),而第一种方法很好,但可能会导致更大的代码,而第三个选项看起来很很好,但你必须对所有可能的功能都这样做,所以方法一可能是最好的。

我想知道是否有任何语法糖可以很好地处理这个问题?

【问题讨论】:

  • 你可以返回一个空字符串而不是'None'。
  • 当然可以,但是假设我调用的函数是一个库并且我不想更改 API
  • 你只能有一个语法方法,因为必须是一个调用:ret = foobar(arg); ret.upper() if ret else pass
  • @cox: ret.upper() 返回新字符串。而且你不能在条件表达式中使用pass
  • 我知道,就是例子;我不想用 [code][/code] 和缩进来回答它;泛型编程

标签: python syntactic-sugar


【解决方案1】:

你可以使用:

(foobar('foo') or '').upper()

括号内的表达式返回一个字符串,即使foobar() 返回一个假值。

确实导致None被空字符串替换。如果您的代码依赖于保留None,那么您最好的选择仍然是仅在返回值不是None 时使用单独的if 语句来调用.upper()

【讨论】:

  • 当然!我从来没有一次想到所有的语言特性:)
  • or 的技巧很巧妙,除非您在可能的返回值中有 几个 虚假值(0""None[] , 等等。)。只是一个提醒......
  • @Alfe:当然可以,但是由于您希望能够在这里调用.upper(),这看起来像是使用or优秀结果。 :-)
  • 我认为.upper() 只是一个更一般问题的示例。顺便说一句,我仍在考虑如何避免调用.upper() 的问题。在一般情况下(调用f(foobar('foo') or '')),这可能是一个代价高昂的调用,具体取决于f(),最好完全避免调用它。但我想不出没有 tmp 变量或此任务的特殊功能的解决方案。
  • @Alfe:你不能避免这个电话,除非你能先验地知道它什么时候返回None。然后你只是不调用该函数。但如果你无法预测它是否会返回None你别无选择,只能调用它,然后测试返回值。
【解决方案2】:

自从回答了这个问题后,有几个新的发展使这种模式更容易一些。

pymaybe 包允许您简单地用maybe 包装您的值,以静默传播None 值:

from pymaybe import maybe
maybe(foobar('foo')).upper()

这与 Martijn Pieters 的答案非常相似,只是它保留了 None 值而不是用空字符串替换它们,并且在存在多个错误值的情况下它会更加健壮。

此外,PEP 505 似乎有一个标准跟踪草案提案,旨在将类似于 C# 的 ?. 访问器的语法添加到语言本身。我不确定它有多少支持,但它是一个寻找可能的进一步增强功能的地方。

【讨论】:

    【解决方案3】:

    另一种方法 - 第一个/第三个选项可以结束:

    def forward_none(func):
        def wrapper(arg):
            return None if arg is None else func(arg)
        return wrapper
    

    请记住,方法不必用作方法 - 它们仍然是类的属性,在类中查找时,它们是普通函数:

    forward_none(str.upper)(foobar('foo'))
    

    我们也可以将其用作装饰器:

    @forward_none
    def do_interesting_things(value):
        # code that assumes value is not None...
    
    do_interesting_things(None) # ignores the original code and evaluates to None
    do_interesting_things("something") # as before decoration
    

    虽然在实践中,如果我不得不这样做,我可能会按照 Martijn 的建议去做。而且我会非常努力地不必这样做;进入这种情况是一种代码味道,表明我们应该提出异常而不是首先返回None。 :)

    【讨论】:

      【解决方案4】:

      使用海象运算符 python 3.8:

      if x := foobar("b"):  # Truthy if x is not None
          x.upper()
      

      ...或更明确地说:

      if (x := foobar("b")) is not None:
          x.upper()
      

      【讨论】:

        猜你喜欢
        • 2011-09-02
        • 2018-03-08
        • 1970-01-01
        • 1970-01-01
        • 2015-07-03
        • 1970-01-01
        • 1970-01-01
        • 2021-09-10
        • 2012-03-06
        相关资源
        最近更新 更多