Pythonic方法避免"if x:return x"语句

Ber*_*ard 217 python if-statement

我有一个方法,按顺序调用其他4个方法来检查特定的条件,并在每次返回Truthy时立即返回(不检查以下的方法).

def check_all_conditions():
    x = check_size()
    if x:
        return x

    x = check_color()
    if x:
        return x

    x = check_tone()
    if x:
        return x

    x = check_flavor()
    if x:
        return x
    return None
Run Code Online (Sandbox Code Playgroud)

这似乎是很多行李代码.而不是每个2行if语句,我宁愿做类似的事情:

x and return x
Run Code Online (Sandbox Code Playgroud)

但那是无效的Python.我在这里错过了一个简单优雅的解决方案吗?顺便说一下,在这种情况下,这四种检查方法可能很昂贵,所以我不想多次调用它们.

tim*_*geb 393

除了Martijn的好答案,你可以链接or.这将返回第一个真值,或者None如果没有真值:

def check_all_conditions():
    return check_size() or check_color() or check_tone() or check_flavor() or None
Run Code Online (Sandbox Code Playgroud)

演示:

>>> x = [] or 0 or {} or -1 or None
>>> x
-1
>>> x = [] or 0 or {} or '' or None
>>> x is None
True
Run Code Online (Sandbox Code Playgroud)

  • 我认为这是更好的解决方案.争论_"如果有多个选项,它会变得乏味[...]"_是没有意义的,因为单个函数不应该进行过多的检查.如果需要,则应将检查分成多个功能. (47认同)
  • @Caridorc:我非常不喜欢使用````来扩展逻辑行.改为使用括号; 所以`return(....)`根据需要插入换行符.不过,这将是一条长长的逻辑路线. (37认同)
  • 我会和你一样写[缩进](http://codepad.org/hSlHRGNc) (17认同)
  • @MartijnPieters你可以使用``````来将每个检查放在自己的行上. (14认同)
  • @MartijnPieters我从不暗示我的答案比你的好,我也喜欢你的答案:) (12认同)
  • 当然,如果有多个选项,那么快速阅读将变得乏味.此外,我的方法允许您使用可变数量的条件. (9认同)
  • (我倾向于认为这个解决方案和Martijn的解决方案一样,除了用`,'用`或'来划分你的列表项.在代码结构方面,它仍然是一个列表,所以我不购买它是更糟糕的阅读.也许更好,因为你不需要for循环,所以控制流程在阅读文本时字面上进展.) (7认同)
  • 我建议用@GrijeshChauhan使用的样式修改你的答案.这使**更容易阅读. (4认同)
  • @timgeb:我从不暗示你隐含任何东西.:-)我也喜欢你的答案! (2认同)
  • 我最初对"或者如果没有真正的价值"感到困惑,因为我认为你的意思是"或"的属性.我现在看到你并没有这么说,只是为了明确:没有truthy值的`或`链返回最右边的值,你选择的那个是'None`. (2认同)
  • 我同意这是更好的解决方案.它比循环方法更容易阅读,它可以扩展到某些检查需要不同输入参数的情况,而且我不认为`或`s链比维护列表更难以维护功能. (2认同)

Mar*_*ers 276

你可以使用一个循环:

conditions = (check_size, check_color, check_tone, check_flavor)
for condition in conditions:
    result = condition()
    if result:
        return result
Run Code Online (Sandbox Code Playgroud)

这具有额外的优势,您现在可以使条件数变量.

你可以使用map()+ filter()(Python 3版本,使用Python 2中的future_builtins版本)来获得第一个这样的匹配值:

try:
    # Python 2
    from future_builtins import map, filter
except ImportError:
    # Python 3
    pass

conditions = (check_size, check_color, check_tone, check_flavor)
return next(filter(None, map(lambda f: f(), conditions)), None)
Run Code Online (Sandbox Code Playgroud)

但如果这更具可读性是值得商榷的.

另一种选择是使用生成器表达式:

conditions = (check_size, check_color, check_tone, check_flavor)
checks = (condition() for condition in conditions)
return next((check for check in checks if check), None)
Run Code Online (Sandbox Code Playgroud)

  • 他们说,"喜欢Python".他们说,"Perl是不可读的".然后发生这种情况:`返回下一步((检查检查检查是否检查),无)`. (34认同)
  • 如果条件实际上只是条件,即在您的第一个提案中的布尔值,您也可以使用内置的"any"而不是循环.`return any(condition()条件中的条件)` (27认同)
  • 这个难以理解的代码块真的是"pythonian"吗?特别是压缩`conditions`和`arguments`的想法?这是恕我直言,比原始代码更糟糕,原始代码需要大约10秒才能解析. (15认同)
  • 可读性几乎胜过所有其他考虑因素.你说,map/filter是'有争议的',我把我的投票放在了难以置信的丑陋之中.当然,谢谢,但是如果我的团队中的任何人为此代码添加了地图/过滤器,我会将它们转移到另一个团队或分配给他们. (13认同)
  • @Leonhard:`any`里面的实现几乎相同.但它看起来好多了,请将其作为答案发布) (4认同)

Jac*_*ley 86

不要改变它

正如各种其他答案所示,还有其他方法可以做到这一点.没有一个像你的原始代码一样清晰.

  • 我反对这一点,但你的建议是合情合理的.就个人而言,我发现我的眼睛紧张地试图读取​​OP,例如,timgeb的解决方案立即点击. (39认同)
  • 这真的是一个意见问题.我个人而言,我会在`:`之后删除换行符,因为我认为`if x:return x`非常精细,它使函数看起来更紧凑.但那可能只是我. (3认同)
  • 这不仅仅是你.使用`或`作为timgeb确实是一个恰当且易于理解的习语.许多语言都有这个; 也许当它被称为"orelse"时,它甚至更加清晰,但即使是普通的`或'(或其他语言中的`||`)也可以被理解为如果第一个"不起作用的话,可以选择尝试". (2认同)

Way*_*ner 83

在与timgeb有效的答案中,您可以使用括号进行更好的格式化:

def check_all_the_things():
    return (
        one()
        or two()
        or five()
        or three()
        or None
    )
Run Code Online (Sandbox Code Playgroud)

  • 请大家帮忙将这个答案提高到第一名.尽你所能! (8认同)

Phi*_*ost 74

根据Curly定律,通过分割两个问题,可以使这些代码更具可读性:

  • 我要检查什么?
  • 有一件事是真的吗?

分为两个功能:

def all_conditions():
    yield check_size()
    yield check_color()
    yield check_tone()
    yield check_flavor()

def check_all_conditions():
    for condition in all_conditions():
        if condition:
            return condition
    return None
Run Code Online (Sandbox Code Playgroud)

这避免了:

  • 复杂的逻辑结构
  • 真的很长
  • 重复

...同时保留线性,易读的流程.

根据您的特定情况,您可能还可以提供更好的功能名称,这使其更具可读性.

  • 请注意,`return None`不是必需的,因为函数默认返回`None`.但是,明确地返回"无"是没有错的,我喜欢你选择这样做. (4认同)
  • 这是我最喜欢的!它也应对不同的检查和论据.很可能过度设计这个特定的例子,但对于未来的问题是一个非常有用的工具! (2认同)

小智 42

这是Martijns第一个例子的变种.它还使用"callables"风格,以便允许短路.

您可以使用内置而不是循环any.

conditions = (check_size, check_color, check_tone, check_flavor)
return any(condition() for condition in conditions) 
Run Code Online (Sandbox Code Playgroud)

请注意,any返回一个布尔值,因此如果您需要检查的确切返回值,此解决方案将不起作用.any不会区分14,'red','sharp','spicy'作为返回值,将他们全部返回True.


zwo*_*wol 27

你考虑过只写if x: return x一行吗?

def check_all_conditions():
    x = check_size()
    if x: return x

    x = check_color()
    if x: return x

    x = check_tone()
    if x: return x

    x = check_flavor()
    if x: return x

    return None
Run Code Online (Sandbox Code Playgroud)

这并不比你的重复少,但IMNSHO它读得更顺畅.


nga*_*ull 24

我很惊讶没有人提到any为此目的而制作的内置内容:

def check_all_conditions():
    return any([
        check_size(),
        check_color(),
        check_tone(),
        check_flavor()
    ])
Run Code Online (Sandbox Code Playgroud)

请注意,尽管此实现可能是最清楚的,但它会评估所有检查,即使第一个检查是True.


如果您确实需要在第一次失败检查时停止,请考虑使用reduce哪个将列表转换为简单值:

def check_all_conditions():
    checks = [check_size, check_color, check_tone, check_flavor]
    return reduce(lambda a, f: a or f(), checks, False)
Run Code Online (Sandbox Code Playgroud)

reduce(function, iterable[, initializer]):将两个参数的函数累加到可迭代的项目,从左到右,以便将迭代减少到单个值.左参数x是累加值,右参数y是迭代的更新值.如果存在可选的初始化程序,则将其放在计算中的iterable项之前

在你的情况下:

  • lambda a, f: a or f()是检查累加器a或当前检查的f()函数True.注意,如果aTrue,f()将不会被评估.
  • checks包含检查功能(f来自lambda 的项目)
  • False 是初始值,否则不会发生检查,结果总是如此 True

any并且reduce是函数式编程的基本工具.我强烈建议你训练这些以及map哪些也很棒!

  • `any`只有在检查实际返回一个布尔值,字面上是'True`或'False`时才有效,但问题没有指明.您需要使用`reduce`来返回检查返回的实际值.此外,使用生成器避免使用"any"评估所有检查是很容易的,例如`any(c()for c in(check_size,check_color,check_tone,check_flavor))`.如[Leonhard的回答](/sf/answers/2528883241/) (9认同)
  • @blint抱歉,我不清楚.该问题的目标是_return check_的结果(而不仅仅是指示检查是成功还是失败).我指出,如果从检查函数返回实际的布尔值,`any`仅适用于_that_目的. (3认同)
  • @DavidZ 实际上 `any` 适用于真值: `any([1, "abc", False]) == True` 和 `any(["", 0]) == False` (2认同)

Phi*_*net 19

如果你想要相同的代码结构,你可以使用三元语句!

def check_all_conditions():
    x = check_size()
    x = x if x else check_color()
    x = x if x else check_tone()
    x = x if x else check_flavor()

    return x if x else None
Run Code Online (Sandbox Code Playgroud)

如果你看一下,我觉得这很好看.

演示:

它运行的截图

  • @LegoStormtroopr我使用鱼壳,所以我用ascii鱼缸装饰它让我开心.:) (36认同)
  • `x if x else <something>`可以简化为`x或<something>` (9认同)
  • 您的终端上方的小型ASCII鱼是什么? (7认同)
  • 你可以在https://fishshell.com获取鱼,ascii的配置文件在http://pastebin.com/yYVYvVeK,编辑器也是崇高的文本. (4认同)
  • 感谢细鱼(顺便提一下,那是什么编辑?) (3认同)
  • ...一旦你用`x或y'替换那些'x if x else y`,它只是@ timgeb版本的一小步. (2认同)
  • 这具有不会短路的缺点. (2认同)

jua*_*ant 5

对我来说,最好的答案是@ phil-frost,其次是@ wayne-werner。

我发现有趣的是,没有人说过一个函数将返回许多不同数据类型这一事实,这将使然后必须对x本身的类型进行检查以进行进一步的工作。

因此,我将@PhilFrost的响应与保持单一类型的想法混合在一起:

def all_conditions(x):
    yield check_size(x)
    yield check_color(x)
    yield check_tone(x)
    yield check_flavor(x)

def assessed_x(x,func=all_conditions):
    for condition in func(x):
        if condition:
            return x
    return None
Run Code Online (Sandbox Code Playgroud)

请注意,x它作为参数传递,但也all_conditions用作检查函数的传递生成器,其中所有函数都x将被检查,然后返回TrueFalse。通过使用funcwith all_conditions作为默认值,您可以使用assessed_x(x),也可以通过传递进一步的个性化生成器func

这样一来,您x就可以通过一张支票,但是它始终是同一类型。