避免“if x:return x”语句的Pythonic方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36117583/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 17:26:53  来源:igfitidea点击:

Pythonic way to avoid "if x: return x" statements

pythonif-statement

提问by Bernard

I have a method that calls 4 other methods in sequence to check for specific conditions, and returns immediately (not checking the following ones) whenever one returns something Truthy.

我有一个方法,它依次调用 4 个其他方法来检查特定条件,并在返回真实值时立即返回(不检查以下方法)。

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

This seems like a lot of baggage code. Instead of each 2-line if statement, I'd rather do something like:

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

x and return x

But that is invalid Python. Am I missing a simple, elegant solution here? Incidentally, in this situation, those four check methods may be expensive, so I do not want to call them multiple times.

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

回答by timgeb

Alternatively to Martijn's fine answer, you could chain or. This will return the first truthy value, or Noneif there's no truthy value:

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

def check_all_conditions():
    return check_size() or check_color() or check_tone() or check_flavor() or None

Demo:

演示:

>>> x = [] or 0 or {} or -1 or None
>>> x
-1
>>> x = [] or 0 or {} or '' or None
>>> x is None
True

回答by Martijn Pieters

You could use a loop:

您可以使用循环:

conditions = (check_size, check_color, check_tone, check_flavor)
for condition in conditions:
    result = condition()
    if result:
        return result

This has the added advantage that you can now make the number of conditions variable.

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

You could use map()+ filter()(the Python 3 versions, use the future_builtinsversionsin Python 2) to get the first such matching value:

您可以使用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)

but if this is more readable is debatable.

但如果这更具可读性是有争议的。

Another option is to use a generator expression:

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

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)

回答by Hyman Aidley

Don't change it

不要改变它

There are other ways of doing this as the various other answers show. None are as clear as your original code.

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

回答by Wayne Werner

In effectively the same answer as timgeb, but you could use parenthesis for nicer formatting:

实际上与 timgeb 的答案相同,但您可以使用括号进行更好的格式设置:

def check_all_the_things():
    return (
        one()
        or two()
        or five()
        or three()
        or None
    )

回答by Phil Frost

According to Curly's law, you can make this code more readable by splitting two concerns:

根据Curly 定律,您可以通过拆分两个关注点来使此代码更具可读性:

  • What things do I check?
  • Has one thing returned true?
  • 我要检查哪些项目?
  • 一件事回归真了吗?

into two functions:

分为两个功能:

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

This avoids:

这避免了:

  • complicated logical structures
  • really long lines
  • repetition
  • 复杂的逻辑结构
  • 很长的队伍
  • 重复

...while preserving a linear, easy to read flow.

...同时保持线性、易于阅读的流程。

You can probably also come up with even better function names, according to your particular circumstance, which make it even more readable.

根据您的特定情况,您可能还可以想出更好的函数名称,这使其更具可读性。

回答by Phil Frost

This is a variant of Martijns first example. It also uses the "collection of callables"-style in order to allow short-circuiting.

这是 Martijns 第一个示例的变体。它还使用“可调用集合”样式以允许短路。

Instead of a loop you can use the builtin any.

您可以使用内置的any.

conditions = (check_size, check_color, check_tone, check_flavor)
return any(condition() for condition in conditions) 

Note that anyreturns a boolean, so if you need the exact return value of the check, this solution will not work. anywill not distinguish between 14, 'red', 'sharp', 'spicy'as return values, they will all be returned as True.

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

回答by zwol

Have you considered just writing if x: return xall on one line?

您是否考虑过将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

This isn't any less repetitivethan what you had, but IMNSHO it reads quite a bit smoother.

这并不比你所拥有的重复性更少,但 IMNSHO 它读起来相当流畅。

回答by ngasull

I'm quite surprised nobody mentioned the built-in anywhich is made for this purpose:

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

def check_all_conditions():
    return any([
        check_size(),
        check_color(),
        check_tone(),
        check_flavor()
    ])

Note that although this implementation is probably the clearest, it evaluates all the checks even if the first one is True.

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



If you really need to stop at the first failed check, consider using reducewhich is made to convert a list to a simple value:

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

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

reduce(function, iterable[, initializer]): Apply function of two arguments cumulatively to the items of iterable, from left to right, so as to reduce the iterable to a single value. The left argument, x, is the accumulated value and the right argument, y, is the update value from the iterable. If the optional initializer is present, it is placed before the items of the iterable in the calculation

reduce(function, iterable[, initializer]): 将两个参数的函数从左到右累加应用于iterable的项,从而将iterable减少为单个值。左参数 x 是累加值,右参数 y 是可迭代对象的更新值。如果存在可选的初始化器,它会被放置在计算中的可迭代项之前

In your case:

在你的情况下:

  • lambda a, f: a or f()is the function that checks that either the accumulator aor the current check f()is True. Note that if ais True, f()won't be evaluated.
  • checkscontains check functions (the fitem from the lambda)
  • Falseis the initial value, otherwise no check would happen and the result would always be True
  • lambda a, f: a or f()是检查累加器a或当前检查f()是否为 的函数True。请注意,如果aTruef()则不会被评估。
  • checks包含检查函数(f来自 lambda的项目)
  • False是初始值,否则不会发生检查,结果总是 True

anyand reduceare basic tools for functional programming. I strongly encourage you to train these out as well as mapwhich is awesome too!

anyreduce对于函数式编程的基本工具。我强烈鼓励你训练这些,map这也很棒!

回答by Phinet

If you want the same code structure, you could use ternary statements!

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

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

I think this looks nice and clear if you look at it.

如果你看它,我认为这看起来很好很清楚。

Demo:

演示:

Screenshot of it running

运行截图

回答by juandesant

For me, the best answer is that from @phil-frost, followed by @wayne-werner's.

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

What I find interesting is that no one has said anything about the fact that a function will be returning many different data types, which will make then mandatory to do checks on the type of x itself to do any further work.

我觉得有趣的是,没有人说过函数将返回许多不同的数据类型这一事实,这将强制检查 x 本身的类型以做任何进一步的工作。

So I would mix @PhilFrost's response with the idea of keeping a single type:

所以我会将@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

Notice that xis passed as an argument, but also all_conditionsis used as a passed generator of checking functions where all of them get an xto be checked, and return Trueor False. By using funcwith all_conditionsas default value, you can use assessed_x(x), or you can pass a further personalised generator via func.

请注意,x它作为参数传递,但也all_conditions用作检查函数的传递生成器,其中所有检查函数都得到 anx被检查,并返回Trueor False。通过使用funcwithall_conditions作为默认值,您可以使用assessed_x(x),或者您可以通过 传递进一步的个性化生成器func

That way, you get xas soon as one check passes, but it will always be the same type.

这样一来,只要x通过了一次检查,您就会得到,但它始终是相同的类型。