避免“if x: return x”语句的python方式

时间:2021-09-13 21:35:12

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:

这看起来像是很多行李编码。我宁愿做如下的事情:

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。我是否错过了一个简单、优雅的解决方案?顺便说一下,在这种情况下,这四种检查方法可能比较昂贵,所以我不想多次调用它们。

17 个解决方案

#1


268  

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_builtins versions in 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)

#2


381  

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

另外,你也可以用链条或。这将返回第一个真实的价值,或者没有真实的价值,

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

#3


81  

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.

还有其他的方法可以做到这一点。没有一种代码与您的原始代码一样清晰。

#4


72  

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
    )

#5


71  

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.

根据您的具体情况,您可能还可以提出更好的函数名,从而使其更具可读性。

#6


40  

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.

你可以使用内建函数来代替循环。

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

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

注意,any返回一个布尔值,因此如果您需要检查的确切返回值,这个解决方案将不起作用。任何将不区分14、“红”、“锐”、“辣”作为返回值,它们都将返回为True。

#7


25  

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

你是否考虑过如果x:在一行上返回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 repetitive than what you had, but IMNSHO it reads quite a bit smoother.

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

#8


23  

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

我很惊讶没有人提到内置的任何东西是为了这个目的:

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.

请注意,尽管这个实现可能是最清晰的,但它会对所有的检查进行评估,即使第一个检查是正确的。


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

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

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的项,从而将可迭代性减少到单个值。左边的参数x,是累积的值,右边的参数y,是来自iterable的更新值。如果存在可选初始化器,则将其放在计算中的可迭代项之前

In your case:

在你的例子:

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

any and reduce are basic tools for functional programming. I strongly encourage you to train these out as well as map which is awesome too!

任何和减少都是函数式编程的基本工具。我强烈建议你把这些训练出来,还有地图,这也很棒!

#9


18  

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:

演示:

避免“if x: return x”语句的python方式

#10


3  

A slight variation on Martijns first example above, that avoids the if inside the loop:

上面的第一个例子中,对Martijns稍有改动,避免了循环内部的if:

Status = None
for c in [check_size, check_color, check_tone, check_flavor]:
  Status = Status or c();
return Status

#11


3  

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 x is passed as an argument, but also all_conditions is used as a passed generator of checking functions where all of them get an x to be checked, and return True or False. By using func with all_conditions as default value, you can use assessed_x(x), or you can pass a further personalised generator via func.

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

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

这样,一旦一个检查通过,就会得到x,但类型总是相同的。

#12


2  

Ideally, I would re-write the check_ functions to return True or False rather than a value. Your checks then become

理想情况下,我将重写check_函数以返回True或False而不是值。你的检查然后成为

if check_size(x):
    return x
#etc

Assuming your x is not immutable, your function can still modify it (although they can't reassign it) - but a function called check shouldn't really be modifying it anyway.

假设您的x不是不可变的,那么您的函数仍然可以修改它(尽管它们不能重新分配它)——但是一个名为check的函数实际上不应该修改它。

#13


2  

This way is a little bit outside of the box, but I think the end result is simple, readable, and looks nice.

这种方式有点超出了框框的范围,但我认为最终的结果是简单的、可读的,而且看起来很不错。

The basic idea is to raise an exception when one of the functions evaluates as truthy, and return the result. Here's how it might look:

基本思想是,当其中一个函数计算为truthy时,抛出异常,并返回结果。它的样子是这样的:

def check_conditions():
    try:
        assertFalsey(
            check_size,
            check_color,
            check_tone,
            check_flavor)
    except TruthyException as e:
        return e.trigger
    else:
        return None

You'll need a assertFalsey function that raises an exception when one of the called function arguments evaluates as truthy:

您将需要一个assertFalsey函数,当其中一个被调用的函数参数计算为truthy时,该函数将引发异常:

def assertFalsey(*funcs):
    for f in funcs:
        o = f()
        if o:
            raise TruthyException(o)

The above could be modified so as to also provide arguments for the functions to be evaluated.

可以修改上面的内容,以便为要计算的函数提供参数。

And of course you'll need the TruthyException itself. This exception provides the object that triggered the exception:

当然,你也需要TruthyException本身。此异常提供触发该异常的对象:

class TruthyException(Exception):
    def __init__(self, obj, *args):
        super().__init__(*args)
        self.trigger = obj

You can turn the original function into something more general, of course:

当然,你可以把原来的函数变成更一般的东西:

def get_truthy_condition(*conditions):
    try:
        assertFalsey(*conditions)
    except TruthyException as e:
        return e.trigger
    else:
        return None

result = get_truthy_condition(check_size, check_color, check_tone, check_flavor)

This might be a bit slower because you are using both an if statement and handling an exception. However, the exception is only handled a maximum of one time, so the hit to performance should be minor unless you expect to run the check and get a True value many many thousands of times.

这可能有点慢,因为您同时使用if语句和处理异常。但是,这个异常最多只能处理一次,因此对性能的影响应该很小,除非您期望运行检查并获得数千次的真实值。

#14


1  

The pythonic way is either using reduce (as someone already mentioned) or itertools (as shown below), but it seems to me that simply using short circuiting of the or operator produces clearer code

python的方法是使用reduce(如前面提到的)或itertools(如下所示),但是在我看来,简单地使用或运算符的短路会产生更清晰的代码。

from itertools import imap, dropwhile

def check_all_conditions():
    conditions = (check_size,\
        check_color,\
        check_tone,\
        check_flavor)
    results_gen = dropwhile(lambda x:not x, imap(lambda check:check(), conditions))
    try:
        return results_gen.next()
    except StopIteration:
        return None

#15


1  

I like @timgeb's. In the meantime I would like to add that expressing None in the return statement is not needed as the collection of or separated statements are evaluated and the first none-zero, none-empty, none-None is returned and if there isn't any then None is returned whether there is a None or not!

我喜欢@timgeb的。同时我想添加的表达没有返回语句的集合或分隔语句不需要评估和第一个非零,none-empty,none-None返回如果没有那么没有返回是否有或没有一个不是!

So my check_all_conditions() function looks like this:

我的check_all_conditions()函数是这样的:

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

Using timeit with number=10**7 I looked at the running time of a number of the suggestions. For the sake of comparison I just used the random.random() function to return a string or None based on random numbers. Here is the whole code:

使用时间,数字=10**7,我查看了一些建议的运行时间。为了比较起见,我只使用随机的。random()函数来返回一个字符串,或者没有基于随机数的函数。这里是整个代码:

import random
import timeit

def check_size():
    if random.random() < 0.25: return "BIG"

def check_color():
    if random.random() < 0.25: return "RED"

def check_tone():
    if random.random() < 0.25: return "SOFT"

def check_flavor():
    if random.random() < 0.25: return "SWEET"

def check_all_conditions_Bernard():
    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

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

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

def check_all_conditions_Reza():
    return check_size() or check_color() or check_tone() or check_flavor()

def check_all_conditions_Phinet():
    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

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

def check_all_conditions_Phil_Frost():
    for condition in all_conditions():
        if condition:
            return condition

def main():
    num = 10000000
    random.seed(20)
    print("Bernard:", timeit.timeit('check_all_conditions_Bernard()', 'from __main__ import check_all_conditions_Bernard', number=num))
    random.seed(20)
    print("Martijn Pieters:", timeit.timeit('check_all_Martijn_Pieters()', 'from __main__ import check_all_Martijn_Pieters', number=num))
    random.seed(20)
    print("timgeb:", timeit.timeit('check_all_conditions_timgeb()', 'from __main__ import check_all_conditions_timgeb', number=num))
    random.seed(20)
    print("Reza:", timeit.timeit('check_all_conditions_Reza()', 'from __main__ import check_all_conditions_Reza', number=num))
    random.seed(20)
    print("Phinet:", timeit.timeit('check_all_conditions_Phinet()', 'from __main__ import check_all_conditions_Phinet', number=num))
    random.seed(20)
    print("Phil Frost:", timeit.timeit('check_all_conditions_Phil_Frost()', 'from __main__ import check_all_conditions_Phil_Frost', number=num))

if __name__ == '__main__':
    main()

And here are the results:

结果如下:

Bernard: 7.398444877040768
Martijn Pieters: 8.506569201346597
timgeb: 7.244275416364456
Reza: 6.982133448743038
Phinet: 7.925932800076634
Phil Frost: 11.924794811353031

#16


0  

I'm going to jump in here and have never written a single line of Python, but I assume if x = check_something(): return x is valid?

我将跳到这里,并且从来没有写过Python的任何一行,但是我假设如果x = check_something(): return x是有效的?

if so:

如果是这样:

def check_all_conditions():

    if x = check_size(): return x

    if x = check_color(): return x

    if x = check_tone(): return x

    if x = check_flavor(): return x

    return None

#17


-2  

I have seen some interesting implementations of switch/case statements with dicts in the past that led me to this answer. Using the example you've provided you would get the following. (It's madness using_complete_sentences_for_function_names, so check_all_conditions is renamed to status. See (1))

在过去,我看到了一些有趣的开关/case语句的实现,这些都是我的答案。使用您提供的示例将得到以下内容。(这是疯狂using_complete_sentences_for_function_names,所以check_all_conditions被重命名为status。参见(1))

def status(k = 'a', s = {'a':'b','b':'c','c':'d','d':None}) :
  select = lambda next, test : test if test else next
  d = {'a': lambda : select(s['a'], check_size()  ),
       'b': lambda : select(s['b'], check_color() ),
       'c': lambda : select(s['c'], check_tone()  ),
       'd': lambda : select(s['d'], check_flavor())}
  while k in d : k = d[k]()
  return k

The select function eliminates the need to call each check_FUNCTION twice i.e. you avoid check_FUNCTION() if check_FUNCTION() else next by adding another function layer. This is useful for long running functions. The lambdas in the dict delay execution of it's values until the while loop.

select函数消除了调用每个check_FUNCTION两次的需要,例如,如果check_FUNCTION()接下来添加另一个函数层,您将避免check_FUNCTION()。这对于长时间运行的函数很有用。dict中的lambdas将其值的执行延迟到while循环。

As a bonus you may modify the execution order and even skip some of the tests by altering k and s e.g. k='c',s={'c':'b','b':None} reduces the number of tests and reverses the original processing order.

作为奖励,您可以修改执行顺序,甚至通过修改k和s来跳过一些测试,例如k='c',s={'c':'b','b':None}减少了测试的数量,并反转了原始的处理顺序。

The timeit fellows might haggle over the cost of adding an extra layer or two to the stack and the cost for the dict look up but you seem more concerned with the prettiness of the code.

it人员可能会为向堆栈增加一到两个层的成本以及查找dict语句的成本而讨价还价,但您似乎更关心代码的美观。

Alternatively a simpler implementation might be the following :

另一种更简单的实现方法是:

def status(k=check_size) :
  select = lambda next, test : test if test else next
  d = {check_size  : lambda : select(check_color,  check_size()  ),
       check_color : lambda : select(check_tone,   check_color() ),
       check_tone  : lambda : select(check_flavor, check_tone()  ),
       check_flavor: lambda : select(None,         check_flavor())}
  while k in d : k = d[k]()
  return k
  1. I mean this not in terms of pep8 but in terms of using one concise descriptive word in place of a sentence. Granted the OP may be following some coding convention, working one some existing code base or not care for terse terms in their codebase.
  2. 我的意思不是说总统防治艾滋病紧急救援计划,而是说用一个简明的描述性词汇来代替句子。假定OP可能遵循一些编码约定,使用一些现有的代码基,或者不关心代码基中的术语。

#1


268  

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_builtins versions in 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)

#2


381  

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

另外,你也可以用链条或。这将返回第一个真实的价值,或者没有真实的价值,

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

#3


81  

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.

还有其他的方法可以做到这一点。没有一种代码与您的原始代码一样清晰。

#4


72  

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
    )

#5


71  

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.

根据您的具体情况,您可能还可以提出更好的函数名,从而使其更具可读性。

#6


40  

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.

你可以使用内建函数来代替循环。

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

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

注意,any返回一个布尔值,因此如果您需要检查的确切返回值,这个解决方案将不起作用。任何将不区分14、“红”、“锐”、“辣”作为返回值,它们都将返回为True。

#7


25  

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

你是否考虑过如果x:在一行上返回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 repetitive than what you had, but IMNSHO it reads quite a bit smoother.

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

#8


23  

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

我很惊讶没有人提到内置的任何东西是为了这个目的:

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.

请注意,尽管这个实现可能是最清晰的,但它会对所有的检查进行评估,即使第一个检查是正确的。


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

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

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的项,从而将可迭代性减少到单个值。左边的参数x,是累积的值,右边的参数y,是来自iterable的更新值。如果存在可选初始化器,则将其放在计算中的可迭代项之前

In your case:

在你的例子:

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

any and reduce are basic tools for functional programming. I strongly encourage you to train these out as well as map which is awesome too!

任何和减少都是函数式编程的基本工具。我强烈建议你把这些训练出来,还有地图,这也很棒!

#9


18  

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:

演示:

避免“if x: return x”语句的python方式

#10


3  

A slight variation on Martijns first example above, that avoids the if inside the loop:

上面的第一个例子中,对Martijns稍有改动,避免了循环内部的if:

Status = None
for c in [check_size, check_color, check_tone, check_flavor]:
  Status = Status or c();
return Status

#11


3  

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 x is passed as an argument, but also all_conditions is used as a passed generator of checking functions where all of them get an x to be checked, and return True or False. By using func with all_conditions as default value, you can use assessed_x(x), or you can pass a further personalised generator via func.

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

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

这样,一旦一个检查通过,就会得到x,但类型总是相同的。

#12


2  

Ideally, I would re-write the check_ functions to return True or False rather than a value. Your checks then become

理想情况下,我将重写check_函数以返回True或False而不是值。你的检查然后成为

if check_size(x):
    return x
#etc

Assuming your x is not immutable, your function can still modify it (although they can't reassign it) - but a function called check shouldn't really be modifying it anyway.

假设您的x不是不可变的,那么您的函数仍然可以修改它(尽管它们不能重新分配它)——但是一个名为check的函数实际上不应该修改它。

#13


2  

This way is a little bit outside of the box, but I think the end result is simple, readable, and looks nice.

这种方式有点超出了框框的范围,但我认为最终的结果是简单的、可读的,而且看起来很不错。

The basic idea is to raise an exception when one of the functions evaluates as truthy, and return the result. Here's how it might look:

基本思想是,当其中一个函数计算为truthy时,抛出异常,并返回结果。它的样子是这样的:

def check_conditions():
    try:
        assertFalsey(
            check_size,
            check_color,
            check_tone,
            check_flavor)
    except TruthyException as e:
        return e.trigger
    else:
        return None

You'll need a assertFalsey function that raises an exception when one of the called function arguments evaluates as truthy:

您将需要一个assertFalsey函数,当其中一个被调用的函数参数计算为truthy时,该函数将引发异常:

def assertFalsey(*funcs):
    for f in funcs:
        o = f()
        if o:
            raise TruthyException(o)

The above could be modified so as to also provide arguments for the functions to be evaluated.

可以修改上面的内容,以便为要计算的函数提供参数。

And of course you'll need the TruthyException itself. This exception provides the object that triggered the exception:

当然,你也需要TruthyException本身。此异常提供触发该异常的对象:

class TruthyException(Exception):
    def __init__(self, obj, *args):
        super().__init__(*args)
        self.trigger = obj

You can turn the original function into something more general, of course:

当然,你可以把原来的函数变成更一般的东西:

def get_truthy_condition(*conditions):
    try:
        assertFalsey(*conditions)
    except TruthyException as e:
        return e.trigger
    else:
        return None

result = get_truthy_condition(check_size, check_color, check_tone, check_flavor)

This might be a bit slower because you are using both an if statement and handling an exception. However, the exception is only handled a maximum of one time, so the hit to performance should be minor unless you expect to run the check and get a True value many many thousands of times.

这可能有点慢,因为您同时使用if语句和处理异常。但是,这个异常最多只能处理一次,因此对性能的影响应该很小,除非您期望运行检查并获得数千次的真实值。

#14


1  

The pythonic way is either using reduce (as someone already mentioned) or itertools (as shown below), but it seems to me that simply using short circuiting of the or operator produces clearer code

python的方法是使用reduce(如前面提到的)或itertools(如下所示),但是在我看来,简单地使用或运算符的短路会产生更清晰的代码。

from itertools import imap, dropwhile

def check_all_conditions():
    conditions = (check_size,\
        check_color,\
        check_tone,\
        check_flavor)
    results_gen = dropwhile(lambda x:not x, imap(lambda check:check(), conditions))
    try:
        return results_gen.next()
    except StopIteration:
        return None

#15


1  

I like @timgeb's. In the meantime I would like to add that expressing None in the return statement is not needed as the collection of or separated statements are evaluated and the first none-zero, none-empty, none-None is returned and if there isn't any then None is returned whether there is a None or not!

我喜欢@timgeb的。同时我想添加的表达没有返回语句的集合或分隔语句不需要评估和第一个非零,none-empty,none-None返回如果没有那么没有返回是否有或没有一个不是!

So my check_all_conditions() function looks like this:

我的check_all_conditions()函数是这样的:

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

Using timeit with number=10**7 I looked at the running time of a number of the suggestions. For the sake of comparison I just used the random.random() function to return a string or None based on random numbers. Here is the whole code:

使用时间,数字=10**7,我查看了一些建议的运行时间。为了比较起见,我只使用随机的。random()函数来返回一个字符串,或者没有基于随机数的函数。这里是整个代码:

import random
import timeit

def check_size():
    if random.random() < 0.25: return "BIG"

def check_color():
    if random.random() < 0.25: return "RED"

def check_tone():
    if random.random() < 0.25: return "SOFT"

def check_flavor():
    if random.random() < 0.25: return "SWEET"

def check_all_conditions_Bernard():
    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

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

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

def check_all_conditions_Reza():
    return check_size() or check_color() or check_tone() or check_flavor()

def check_all_conditions_Phinet():
    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

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

def check_all_conditions_Phil_Frost():
    for condition in all_conditions():
        if condition:
            return condition

def main():
    num = 10000000
    random.seed(20)
    print("Bernard:", timeit.timeit('check_all_conditions_Bernard()', 'from __main__ import check_all_conditions_Bernard', number=num))
    random.seed(20)
    print("Martijn Pieters:", timeit.timeit('check_all_Martijn_Pieters()', 'from __main__ import check_all_Martijn_Pieters', number=num))
    random.seed(20)
    print("timgeb:", timeit.timeit('check_all_conditions_timgeb()', 'from __main__ import check_all_conditions_timgeb', number=num))
    random.seed(20)
    print("Reza:", timeit.timeit('check_all_conditions_Reza()', 'from __main__ import check_all_conditions_Reza', number=num))
    random.seed(20)
    print("Phinet:", timeit.timeit('check_all_conditions_Phinet()', 'from __main__ import check_all_conditions_Phinet', number=num))
    random.seed(20)
    print("Phil Frost:", timeit.timeit('check_all_conditions_Phil_Frost()', 'from __main__ import check_all_conditions_Phil_Frost', number=num))

if __name__ == '__main__':
    main()

And here are the results:

结果如下:

Bernard: 7.398444877040768
Martijn Pieters: 8.506569201346597
timgeb: 7.244275416364456
Reza: 6.982133448743038
Phinet: 7.925932800076634
Phil Frost: 11.924794811353031

#16


0  

I'm going to jump in here and have never written a single line of Python, but I assume if x = check_something(): return x is valid?

我将跳到这里,并且从来没有写过Python的任何一行,但是我假设如果x = check_something(): return x是有效的?

if so:

如果是这样:

def check_all_conditions():

    if x = check_size(): return x

    if x = check_color(): return x

    if x = check_tone(): return x

    if x = check_flavor(): return x

    return None

#17


-2  

I have seen some interesting implementations of switch/case statements with dicts in the past that led me to this answer. Using the example you've provided you would get the following. (It's madness using_complete_sentences_for_function_names, so check_all_conditions is renamed to status. See (1))

在过去,我看到了一些有趣的开关/case语句的实现,这些都是我的答案。使用您提供的示例将得到以下内容。(这是疯狂using_complete_sentences_for_function_names,所以check_all_conditions被重命名为status。参见(1))

def status(k = 'a', s = {'a':'b','b':'c','c':'d','d':None}) :
  select = lambda next, test : test if test else next
  d = {'a': lambda : select(s['a'], check_size()  ),
       'b': lambda : select(s['b'], check_color() ),
       'c': lambda : select(s['c'], check_tone()  ),
       'd': lambda : select(s['d'], check_flavor())}
  while k in d : k = d[k]()
  return k

The select function eliminates the need to call each check_FUNCTION twice i.e. you avoid check_FUNCTION() if check_FUNCTION() else next by adding another function layer. This is useful for long running functions. The lambdas in the dict delay execution of it's values until the while loop.

select函数消除了调用每个check_FUNCTION两次的需要,例如,如果check_FUNCTION()接下来添加另一个函数层,您将避免check_FUNCTION()。这对于长时间运行的函数很有用。dict中的lambdas将其值的执行延迟到while循环。

As a bonus you may modify the execution order and even skip some of the tests by altering k and s e.g. k='c',s={'c':'b','b':None} reduces the number of tests and reverses the original processing order.

作为奖励,您可以修改执行顺序,甚至通过修改k和s来跳过一些测试,例如k='c',s={'c':'b','b':None}减少了测试的数量,并反转了原始的处理顺序。

The timeit fellows might haggle over the cost of adding an extra layer or two to the stack and the cost for the dict look up but you seem more concerned with the prettiness of the code.

it人员可能会为向堆栈增加一到两个层的成本以及查找dict语句的成本而讨价还价,但您似乎更关心代码的美观。

Alternatively a simpler implementation might be the following :

另一种更简单的实现方法是:

def status(k=check_size) :
  select = lambda next, test : test if test else next
  d = {check_size  : lambda : select(check_color,  check_size()  ),
       check_color : lambda : select(check_tone,   check_color() ),
       check_tone  : lambda : select(check_flavor, check_tone()  ),
       check_flavor: lambda : select(None,         check_flavor())}
  while k in d : k = d[k]()
  return k
  1. I mean this not in terms of pep8 but in terms of using one concise descriptive word in place of a sentence. Granted the OP may be following some coding convention, working one some existing code base or not care for terse terms in their codebase.
  2. 我的意思不是说总统防治艾滋病紧急救援计划,而是说用一个简明的描述性词汇来代替句子。假定OP可能遵循一些编码约定,使用一些现有的代码基,或者不关心代码基中的术语。