标签归档:if-statement

“如果a或b或c但不是全部”的Python语法

问题:“如果a或b或c但不是全部”的Python语法

我有一个可以接收零个或三个命令行参数的python脚本。(要么以默认行为运行,要么需要指定所有三个值。)

诸如此类的理想语法是什么:

if a and (not b or not c) or b and (not a or not c) or c and (not b or not a):

I have a python script that can receive either zero or three command line arguments. (Either it runs on default behavior or needs all three values specified.)

What’s the ideal syntax for something like:

if a and (not b or not c) or b and (not a or not c) or c and (not b or not a):

?


回答 0

如果您的意思是最小形式,请执行以下操作:

if (not a or not b or not c) and (a or b or c):

这将翻译您的问题的标题。

更新:正如Volatility和Supr正确地说的那样,您可以应用De Morgan的定律并获得等效项:

if (a or b or c) and not (a and b and c):

我的建议是使用对您和其他程序员更重要的形式。第一个意思是“有一些错误,但也有一些真实”,第二个意思是“有一些真实,但不是一切”。如果要在硬件上进行优化或执行此操作,则选择第二个,这里只选择最易读的代码(还要考虑要测试的条件及其名称)。我选了第一个。

If you mean a minimal form, go with this:

if (not a or not b or not c) and (a or b or c):

Which translates the title of your question.

UPDATE: as correctly said by Volatility and Supr, you can apply De Morgan’s law and obtain equivalent:

if (a or b or c) and not (a and b and c):

My advice is to use whichever form is more significant to you and to other programmers. The first means “there is something false, but also something true”, the second “There is something true, but not everything”. If I were to optimize or do this in hardware, I would choose the second, here just choose the most readable (also taking in consideration the conditions you will be testing and their names). I picked the first.


回答 1

怎么样:

conditions = [a, b, c]
if any(conditions) and not all(conditions):
   ...

其他变体:

if 1 <= sum(map(bool, conditions)) <= 2:
   ...

How about:

conditions = [a, b, c]
if any(conditions) and not all(conditions):
   ...

Other variant:

if 1 <= sum(map(bool, conditions)) <= 2:
   ...

回答 2

这个问题已经有很多被高度认可的答案和一个被接受的答案,但是到目前为止,所有这些都被表达布尔问题的各种方法分散了注意力,并且错过了关键点:

我有一个可以接收零或三个命令行参数的python脚本。(要么以默认行为运行,要么需要指定所有三个值)

首先,此逻辑不应由您的代码负责,而应由argparse模块处理。不用费心编写复杂的if语句,而喜欢设置参数解析器,如下所示:

#!/usr/bin/env python
import argparse as ap
parser = ap.ArgumentParser()
parser.add_argument('--foo', nargs=3, default=['x', 'y', 'z'])
args = parser.parse_args()
print(args.foo)

是的,它应该是一个选项而不是位置参数,因为它毕竟是optional


编辑: 为了解决LarsH在注释中的问题,下面是一个示例示例,如果您确定要使用3个或0个位置 args的接口,可以如何编写它。我认为以前的接口是更好的样式,因为可选参数应该是 options,但是出于完整性考虑,这是一种替代方法。usage在创建解析器时,请注意最重要的kwarg,argparse否则会自动生成误导性的使用消息!

#!/usr/bin/env python
import argparse as ap
parser = ap.ArgumentParser(usage='%(prog)s [-h] [a b c]\n')
parser.add_argument('abc', nargs='*', help='specify 3 or 0 items', default=['x', 'y', 'z'])
args = parser.parse_args()
if len(args.abc) != 3:
  parser.error('expected 3 arguments')
print(args.abc)

以下是一些用法示例:

# default case
wim@wim-zenbook:/tmp$ ./three_or_none.py 
['x', 'y', 'z']

# explicit case
wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 3
['1', '2', '3']

# example failure mode
wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 
usage: three_or_none.py [-h] [a b c]
three_or_none.py: error: expected 3 arguments

This question already had many highly upvoted answers and an accepted answer, but all of them so far were distracted by various ways to express the boolean problem and missed a crucial point:

I have a python script that can receive either zero or three command line arguments. (Either it runs on default behavior or needs all three values specified)

This logic should not be the responsibility of your code in the first place, rather it should be handled by argparse module. Don’t bother writing a complex if statement, instead prefer to setup your argument parser something like this:

#!/usr/bin/env python
import argparse as ap
parser = ap.ArgumentParser()
parser.add_argument('--foo', nargs=3, default=['x', 'y', 'z'])
args = parser.parse_args()
print(args.foo)

And yes, it should be an option not a positional argument, because it is after all optional.


edited: To address the concern of LarsH in the comments, below is an example of how you could write it if you were certain you wanted the interface with either 3 or 0 positional args. I am of the opinion that the previous interface is better style, because optional arguments should be options, but here’s an alternative approach for the sake of completeness. Note the overriding kwarg usage when creating your parser, because argparse will auto-generate a misleading usage message otherwise!

#!/usr/bin/env python
import argparse as ap
parser = ap.ArgumentParser(usage='%(prog)s [-h] [a b c]\n')
parser.add_argument('abc', nargs='*', help='specify 3 or 0 items', default=['x', 'y', 'z'])
args = parser.parse_args()
if len(args.abc) != 3:
  parser.error('expected 3 arguments')
print(args.abc)

Here are some usage examples:

# default case
wim@wim-zenbook:/tmp$ ./three_or_none.py 
['x', 'y', 'z']

# explicit case
wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 3
['1', '2', '3']

# example failure mode
wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 
usage: three_or_none.py [-h] [a b c]
three_or_none.py: error: expected 3 arguments

回答 3

我会去:

conds = iter([a, b, c])
if any(conds) and not any(conds):
    # okay...

我认为这应该有效地短路

说明

通过做conds一个迭代器,any如果有任何一项为true ,则第一次使用will将使迭代器短路并使迭代器指向下一个元素。否则,它将消耗整个列表,并且为False。下一个any将迭代器中的其余项取为零,并确保没有其他任何真值…如果有,则整个语句不可能为真,因此就没有一个唯一元素(因此短路再次)。最后一个any将返回False或将耗尽iterable和be True

注意:以上检查是否仅设置了一个条件


如果要检查是否设置了一个或多个项目,但没有设置每个项目,则可以使用:

not all(conds) and any(conds)

I’d go for:

conds = iter([a, b, c])
if any(conds) and not any(conds):
    # okay...

I think this should short-circuit fairly efficiently

Explanation

By making conds an iterator, the first use of any will short circuit and leave the iterator pointing to the next element if any item is true; otherwise, it will consume the entire list and be False. The next any takes the remaining items in the iterable, and makes sure than there aren’t any other true values… If there are, the whole statement can’t be true, thus there isn’t one unique element (so short circuits again). The last any will either return False or will exhaust the iterable and be True.

note: the above checks if only a single condition is set


If you want to check if one or more items, but not every item is set, then you can use:

not all(conds) and any(conds)

回答 4

英文句子:

“如果是a或b或c,但不是全部”

转换为以下逻辑:

(a or b or c) and not (a and b and c)

单词“但是”通常表示连词,即“和”。此外,“所有这些”转换为的条件结合:这种情况下,该条件下,其它条件。“ not”反转整个连接。

我不同意接受的答案。作者忽略了将最直接的解释应用于规范,而忽略了应用戴摩根定律(De Morgan’s Law)将表达式简化为更少的运算符:

 not a or not b or not c  ->  not (a and b and c)

同时声称答案是“最小形式”。

The English sentence:

“if a or b or c but not all of them”

Translates to this logic:

(a or b or c) and not (a and b and c)

The word “but” usually implies a conjunction, in other words “and”. Furthermore, “all of them” translates to a conjunction of conditions: this condition, and that condition, and other condition. The “not” inverts that entire conjunction.

I do not agree that the accepted answer. The author neglected to apply the most straightforward interpretation to the specification, and neglected to apply De Morgan’s Law to simplify the expression to fewer operators:

 not a or not b or not c  ->  not (a and b and c)

while claiming that the answer is a “minimal form”.


回答 5

True如果三个条件之一并且只有一个是,则返回此值True。可能是您在示例代码中想要的。

if sum(1 for x in (a,b,c) if x) == 1:

This returns True if one and only one of the three conditions is True. Probably what you wanted in your example code.

if sum(1 for x in (a,b,c) if x) == 1:

回答 6

关于:(唯一条件)

if (bool(a) + bool(b) + bool(c) == 1):

注意,如果您同时允许两个条件,也可以这样做

if (bool(a) + bool(b) + bool(c) in [1,2]):

What about: (unique condition)

if (bool(a) + bool(b) + bool(c) == 1):

Notice, if you allow two conditions too you could do that

if (bool(a) + bool(b) + bool(c) in [1,2]):

回答 7

需要明确的是,您要基于多少个参数为逻辑TRUE(在使用字符串参数的情况下-不为空)做出决定?

argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0)

然后您做出了决定:

if ( 0 < argsne < 3 ):
 doSth() 

现在的逻辑更加清晰了。

To be clear, you want to made your decision based on how much of the parameters are logical TRUE (in case of string arguments – not empty)?

argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0)

Then you made a decision:

if ( 0 < argsne < 3 ):
 doSth() 

Now the logic is more clear.


回答 8

为何不仅仅计算它们呢?

import sys
a = sys.argv
if len(a) = 1 :  
    # No arguments were given, the program name count as one
elif len(a) = 4 :
    # Three arguments were given
else :
    # another amount of arguments was given

And why not just count them ?

import sys
a = sys.argv
if len(a) = 1 :  
    # No arguments were given, the program name count as one
elif len(a) = 4 :
    # Three arguments were given
else :
    # another amount of arguments was given

回答 9

如果您不介意有点晦涩难懂,则可以简单地滚动一下0 < (a + b + c) < 3true如果您有一个和两个真实的陈述,则返回,如果全部为假或没有为假,则返回false。

这也简化了您是否使用函数来评估布尔值,因为您只评估了一次变量,这意味着您可以内联编写函数,而无需临时存储变量。(例如:0 < ( a(x) + b(x) + c(x) ) < 3。)

If you don’t mind being a bit cryptic you can simly roll with 0 < (a + b + c) < 3 which will return true if you have between one and two true statements and false if all are false or none is false.

This also simplifies if you use functions to evaluate the bools as you only evaluate the variables once and which means you can write the functions inline and do not need to temporarily store the variables. (Example: 0 < ( a(x) + b(x) + c(x) ) < 3.)


回答 10

问题表明您需要全部三个参数(a和b以及c),或者都不需要(而不是(a或b或c))

这给出:

(a and b and c)否(a或b或c)

The question states that you need either all three arguments (a and b and c) or none of them (not (a or b or c))

This gives:

(a and b and c) or not (a or b or c)


回答 11

据我了解,您有一个接收3个参数的函数,但是如果不这样做,它将以默认行为运行。由于您尚未说明提供1或2个参数时应该发生的情况,因此我假设它应该只是执行默认行为。在这种情况下,我认为您会发现以下答案非常有利:

def method(a=None, b=None, c=None):
    if all([a, b, c]):
        # received 3 arguments
    else:
        # default behavior

但是,如果要对1或2个参数进行不同的处理:

def method(a=None, b=None, c=None):
    args = [a, b, c]
    if all(args):
        # received 3 arguments
    elif not any(args):
        # default behavior
    else:
        # some args (raise exception?)

注意:假设“ False”值不会传递到此方法中。

As I understand it, you have a function that receives 3 arguments, but if it does not it will run on default behavior. Since you have not explained what should happen when 1 or 2 arguments are supplied I will assume it should simply do the default behavior. In which case, I think you will find the following answer very advantageous:

def method(a=None, b=None, c=None):
    if all([a, b, c]):
        # received 3 arguments
    else:
        # default behavior

However, if you want 1 or 2 arguments to be handled differently:

def method(a=None, b=None, c=None):
    args = [a, b, c]
    if all(args):
        # received 3 arguments
    elif not any(args):
        # default behavior
    else:
        # some args (raise exception?)

note: This assumes that “False” values will not be passed into this method.


回答 12

如果使用条件迭代器,则访问速度可能很慢。但是,您不需要一次访问每个元素,也不总是需要读取所有元素。这是可与无限生成器一起使用的解决方案:

#!/usr/bin/env python3
from random import randint
from itertools import tee

def generate_random():
    while True:
        yield bool(randint(0,1))

def any_but_not_all2(s): # elegant
    t1, t2 = tee(s)
    return False in t1 and True in t2 # could also use "not all(...) and any(...)"

def any_but_not_all(s): # simple
    hadFalse = False
    hadTrue = False
    for i in s:
        if i:
            hadTrue = True
        else:
            hadFalse = True
        if hadTrue and hadFalse:
            return True
    return False


r1, r2 = tee(generate_random())
assert any_but_not_all(r1)
assert any_but_not_all2(r2)

assert not any_but_not_all([True, True])
assert not any_but_not_all2([True, True])

assert not any_but_not_all([])
assert not any_but_not_all2([])

assert any_but_not_all([True, False])
assert any_but_not_all2([True, False])

If you work with an iterator of conditions, it could be slow to access. But you don’t need to access each element more than once, and you don’t always need to read all of it. Here’s a solution that will work with infinite generators:

#!/usr/bin/env python3
from random import randint
from itertools import tee

def generate_random():
    while True:
        yield bool(randint(0,1))

def any_but_not_all2(s): # elegant
    t1, t2 = tee(s)
    return False in t1 and True in t2 # could also use "not all(...) and any(...)"

def any_but_not_all(s): # simple
    hadFalse = False
    hadTrue = False
    for i in s:
        if i:
            hadTrue = True
        else:
            hadFalse = True
        if hadTrue and hadFalse:
            return True
    return False


r1, r2 = tee(generate_random())
assert any_but_not_all(r1)
assert any_but_not_all2(r2)

assert not any_but_not_all([True, True])
assert not any_but_not_all2([True, True])

assert not any_but_not_all([])
assert not any_but_not_all2([])

assert any_but_not_all([True, False])
assert any_but_not_all2([True, False])

回答 13

当每一个给定boolTrue,或者当每一个给定boolFalse……
他们都是彼此相等!

所以,我们只需要找到两个元素的计算结果为不同的这bool小号
知道至少有一个True和至少一个False

我的简短解决方案:

not bool(a)==bool(b)==bool(c)

我相信它会短路,导致AFAIK a==b==c等于a==b and b==c

我的广义解决方案:

def _any_but_not_all(first, iterable): #doing dirty work
    bool_first=bool(first)
    for x in iterable:
        if bool(x) is not bool_first:
            return True
    return False

def any_but_not_all(arg, *args): #takes any amount of args convertable to bool
    return _any_but_not_all(arg, args)

def v_any_but_not_all(iterable): #takes iterable or iterator
    iterator=iter(iterable)
    return _any_but_not_all(next(iterator), iterator)

我也写了一些处理多个可迭代对象的代码,但是我从这里删除了它,因为我认为这没有意义。但是,这里仍然可用。

When every given bool is True, or when every given bool is False
they all are equal to each other!

So, we just need to find two elements which evaluates to different bools
to know that there is at least one True and at least one False.

My short solution:

not bool(a)==bool(b)==bool(c)

I belive it short-circuits, cause AFAIK a==b==c equals a==b and b==c.

My generalized solution:

def _any_but_not_all(first, iterable): #doing dirty work
    bool_first=bool(first)
    for x in iterable:
        if bool(x) is not bool_first:
            return True
    return False

def any_but_not_all(arg, *args): #takes any amount of args convertable to bool
    return _any_but_not_all(arg, args)

def v_any_but_not_all(iterable): #takes iterable or iterator
    iterator=iter(iterable)
    return _any_but_not_all(next(iterator), iterator)

I wrote also some code dealing with multiple iterables, but I deleted it from here because I think it’s pointless. It’s however still available here.


回答 14

基本上,这是“一些(但不是全部)”功能(与any()all()内置功能相比)。

这意味着结果之间应该有Falses True s。因此,您可以执行以下操作:

some = lambda ii: frozenset(bool(i) for i in ii).issuperset((True, False))

# one way to test this is...
test = lambda iterable: (any(iterable) and (not all(iterable))) # see also http://stackoverflow.com/a/16522290/541412

# Some test cases...
assert(some(()) == False)       # all() is true, and any() is false
assert(some((False,)) == False) # any() is false
assert(some((True,)) == False)  # any() and all() are true

assert(some((False,False)) == False)
assert(some((True,True)) == False)
assert(some((True,False)) == True)
assert(some((False,True)) == True)

此代码的优点之一是,您只需要对结果(布尔值)项目进行一次迭代。

缺点之一是,所有这些真值表达式总是被求值,并且不像/ 运算符那样发生短路orand

This is basically a “some (but not all)” functionality (when contrasted with the any() and all() builtin functions).

This implies that there should be Falses and Trues among the results. Therefore, you can do the following:

some = lambda ii: frozenset(bool(i) for i in ii).issuperset((True, False))

# one way to test this is...
test = lambda iterable: (any(iterable) and (not all(iterable))) # see also http://stackoverflow.com/a/16522290/541412

# Some test cases...
assert(some(()) == False)       # all() is true, and any() is false
assert(some((False,)) == False) # any() is false
assert(some((True,)) == False)  # any() and all() are true

assert(some((False,False)) == False)
assert(some((True,True)) == False)
assert(some((True,False)) == True)
assert(some((False,True)) == True)

One advantage of this code is that you only need to iterate once through the resulting (booleans) items.

One disadvantage is that all these truth-expressions are always evaluated, and do not do short-circuiting like the or/and operators.


什么是无值?

问题:什么是无值?

我一直在研究Python,并且阅读了一章描述了它的None价值,但不幸的是,这本书在某些方面并不十分清楚。我认为,如果我在那分享我的问题,我会找到答案。

我想知道None价值什么,您将其用于什么?

而且,我没有得到本书的这一部分:

将值None赋给变量是将其重置为其原始的空状态的一种方法。

那是什么意思?

答案很棒,尽管由于我对计算机世界的了解不足(我还没有了解类,对象等),所以我对大多数答案都不了解。这句话是什么意思?

将值None赋给变量是将其重置为其原始的空状态的一种方法。

最后:

最后,我从寻找不同的答案中得到了答案。我必须感谢所有抽出宝贵时间来帮助我的人(尤其是Martijn Pieters和DSM),我希望我可以选择所有答案作为最佳答案,但是选择仅限于一个。所有的答案都很好。

I have been studying Python, and I read a chapter which describes the None value, but unfortunately this book isn’t very clear at some points. I thought that I would find the answer to my question, if I share it there.

I want to know what the None value is and what do you use it for?

And also, I don’t get this part of the book:

Assigning a value of None to a variable is one way to reset it to its original, empty state.

What does that mean?

The answers were great, although I didn’t understand most of answers due to my low knowledge of the computer world (I haven’t learned about classes, objects, etc.). What does this sentence mean?

Assigning a value of None to a variable is one way to reset it to its original, empty state.

Final:

Finally I’ve got my answer from looking to different answers. I must appreciate all the people who put their times to help me (especially Martijn Pieters and DSM), and I wish that I could choose all answers as the best, but the selection is limited to one. All of the answers were great.


回答 0

Martijn的答案解释了NonePython中的内容,并正确指出该书具有误导性。由于Python程序员通常不会说

将值None赋给变量是将其重置为其原始的空状态的一种方法。

很难以一种有意义的方式来解释布里格斯的意思,并解释为什么这里没有人对此感到满意。一个类推可能会有所帮助:

在Python中,变量名称就像贴在对象上的标签。每个标签上都有一个唯一的名称,并且一次只能在一个对象上,但是如果需要,您可以在同一对象上放置多个标签。当你写

F = "fork"

您将标签“ F”放在字符串对象上"fork"。如果你再写

F = None

您将标签移动到None对象。

Briggs想让您想象的是,您没有贴纸"F",但已经F贴纸上了None,您所做的就是其从None移到"fork"。因此F = None,如果我们决定将其None视为,则在键入时,您会将其“重置为原始的空状态” empty state

我可以看到他的意思,但这是一种不好的观察方式。如果启动Python并输入print(F),则会看到

>>> print(F)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'F' is not defined

NameError意味着Python无法识别这个名字F因为没有这样的标签。如果Briggs是正确的并且F = None重置F为原始状态,那么它应该现在在那里,并且我们应该看到

>>> print(F)
None

就像我们在键入F = None并贴上贴纸后所做的一样None


这就是所有的事情。实际上,Python附带了一些已经粘贴到对象上的标签(内置名称),但是其他一些标签则需要使用诸如F = "fork"and A = 2和这样的行来编写c17 = 3.14,然后再将其粘贴在其他对象上(例如F = 10or F = None;它们都是一样的) )

Briggs假装您可能要写的所有可能的贴纸均已粘贴到该None对象上。

Martijn’s answer explains what None is in Python, and correctly states that the book is misleading. Since Python programmers as a rule would never say

Assigning a value of None to a variable is one way to reset it to its original, empty state.

it’s hard to explain what Briggs means in a way which makes sense and explains why no one here seems happy with it. One analogy which may help:

In Python, variable names are like stickers put on objects. Every sticker has a unique name written on it, and it can only be on one object at a time, but you could put more than one sticker on the same object, if you wanted to. When you write

F = "fork"

you put the sticker “F” on a string object "fork". If you then write

F = None

you move the sticker to the None object.

What Briggs is asking you to imagine is that you didn’t write the sticker "F", there was already an F sticker on the None, and all you did was move it, from None to "fork". So when you type F = None, you’re “reset[ting] it to its original, empty state”, if we decided to treat None as meaning empty state.

I can see what he’s getting at, but that’s a bad way to look at it. If you start Python and type print(F), you see

>>> print(F)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'F' is not defined

and that NameError means Python doesn’t recognize the name F, because there is no such sticker. If Briggs were right and F = None resets F to its original state, then it should be there now, and we should see

>>> print(F)
None

like we do after we type F = None and put the sticker on None.


So that’s all that’s going on. In reality, Python comes with some stickers already attached to objects (built-in names), but others you have to write yourself with lines like F = "fork" and A = 2 and c17 = 3.14, and then you can stick them on other objects later (like F = 10 or F = None; it’s all the same.)

Briggs is pretending that all possible stickers you might want to write were already stuck to the None object.


回答 1

None只是通常用于表示“空”或“此处无值”的值。它是一个信号对象 ; 它仅具有含义,因为Python文档说明了它的含义。

在给定的Python解释器会话中,该对象只有一个副本。

例如,如果您编写一个函数,但该函数不使用显式return语句,None则返回该函数。这样,使用函数进行编程就大大简化了;一个函数总是返回某些东西,即使它只是那个None对象。

您可以明确地对其进行测试:

if foo is None:
    # foo is set to None

if bar is not None:
    # bar is set to something *other* than None

另一个用途是为函数提供可选参数,默认为“空”:

def spam(foo=None):
    if foo is not None:
        # foo was specified, do something clever!

该函数spam()有一个可选参数。如果在spam()未指定的情况下进行调用,则会为其指定默认值None,从而易于检测是否使用参数调用了该函数。

其他语言也有类似的概念。SQL有NULL; JavaScript具有undefined null,等等。

请注意,在Python中,变量通过使用而存在。您无需先声明变量,因此Python 中没有真正的变量。那么,将变量设置None为与将其设置为默认的空值是不同的。None也是一个值,尽管该值通常用于表示空白。您正在阅读的书在这一点上具有误导性。

None is just a value that commonly is used to signify ’empty’, or ‘no value here’. It is a signal object; it only has meaning because the Python documentation says it has that meaning.

There is only one copy of that object in a given Python interpreter session.

If you write a function, and that function doesn’t use an explicit return statement, None is returned instead, for example. That way, programming with functions is much simplified; a function always returns something, even if it is only that one None object.

You can test for it explicitly:

if foo is None:
    # foo is set to None

if bar is not None:
    # bar is set to something *other* than None

Another use is to give optional parameters to functions an ’empty’ default:

def spam(foo=None):
    if foo is not None:
        # foo was specified, do something clever!

The function spam() has a optional argument; if you call spam() without specifying it, the default value None is given to it, making it easy to detect if the function was called with an argument or not.

Other languages have similar concepts. SQL has NULL; JavaScript has undefined and null, etc.

Note that in Python, variables exist by virtue of being used. You don’t need to declare a variable first, so there are no really empty variables in Python. Setting a variable to None is then not the same thing as setting it to a default empty value; None is a value too, albeit one that is often used to signal emptyness. The book you are reading is misleading on that point.


回答 2

这就是Python文档必须说的None

types.NoneType的唯一值。当没有将默认参数传递给函数时,通常不使用None来表示缺少值。

在版本2.4中更改:分配为None是非法的,并引发SyntaxError。

注意不能重新分配名称None和debug(分配给它们,即使作为属性名称,也会引发SyntaxError),因此可以将它们视为“ true”常量。

  1. 让我们确认None第一个的类型

    print type(None)
    print None.__class__
    

    输出量

    <type 'NoneType'>
    <type 'NoneType'>
    

基本上,NoneType数据类型与intfloat等类似。您可以在8.15中查看Python中可用的默认类型列表types —内置类型的名称

  1. 并且,NoneNoneType类的实例。因此,我们可能要创建None自己的实例。让我们尝试一下

    print types.IntType()
    print types.NoneType()
    

    输出量

    0
    TypeError: cannot create 'NoneType' instances
    

很明显,无法创建NoneType实例。我们不必担心价值的独特性None

  1. 让我们检查一下我们是如何None内部实现的。

    print dir(None)

    输出量

    ['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', 
     '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__',
     '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
    

除了__setattr__,所有其他均为只读属性。因此,我们无法更改的属性None

  1. 让我们尝试为添加新属性 None

    setattr(types.NoneType, 'somefield', 'somevalue')
    setattr(None, 'somefield', 'somevalue')
    None.somefield = 'somevalue'
    

    输出量

    TypeError: can't set attributes of built-in/extension type 'NoneType'
    AttributeError: 'NoneType' object has no attribute 'somefield'
    AttributeError: 'NoneType' object has no attribute 'somefield'
    

上面看到的语句分别产生这些错误消息。这意味着,我们不能在None实例上动态创建属性。

  1. 让我们检查一下分配东西时会发生什么None。根据文档,它应该抛出SyntaxError。这意味着,如果我们向分配了某些内容None,则该程序将根本不会执行。

    None = 1

    输出量

    SyntaxError: cannot assign to None

我们已经确定

  1. None 是…的实例 NoneType
  2. None 不能有新属性
  3. 的现有属性None无法更改。
  4. 我们无法创建的其他实例 NoneType
  5. 我们甚至不能通过None给它分配值来更改对它的引用。

因此,如文档中所述,None实际上可以将其视为true constant

很高兴知道None:)

This is what the Python documentation has got to say about None:

The sole value of types.NoneType. None is frequently used to represent the absence of a value, as when default arguments are not passed to a function.

Changed in version 2.4: Assignments to None are illegal and raise a SyntaxError.

Note The names None and debug cannot be reassigned (assignments to them, even as an attribute name, raise SyntaxError), so they can be considered “true” constants.

  1. Let’s confirm the type of None first

    print type(None)
    print None.__class__
    

    Output

    <type 'NoneType'>
    <type 'NoneType'>
    

Basically, NoneType is a data type just like int, float, etc. You can check out the list of default types available in Python in 8.15. types — Names for built-in types.

  1. And, None is an instance of NoneType class. So we might want to create instances of None ourselves. Let’s try that

    print types.IntType()
    print types.NoneType()
    

    Output

    0
    TypeError: cannot create 'NoneType' instances
    

So clearly, cannot create NoneType instances. We don’t have to worry about the uniqueness of the value None.

  1. Let’s check how we have implemented None internally.

    print dir(None)
    

    Output

    ['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', 
     '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__',
     '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
    

Except __setattr__, all others are read-only attributes. So, there is no way we can alter the attributes of None.

  1. Let’s try and add new attributes to None

    setattr(types.NoneType, 'somefield', 'somevalue')
    setattr(None, 'somefield', 'somevalue')
    None.somefield = 'somevalue'
    

    Output

    TypeError: can't set attributes of built-in/extension type 'NoneType'
    AttributeError: 'NoneType' object has no attribute 'somefield'
    AttributeError: 'NoneType' object has no attribute 'somefield'
    

The above seen statements produce these error messages, respectively. It means that, we cannot create attributes dynamically on a None instance.

  1. Let us check what happens when we assign something None. As per the documentation, it should throw a SyntaxError. It means, if we assign something to None, the program will not be executed at all.

    None = 1
    

    Output

    SyntaxError: cannot assign to None
    

We have established that

  1. None is an instance of NoneType
  2. None cannot have new attributes
  3. Existing attributes of None cannot be changed.
  4. We cannot create other instances of NoneType
  5. We cannot even change the reference to None by assigning values to it.

So, as mentioned in the documentation, None can really be considered as a true constant.

Happy knowing None :)


回答 3

您所指的书显然是试图大大简化的含义None。Python的变量不具备初始,空状态- Python的变量绑定(只),他们定义的时候。如果不给它一个值,就不能创建一个Python变量。

>>> print(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> def test(x):
...   print(x)
... 
>>> test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: test() takes exactly 1 argument (0 given)
>>> def test():
...   print(x)
... 
>>> test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in test
NameError: global name 'x' is not defined

但是有时候您想让一个函数根据变量是否定义而具有不同的含义。您可以创建默认值为的参数None

>>> def test(x=None):
...   if x is None:
...     print('no x here')
...   else:
...     print(x)
... 
>>> test()
no x here
>>> test('x!')
x!

None在这种情况下,此值是特殊值并不十分重要。我可以使用任何默认值:

>>> def test(x=-1):
...   if x == -1:
...     print('no x here')
...   else:
...     print(x)
... 
>>> test()
no x here
>>> test('x!')
x!

…但是None到处都有给我们带来两个好处:

  1. 我们不必选择-1含义不明确的特殊值,并且
  2. 实际上,我们的函数可能需要-1作为普通输入处理。
>>> test(-1)
no x here

哎呀!

因此,这本书在使用“ 重设 ”一词时通常会产生一些误导–分配None名称是向程序员发出信号,表明该值未在使用中,或者该函数应以某种默认方式运行,但需要重设一个值要恢复其原始的未定义状态,您必须使用del关键字:

>>> x = 3
>>> x
3
>>> del x
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

The book you refer to is clearly trying to greatly simplify the meaning of None. Python variables don’t have an initial, empty state – Python variables are bound (only) when they’re defined. You can’t create a Python variable without giving it a value.

>>> print(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> def test(x):
...   print(x)
... 
>>> test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: test() takes exactly 1 argument (0 given)
>>> def test():
...   print(x)
... 
>>> test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in test
NameError: global name 'x' is not defined

but sometimes you want to make a function mean different things depending on whether a variable is defined or not. You can create an argument with a default value of None:

>>> def test(x=None):
...   if x is None:
...     print('no x here')
...   else:
...     print(x)
... 
>>> test()
no x here
>>> test('x!')
x!

The fact that this value is the special None value is not terribly important in this case. I could’ve used any default value:

>>> def test(x=-1):
...   if x == -1:
...     print('no x here')
...   else:
...     print(x)
... 
>>> test()
no x here
>>> test('x!')
x!

…but having None around gives us two benefits:

  1. We don’t have to pick a special value like -1 whose meaning is unclear, and
  2. Our function may actually need to handle -1 as a normal input.
>>> test(-1)
no x here

oops!

So the book is a little misleading mostly in its use of the word reset – assigning None to a name is a signal to a programmer that that value isn’t being used or that the function should behave in some default way, but to reset a value to its original, undefined state you must use the del keyword:

>>> x = 3
>>> x
3
>>> del x
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

回答 4

其他答案已经很好地解释了None的含义。但是,我仍然想通过一个例子对此进行更多说明。

例:

def extendList(val, list=[]):
    list.append(val)
    return list

list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')

print "list1 = %s" % list1
print "list2 = %s" % list2
print "list3 = %s" % list3

现在尝试猜测上面列表的输出。好吧,答案令人惊讶地如下:

list1 = [10, 'a']
list2 = [123]
list3 = [10, 'a']

但为什么?

许多人会错误地期望list1等于[10]list3等于[‘a’],以为每次调用extendList时,list参数将被设置为其默认值[]

但是,实际发生的情况是,在定义函数时,仅会创建一次新的默认列表,然后在每次调用extendList且未指定list参数的情况下都使用同一列表。这是因为默认参数中的表达式是在定义函数时计算的,而不是在调用函数时计算的

因此,list1list3在同一默认列表上运行,而list2在它创建的单独列表上运行(通过传递其自己的空列表作为list参数的值)。


“无”救星:(修改上面的示例以产生所需的行为)

def extendList(val, list=None):
    if list is None:
       list = []
    list.append(val)
    return list

list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')

print "list1 = %s" % list1
print "list2 = %s" % list2
print "list3 = %s" % list3

使用此修订的实现,输出将是:

list1 = [10]
list2 = [123]
list3 = ['a']

注意 -贷记至toptal.com的示例

Other answers have already explained meaning of None beautifully. However, I would still like to throw more light on this using an example.

Example:

def extendList(val, list=[]):
    list.append(val)
    return list

list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')

print "list1 = %s" % list1
print "list2 = %s" % list2
print "list3 = %s" % list3

Now try to guess output of above list. Well, the answer is surprisingly as below:

list1 = [10, 'a']
list2 = [123]
list3 = [10, 'a']

But Why?

Many will mistakenly expect list1 to be equal to [10] and list3 to be equal to [‘a’], thinking that the list argument will be set to its default value of [] each time extendList is called.

However, what actually happens is that the new default list is created only once when the function is defined, and that same list is then used subsequently whenever extendList is invoked without a list argument being specified. This is because expressions in default arguments are calculated when the function is defined, not when it’s called.

list1 and list3 are therefore operating on the same default list, whereas list2 is operating on a separate list that it created (by passing its own empty list as the value for the list parameter).


‘None’ the savior: (Modify example above to produce desired behavior)

def extendList(val, list=None):
    if list is None:
       list = []
    list.append(val)
    return list

list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')

print "list1 = %s" % list1
print "list2 = %s" % list2
print "list3 = %s" % list3

With this revised implementation, the output would be:

list1 = [10]
list2 = [123]
list3 = ['a']

Note – Example credit to toptal.com


回答 5

None是一个单例对象(意味着只有一个None),在语言和库中的许多地方都用于表示缺少其他值。


例如:
if d是一个字典,如果存在d.get(k)则返回d[k],但None如果d没有key 则返回k

从一个很棒的博客中阅读以下信息:http : //python-history.blogspot.in/

None is a singleton object (meaning there is only one None), used in many places in the language and library to represent the absence of some other value.


For example:
if d is a dictionary, d.get(k) will return d[k] if it exists, but None if d has no key k.

Read this info from a great blog: http://python-history.blogspot.in/


回答 6

所有这些都是很好的答案,但是我认为还有更多的原因可以解释None

想象一下,您在婚礼上收集了RSVP。您想记录每个人是否参加。如果他们正在参加,请设置person.attending = True。如果他们不参加,您设置person.attending = False。如果尚未收到任何RSVP,则person.attending = None。这样,您可以区分无信息None和否定答案。

All of these are good answers but I think there’s more to explain why None is useful.

Imagine you collecting RSVPs to a wedding. You want to record whether each person will attend. If they are attending, you set person.attending = True. If they are not attending you set person.attending = False. If you have not received any RSVP, then person.attending = None. That way you can distinguish between no information – None – and a negative answer.


回答 7

我喜欢代码示例(以及水果),所以让我告诉你

apple = "apple"
print(apple)
>>> apple
apple = None
print(apple)
>>> None

没有意味着什么,没有价值。

没有一个计算结果为False。

I love code examples (as well as fruit), so let me show you

apple = "apple"
print(apple)
>>> apple
apple = None
print(apple)
>>> None

None means nothing, it has no value.

None evaluates to False.


回答 8

largest=none
smallest =none 
While True :
          num =raw_input ('enter a number ') 
          if num =="done ": break 
          try :
           inp =int (inp) 
          except:
              Print'Invalid input' 
           if largest is none :
               largest=inp
           elif inp>largest:
                largest =none 
           print 'maximum', largest

          if smallest is none:
               smallest =none
          elif inp<smallest :
               smallest =inp
          print 'minimum', smallest 

print 'maximum, minimum, largest, smallest 
largest=none
smallest =none 
While True :
          num =raw_input ('enter a number ') 
          if num =="done ": break 
          try :
           inp =int (inp) 
          except:
              Print'Invalid input' 
           if largest is none :
               largest=inp
           elif inp>largest:
                largest =none 
           print 'maximum', largest

          if smallest is none:
               smallest =none
          elif inp<smallest :
               smallest =inp
          print 'minimum', smallest 

print 'maximum, minimum, largest, smallest 

检查环境变量是否存在的良好实践是什么?

问题:检查环境变量是否存在的良好实践是什么?

我想检查我的环境中是否存在"FOO"Python 中的变量。为此,我正在使用os标准库。阅读图书馆的文档后,我想出了两种实现目标的方法:

方法1:

if "FOO" in os.environ:
    pass

方法2:

if os.getenv("FOO") is not None:
    pass

我想知道哪种方法是好的/首选条件,以及为什么。

I want to check my environment for the existence of a variable, say "FOO", in Python. For this purpose, I am using the os standard library. After reading the library’s documentation, I have figured out 2 ways to achieve my goal:

Method 1:

if "FOO" in os.environ:
    pass

Method 2:

if os.getenv("FOO") is not None:
    pass

I would like to know which method, if either, is a good/preferred conditional and why.


回答 0

使用第一个;它直接尝试检查是否在中定义了某些内容environ。尽管第二种形式同样可以很好地工作,但是它在语义上是不足的,因为如果存在,您会得到一个返回的值,并且将其用于比较。

你想看看是否有存在 environ,为什么你会得到只是为了进行比较,然后折腾它扔掉

那正是这样getenv做的:

获取一个环境变量None如果不存在则返回。可选的第二个参数可以指定备用默认值。

(这也意味着您的支票可能只是if getenv("FOO")

你不想得到它,你想检查它的存在。

无论哪种方式,getenv都只是一个包装,environ.get但是您看不到有人通过以下方式检查映射中的成员身份:

from os import environ
if environ.get('Foo') is not None:

总结一下,使用:

if "FOO" in os.environ:
    pass

如果您只想检查是否存在,请使用,getenv("FOO")如果您确实想用可能获得的价值做某事。

Use the first; it directly tries to check if something is defined in environ. Though the second form works equally well, it’s lacking semantically since you get a value back if it exists and only use it for a comparison.

You’re trying to see if something is present in environ, why would you get just to compare it and then toss it away?

That’s exactly what getenv does:

Get an environment variable, return None if it doesn’t exist. The optional second argument can specify an alternate default.

(this also means your check could just be if getenv("FOO"))

you don’t want to get it, you want to check for it’s existence.

Either way, getenv is just a wrapper around environ.get but you don’t see people checking for membership in mappings with:

from os import environ
if environ.get('Foo') is not None:

To summarize, use:

if "FOO" in os.environ:
    pass

if you just want to check for existence, while, use getenv("FOO") if you actually want to do something with the value you might get.


回答 1

两种解决方案都有一种情况,这取决于您要根据环境变量的存在来执行什么操作。

情况1

如果您想纯粹基于环境变量的存在而采取不同的措施而又不关心其价值,那么第一个解决方案就是最佳实践。它简要描述了您要测试的内容:环境变量列表中的’FOO’。

if 'KITTEN_ALLERGY' in os.environ:
    buy_puppy()
else:
    buy_kitten()

情况二

如果您想在环境变量中未定义该值的情况下设置默认值,则第二个解决方案实际上很有用,尽管它不是您编写的形式:

server = os.getenv('MY_CAT_STREAMS', 'youtube.com')

也许

server = os.environ.get('MY_CAT_STREAMS', 'youtube.com')

请注意,如果您的应用程序有多个选项,则可能需要查看ChainMap,它允许根据键合并多个字典。ChainMap文档中有一个示例:

[...]
combined = ChainMap(command_line_args, os.environ, defaults)

There is a case for either solution, depending on what you want to do conditional on the existence of the environment variable.

Case 1

When you want to take different actions purely based on the existence of the environment variable, without caring for its value, the first solution is the best practice. It succinctly describes what you test for: is ‘FOO’ in the list of environment variables.

if 'KITTEN_ALLERGY' in os.environ:
    buy_puppy()
else:
    buy_kitten()

Case 2

When you want to set a default value if the value is not defined in the environment variables the second solution is actually useful, though not in the form you wrote it:

server = os.getenv('MY_CAT_STREAMS', 'youtube.com')

or perhaps

server = os.environ.get('MY_CAT_STREAMS', 'youtube.com')

Note that if you have several options for your application you might want to look into ChainMap, which allows to merge multiple dicts based on keys. There is an example of this in the ChainMap documentation:

[...]
combined = ChainMap(command_line_args, os.environ, defaults)

回答 2

为了安全起见

os.getenv('FOO') or 'bar'

上述答案的一个极端情况是设置了环境变量但为空

对于这种特殊情况,您会得到

print(os.getenv('FOO', 'bar'))
# prints new line - though you expected `bar`

要么

if "FOO" in os.environ:
    print("FOO is here")
# prints FOO is here - however its not

为了避免这种情况,只需使用 or

os.getenv('FOO') or 'bar'

然后你得到

print(os.getenv('FOO') or 'bar')
# bar

什么时候有空的环境变量?

您忘记在.env文件中设置值

# .env
FOO=

或导出为

$ export FOO=

或忘记设置它 settings.py

# settings.py
os.environ['FOO'] = ''

更新:如果有疑问,请查看这些单线

>>> import os; os.environ['FOO'] = ''; print(os.getenv('FOO', 'bar'))

$ FOO= python -c "import os; print(os.getenv('FOO', 'bar'))"

To be on the safe side use

os.getenv('FOO') or 'bar'

A corner case with the above answers is when the environment variable is set but is empty

For this special case you get

print(os.getenv('FOO', 'bar'))
# prints new line - though you expected `bar`

or

if "FOO" in os.environ:
    print("FOO is here")
# prints FOO is here - however its not

To avoid this just use or

os.getenv('FOO') or 'bar'

Then you get

print(os.getenv('FOO') or 'bar')
# bar

When do we have empty environment variables?

You forgot to set the value in the .env file

# .env
FOO=

or exported as

$ export FOO=

or forgot to set it in settings.py

# settings.py
os.environ['FOO'] = ''

Update: if in doubt, check out these one-liners

>>> import os; os.environ['FOO'] = ''; print(os.getenv('FOO', 'bar'))

$ FOO= python -c "import os; print(os.getenv('FOO', 'bar'))"

回答 3

如果您要检查是否未设置多个环境变量,可以执行以下操作:

import os

MANDATORY_ENV_VARS = ["FOO", "BAR"]

for var in MANDATORY_ENV_VARS:
    if var not in os.environ:
        raise EnvironmentError("Failed because {} is not set.".format(var))

In case you want to check if multiple env variables are not set, you can do the following:

import os

MANDATORY_ENV_VARS = ["FOO", "BAR"]

for var in MANDATORY_ENV_VARS:
    if var not in os.environ:
        raise EnvironmentError("Failed because {} is not set.".format(var))

回答 4

我的评论可能与给定的标签无关。但是,我是从搜索中转到此页面的。我一直在寻找R中的类似支票,并在@hugovdbeg帖子的帮助下提出了以下内容。我希望这对在R中寻求类似解决方案的人有所帮助

'USERNAME' %in% names(Sys.getenv())

My comment might not be relevant to the tags given. However, I was lead to this page from my search. I was looking for similar check in R and I came up the following with the help of @hugovdbeg post. I hope it would be helpful for someone who is looking for similar solution in R

'USERNAME' %in% names(Sys.getenv())

将if-elif-else语句放在一行上吗?

问题:将if-elif-else语句放在一行上吗?

我已阅读以下链接,但未解决我的问题。
Python是否具有三元条件运算符?(问题是将if-else语句压缩到一行)

有没有更简单的方式编写if-elif-else语句,使其适合一行?
例如,

if expression1:
   statement1
elif expression2:
   statement2
else:
   statement3

或一个真实的例子:

if i > 100:
    x = 2
elif i < 100:
    x = 1
else:
    x = 0

我只是觉得,如果上面的示例可以用以下方式编写,则看起来会更加简洁。

x=2 if i>100 elif i<100 1 else 0 [WRONG]

I have read the links below, but it doesn’t address my question.
Does Python have a ternary conditional operator? (the question is about condensing if-else statement to one line)

Is there an easier way of writing an if-elif-else statement so it fits on one line?
For example,

if expression1:
   statement1
elif expression2:
   statement2
else:
   statement3

Or a real-world example:

if i > 100:
    x = 2
elif i < 100:
    x = 1
else:
    x = 0

I just feel if the example above could be written the following way, it could look like more concise.

x=2 if i>100 elif i<100 1 else 0 [WRONG]

回答 0

不,这是不可能的(至少不能使用任意语句),也不是可取的。将所有内容都放在一行中很可能会违反PEP-8,在这种情况下,行的长度不得超过80个字符。

这也与Python的Zen背道而驰:“可读性很重要”。(import this在Python提示符下键入以读取整个内容)。

可以在Python中使用三元表达式,但只能用于表达式,不能用于语句:

>>> a = "Hello" if foo() else "Goodbye"

编辑:

现在,您修改后的问题表明,除了要分配的值之外,这三个语句是相同的。在这种情况下,链式三元运算符确实可以工作,但是我仍然认为它的可读性较差:

>>> i=100
>>> a = 1 if i<100 else 2 if i>100 else 0
>>> a
0
>>> i=101
>>> a = 1 if i<100 else 2 if i>100 else 0
>>> a
2
>>> i=99
>>> a = 1 if i<100 else 2 if i>100 else 0
>>> a
1

No, it’s not possible (at least not with arbitrary statements), nor is it desirable. Fitting everything on one line would most likely violate PEP-8 where it is mandated that lines should not exceed 80 characters in length.

It’s also against the Zen of Python: “Readability counts”. (Type import this at the Python prompt to read the whole thing).

You can use a ternary expression in Python, but only for expressions, not for statements:

>>> a = "Hello" if foo() else "Goodbye"

Edit:

Your revised question now shows that the three statements are identical except for the value being assigned. In that case, a chained ternary operator does work, but I still think that it’s less readable:

>>> i=100
>>> a = 1 if i<100 else 2 if i>100 else 0
>>> a
0
>>> i=101
>>> a = 1 if i<100 else 2 if i>100 else 0
>>> a
2
>>> i=99
>>> a = 1 if i<100 else 2 if i>100 else 0
>>> a
1

回答 1

如果您仅在不同情况下需要不同的表达式,那么这可能对您有用:

expr1 if condition1 else expr2 if condition2 else expr

例如:

a = "neg" if b<0 else "pos" if b>0 else "zero"

If you only need different expressions for different cases then this may work for you:

expr1 if condition1 else expr2 if condition2 else expr

For example:

a = "neg" if b<0 else "pos" if b>0 else "zero"

回答 2

只需在else语句中嵌套另一个if子句。但这并没有使它看起来更漂亮。

>>> x=5
>>> x if x>0 else ("zero" if x==0 else "invalid value")
5
>>> x = 0
>>> x if x>0 else ("zero" if x==0 else "invalid value")
'zero'
>>> x = -1
>>> x if x>0 else ("zero" if x==0 else "invalid value")
'invalid value'

Just nest another if clause in the else statement. But that doesn’t make it look any prettier.

>>> x=5
>>> x if x>0 else ("zero" if x==0 else "invalid value")
5
>>> x = 0
>>> x if x>0 else ("zero" if x==0 else "invalid value")
'zero'
>>> x = -1
>>> x if x>0 else ("zero" if x==0 else "invalid value")
'invalid value'

回答 3

尽管有其他一些答案:是的,这是可能的

if expression1:
   statement1
elif expression2:
   statement2
else:
   statement3

转换为以下一种衬纸:

statement1 if expression1 else (statement2 if expression2 else statement3)

实际上,您可以将它们嵌套到无限远。请享用 ;)

Despite some other answers: YES it IS possible:

if expression1:
   statement1
elif expression2:
   statement2
else:
   statement3

translates to the following one liner:

statement1 if expression1 else (statement2 if expression2 else statement3)

in fact you can nest those till infinity. Enjoy ;)


回答 4

您可以选择实际使用a的get方法dict

x = {i<100: -1, -10<=i<=10: 0, i>100: 1}.get(True, 2)

get如果其中一个键可以保证计算为,则不需要该方法True

x = {i<0: -1, i==0: 0, i>0: 1}[True]

理想情况下,最多不应将其中一个键评估为True。如果一个以上的键计算为True,则结果似乎不可预测。

You can optionally actually use the get method of a dict:

x = {i<100: -1, -10<=i<=10: 0, i>100: 1}.get(True, 2)

You don’t need the get method if one of the keys is guaranteed to evaluate to True:

x = {i<0: -1, i==0: 0, i>0: 1}[True]

At most one of the keys should ideally evaluate to True. If more than one key evaluates to True, the results could seem unpredictable.


回答 5

在我看来,还有一种方法是很难理解的,但无论如何我还是会出于好奇而分享:

x = (i>100 and 2) or (i<100 and 1) or 0

此处提供更多信息:https : //docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not

There’s an alternative that’s quite unreadable in my opinion but I’ll share anyway just as a curiosity:

x = (i>100 and 2) or (i<100 and 1) or 0

More info here: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not


回答 6

if i > 100:
    x = 2
elif i < 100:
    x = 1
else:
    x = 0

如果要在一行中使用上述代码,则可以使用以下代码:

x = 2 if i > 100 else 1 if i < 100 else 0

这样做时,如果i> 100,x将被分配为2;如果i <100,则x将被分配;如果i = 100,则x将被分配为0。

if i > 100:
    x = 2
elif i < 100:
    x = 1
else:
    x = 0

If you want to use the above-mentioned code in one line, you can use the following:

x = 2 if i > 100 else 1 if i < 100 else 0

On doing so, x will be assigned 2 if i > 100, 1 if i < 100 and 0 if i = 100


回答 7

这也取决于您的表情的性质。关于“不做”的其他答案的一般建议对于通用语句和通用表达式非常有效。

但是,如果您只需要一个“ dispatch”表(例如,根据给定选项的值调用一个不同的函数),则可以将这些函数放在字典中进行调用。

就像是:

def save(): 
   ...
def edit():
   ...
options = {"save": save, "edit": edit, "remove": lambda : "Not Implemented"}

option = get_input()
result = options[option]()

代替if-else:

if option=="save":
    save()
...

It also depends on the nature of your expressions. The general advice on the other answers of “not doing it” is quite valid for generic statements and generic expressions.

But if all you need is a “dispatch” table, like, calling a different function depending on the value of a given option, you can put the functions to call inside a dictionary.

Something like:

def save(): 
   ...
def edit():
   ...
options = {"save": save, "edit": edit, "remove": lambda : "Not Implemented"}

option = get_input()
result = options[option]()

Instead of an if-else:

if option=="save":
    save()
...

回答 8

人们已经提到了三元表达式。有时,以简单的条件分配为例,可以使用数学表达式执行条件分配。这可能不会使您的代码具有很高的可读性,但是确实可以将它放在很短的一行上。您的示例可以这样写:

x = 2*(i>100) | 1*(i<100)

比较将为True或False,然后与数字相乘将为1或0。可以使用+而不是| 在中间。

People have already mentioned ternary expressions. Sometimes with a simple conditional assignment as your example, it is possible to use a mathematical expression to perform the conditional assignment. This may not make your code very readable, but it does get it on one fairly short line. Your example could be written like this:

x = 2*(i>100) | 1*(i<100)

The comparisons would be True or False, and when multiplying with numbers would then be either 1 or 0. One could use a + instead of an | in the middle.


回答 9

三元运算符是一个简洁的表达的最好方式。语法为variable = value_1 if condition else value_2。因此,对于您的示例,您必须两次应用三元运算符:

i = 23 # set any value for i
x = 2 if i > 100 else 1 if i < 100 else 0

The ternary operator is the best way to a concise expression. The syntax is variable = value_1 if condition else value_2. So, for your example, you must apply the ternary operator twice:

i = 23 # set any value for i
x = 2 if i > 100 else 1 if i < 100 else 0

回答 10

您可以使用嵌套三元if语句。

# if-else ternary construct
country_code = 'USA'
is_USA = True if country_code == 'USA' else False
print('is_USA:', is_USA)

# if-elif-else ternary construct
# Create function to avoid repeating code.
def get_age_category_name(age):
    age_category_name = 'Young' if age <= 40 else ('Middle Aged' if age > 40 and age <= 65 else 'Senior')
    return age_category_name

print(get_age_category_name(25))
print(get_age_category_name(50))
print(get_age_category_name(75))

You can use nested ternary if statements.

# if-else ternary construct
country_code = 'USA'
is_USA = True if country_code == 'USA' else False
print('is_USA:', is_USA)

# if-elif-else ternary construct
# Create function to avoid repeating code.
def get_age_category_name(age):
    age_category_name = 'Young' if age <= 40 else ('Middle Aged' if age > 40 and age <= 65 else 'Senior')
    return age_category_name

print(get_age_category_name(25))
print(get_age_category_name(50))
print(get_age_category_name(75))

Python中的简单“ if”或逻辑语句

问题:Python中的简单“ if”或逻辑语句

您将如何在Python中编写以下内容?

if key < 1 or key > 34:

我已经尝试了所有可以想到的方法,并且发现它非常令人沮丧。

How would you write the following in Python?

if key < 1 or key > 34:

I’ve tried every way I can think of and am finding it very frustrating.


回答 0

如果key不是一个intfloat一个string,则需要int通过执行以下操作将其转换为第一个

key = int(key)

float做某事

key = float(key)

否则,您所遇到的问题应该可以解决,但是

if (key < 1) or (key > 34):

要么

if not (1 <= key <= 34):

会更清晰一些。

If key isn’t an int or float but a string, you need to convert it to an int first by doing

key = int(key)

or to a float by doing

key = float(key)

Otherwise, what you have in your question should work, but

if (key < 1) or (key > 34):

or

if not (1 <= key <= 34):

would be a bit clearer.


回答 1

这是一个布尔值:

if (not suffix == "flac" )  or (not suffix == "cue" ):   # WRONG! FAILS
    print  filename + ' is not a flac or cue file'

if not (suffix == "flac"  or suffix == "cue" ):     # CORRECT!
       print  filename + ' is not a flac or cue file'

(not a) or (not b) == not ( a and b ) ,仅当a和b均为true时为false

not (a or b) 仅当a和be均为假时才为true。

Here’s a Boolean thing:

if (not suffix == "flac" )  or (not suffix == "cue" ):   # WRONG! FAILS
    print  filename + ' is not a flac or cue file'

but

if not (suffix == "flac"  or suffix == "cue" ):     # CORRECT!
       print  filename + ' is not a flac or cue file'

(not a) or (not b) == not ( a and b ) , is false only if a and b are both true

not (a or b) is true only if a and be are both false.


使用熊猫比较两列

问题:使用熊猫比较两列

以此为起点:

a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])

Out[8]: 
  one  two three
0   10  1.2   4.2
1   15  70   0.03
2    8   5     0

我想if在熊猫中使用类似声明的内容。

if df['one'] >= df['two'] and df['one'] <= df['three']:
    df['que'] = df['one']

基本上,通过if语句检查每一行,然后创建新列。

文档说要使用,.all但没有示例…

Using this as a starting point:

a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])

Out[8]: 
  one  two three
0   10  1.2   4.2
1   15  70   0.03
2    8   5     0

I want to use something like an if statement within pandas.

if df['one'] >= df['two'] and df['one'] <= df['three']:
    df['que'] = df['one']

Basically, check each row via the if statement, create new column.

The docs say to use .all but there is no example…


回答 0

您可以使用np.where。如果cond是布尔数组,A并且B是数组,则

C = np.where(cond, A, B)

将C定义为等于A哪里cond为True,B哪里cond为False。

import numpy as np
import pandas as pd

a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])

df['que'] = np.where((df['one'] >= df['two']) & (df['one'] <= df['three'])
                     , df['one'], np.nan)

Yield

  one  two three  que
0  10  1.2   4.2   10
1  15   70  0.03  NaN
2   8    5     0  NaN

如果您有多个条件,则可以使用np.select代替。例如,如果你想df['que']等于df['two']df['one'] < df['two'],则

conditions = [
    (df['one'] >= df['two']) & (df['one'] <= df['three']), 
    df['one'] < df['two']]

choices = [df['one'], df['two']]

df['que'] = np.select(conditions, choices, default=np.nan)

Yield

  one  two three  que
0  10  1.2   4.2   10
1  15   70  0.03   70
2   8    5     0  NaN

如果我们可以假设df['one'] >= df['two']whendf['one'] < df['two']为False,那么条件和选择可以简化为

conditions = [
    df['one'] < df['two'],
    df['one'] <= df['three']]

choices = [df['two'], df['one']]

(如果包含df['one']df['two']包含NaN,则该假设可能不正确。)


注意

a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])

用字符串值定义一个DataFrame。由于它们看起来是数字,因此最好将这些字符串转换为浮点数:

df2 = df.astype(float)

但是,这会改变结果,因为字符串会逐个字符进行比较,而浮点数会进行数字比较。

In [61]: '10' <= '4.2'
Out[61]: True

In [62]: 10 <= 4.2
Out[62]: False

You could use np.where. If cond is a boolean array, and A and B are arrays, then

C = np.where(cond, A, B)

defines C to be equal to A where cond is True, and B where cond is False.

import numpy as np
import pandas as pd

a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])

df['que'] = np.where((df['one'] >= df['two']) & (df['one'] <= df['three'])
                     , df['one'], np.nan)

yields

  one  two three  que
0  10  1.2   4.2   10
1  15   70  0.03  NaN
2   8    5     0  NaN

If you have more than one condition, then you could use np.select instead. For example, if you wish df['que'] to equal df['two'] when df['one'] < df['two'], then

conditions = [
    (df['one'] >= df['two']) & (df['one'] <= df['three']), 
    df['one'] < df['two']]

choices = [df['one'], df['two']]

df['que'] = np.select(conditions, choices, default=np.nan)

yields

  one  two three  que
0  10  1.2   4.2   10
1  15   70  0.03   70
2   8    5     0  NaN

If we can assume that df['one'] >= df['two'] when df['one'] < df['two'] is False, then the conditions and choices could be simplified to

conditions = [
    df['one'] < df['two'],
    df['one'] <= df['three']]

choices = [df['two'], df['one']]

(The assumption may not be true if df['one'] or df['two'] contain NaNs.)


Note that

a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])

defines a DataFrame with string values. Since they look numeric, you might be better off converting those strings to floats:

df2 = df.astype(float)

This changes the results, however, since strings compare character-by-character, while floats are compared numerically.

In [61]: '10' <= '4.2'
Out[61]: True

In [62]: 10 <= 4.2
Out[62]: False

回答 1

您可以将其.equals用于列或整个数据框。

df['col1'].equals(df['col2'])

如果它们相等,则该语句将返回Trueelse False

You can use .equals for columns or entire dataframes.

df['col1'].equals(df['col2'])

If they’re equal, that statement will return True, else False.


回答 2

您可以使用apply()并执行类似的操作

df['que'] = df.apply(lambda x : x['one'] if x['one'] >= x['two'] and x['one'] <= x['three'] else "", axis=1)

或者如果您不想使用lambda

def que(x):
    if x['one'] >= x['two'] and x['one'] <= x['three']:
        return x['one']
    return ''
df['que'] = df.apply(que, axis=1)

You could use apply() and do something like this

df['que'] = df.apply(lambda x : x['one'] if x['one'] >= x['two'] and x['one'] <= x['three'] else "", axis=1)

or if you prefer not to use a lambda

def que(x):
    if x['one'] >= x['two'] and x['one'] <= x['three']:
        return x['one']
    return ''
df['que'] = df.apply(que, axis=1)

回答 3

一种方法是使用布尔序列对列进行索引df['one']。这将为您提供一个新列,其中的True条目与相同的行具有相同的值,df['one']并且这些False值为NaN

布尔级数仅由您的if语句给出(尽管必须使用&代替and):

>>> df['que'] = df['one'][(df['one'] >= df['two']) & (df['one'] <= df['three'])]
>>> df
    one two three   que
0   10  1.2 4.2      10
1   15  70  0.03    NaN
2   8   5   0       NaN

如果您希望将这些NaN值替换为其他值,则可以使用fillna新列上的方法que。我用的0不是这里的空字符串:

>>> df['que'] = df['que'].fillna(0)
>>> df
    one two three   que
0   10  1.2   4.2    10
1   15   70  0.03     0
2    8    5     0     0

One way is to use a Boolean series to index the column df['one']. This gives you a new column where the True entries have the same value as the same row as df['one'] and the False values are NaN.

The Boolean series is just given by your if statement (although it is necessary to use & instead of and):

>>> df['que'] = df['one'][(df['one'] >= df['two']) & (df['one'] <= df['three'])]
>>> df
    one two three   que
0   10  1.2 4.2      10
1   15  70  0.03    NaN
2   8   5   0       NaN

If you want the NaN values to be replaced by other values, you can use the fillna method on the new column que. I’ve used 0 instead of the empty string here:

>>> df['que'] = df['que'].fillna(0)
>>> df
    one two three   que
0   10  1.2   4.2    10
1   15   70  0.03     0
2    8    5     0     0

回答 4

将每个条件括在括号中,然后使用&运算符组合条件:

df.loc[(df['one'] >= df['two']) & (df['one'] <= df['three']), 'que'] = df['one']

您可以仅使用~(“ not”运算符)来反转匹配项来填充不匹配的行:

df.loc[~ ((df['one'] >= df['two']) & (df['one'] <= df['three'])), 'que'] = ''

您需要使用&~而不是and和,not因为&~运算符是逐个元素地工作的。

最终结果:

df
Out[8]: 
  one  two three que
0  10  1.2   4.2  10
1  15   70  0.03    
2   8    5     0  

Wrap each individual condition in parentheses, and then use the & operator to combine the conditions:

df.loc[(df['one'] >= df['two']) & (df['one'] <= df['three']), 'que'] = df['one']

You can fill the non-matching rows by just using ~ (the “not” operator) to invert the match:

df.loc[~ ((df['one'] >= df['two']) & (df['one'] <= df['three'])), 'que'] = ''

You need to use & and ~ rather than and and not because the & and ~ operators work element-by-element.

The final result:

df
Out[8]: 
  one  two three que
0  10  1.2   4.2  10
1  15   70  0.03    
2   8    5     0  

回答 5

使用np.select,如果你必须从数据帧和输出特定的选择在不同的列中选中多个条件

conditions=[(condition1),(condition2)]
choices=["choice1","chocie2"]

df["new column"]=np.select=(condtion,choice,default=)

注意:没有条件,没有选择项应该匹配,如果对于两个不同的条件您有相同的选择,请重复选择文本

Use np.select if you have multiple conditions to be checked from the dataframe and output a specific choice in a different column

conditions=[(condition1),(condition2)]
choices=["choice1","chocie2"]

df["new column"]=np.select=(condtion,choice,default=)

Note: No of conditions and no of choices should match, repeat text in choice if for two different conditions you have same choices


回答 6

我认为最接近OP直觉的是内联if语句:

df['que'] = (df['one'] if ((df['one'] >= df['two']) and (df['one'] <= df['three'])) 

I think the closest to the OP’s intuition is an inline if statement:

df['que'] = (df['one'] if ((df['one'] >= df['two']) and (df['one'] <= df['three'])) 

当else做得最多时,最有效的方式来执行if-elif-elif-else语句?

问题:当else做得最多时,最有效的方式来执行if-elif-elif-else语句?

我有一条if-elif-elif-else语句,其中99%的时间执行else语句:

if something == 'this':
    doThis()
elif something == 'that':
    doThat()
elif something == 'there':
    doThere()
else:
    doThisMostOfTheTime()

这个构造完成了很多,但是由于它在碰到其他情况之前会遍历所有条件,所以我觉得这不是很有效,更不用说Pythonic了。另一方面,它确实需要知道是否满足任何这些条件,因此无论如何都应该对其进行测试。

是否有人知道是否以及如何可以更有效地完成此工作,或者这仅仅是做到这一点的最佳方法?

I’ve got a in if-elif-elif-else statement in which 99% of the time, the else statement is executed:

if something == 'this':
    doThis()
elif something == 'that':
    doThat()
elif something == 'there':
    doThere()
else:
    doThisMostOfTheTime()

This construct is done a lot, but since it goes over every condition before it hits the else I have the feeling this is not very efficient, let alone Pythonic. On the other hand, it does need to know if any of those conditions are met, so it should test it anyway.

Does anybody know if and how this could be done more efficiently or is this simply the best possible way to do it?


回答 0

代码…

options.get(something, doThisMostOfTheTime)()

…看起来应该是快,但它实际上是慢于ifelifelse结构,因为它要调用一个函数,它可以在一个紧密的循环一个显著的性能开销。

考虑这些例子…

1.py

something = 'something'

for i in xrange(1000000):
    if something == 'this':
        the_thing = 1
    elif something == 'that':
        the_thing = 2
    elif something == 'there':
        the_thing = 3
    else:
        the_thing = 4

2.py

something = 'something'
options = {'this': 1, 'that': 2, 'there': 3}

for i in xrange(1000000):
    the_thing = options.get(something, 4)

3.py

something = 'something'
options = {'this': 1, 'that': 2, 'there': 3}

for i in xrange(1000000):
    if something in options:
        the_thing = options[something]
    else:
        the_thing = 4

4.py

from collections import defaultdict

something = 'something'
options = defaultdict(lambda: 4, {'this': 1, 'that': 2, 'there': 3})

for i in xrange(1000000):
    the_thing = options[something]

…并注意他们使用的CPU时间…

1.py: 160ms
2.py: 170ms
3.py: 110ms
4.py: 100ms

…使用来自的用户时间time(1)

选项#4确实有额外的内存开销,需要为每个不同的键缺失添加一个新项,因此,如果您期望数量众多的不同的键缺失,我会选择方法#3,它在原始构造。

The code…

options.get(something, doThisMostOfTheTime)()

…looks like it ought to be faster, but it’s actually slower than the ifelifelse construct, because it has to call a function, which can be a significant performance overhead in a tight loop.

Consider these examples…

1.py

something = 'something'

for i in xrange(1000000):
    if something == 'this':
        the_thing = 1
    elif something == 'that':
        the_thing = 2
    elif something == 'there':
        the_thing = 3
    else:
        the_thing = 4

2.py

something = 'something'
options = {'this': 1, 'that': 2, 'there': 3}

for i in xrange(1000000):
    the_thing = options.get(something, 4)

3.py

something = 'something'
options = {'this': 1, 'that': 2, 'there': 3}

for i in xrange(1000000):
    if something in options:
        the_thing = options[something]
    else:
        the_thing = 4

4.py

from collections import defaultdict

something = 'something'
options = defaultdict(lambda: 4, {'this': 1, 'that': 2, 'there': 3})

for i in xrange(1000000):
    the_thing = options[something]

…and note the amount of CPU time they use…

1.py: 160ms
2.py: 170ms
3.py: 110ms
4.py: 100ms

…using the user time from time(1).

Option #4 does have the additional memory overhead of adding a new item for every distinct key miss, so if you’re expecting an unbounded number of distinct key misses, I’d go with option #3, which is still a significant improvement on the original construct.


回答 1

我要创建一个字典:

options = {'this': doThis,'that' :doThat, 'there':doThere}

现在只使用:

options.get(something, doThisMostOfTheTime)()

如果somethingoptionsdict中找不到,dict.get则将返回默认值doThisMostOfTheTime

一些时间比较:

脚本:

from random import shuffle
def doThis():pass
def doThat():pass
def doThere():pass
def doSomethingElse():pass
options = {'this':doThis, 'that':doThat, 'there':doThere}
lis = range(10**4) + options.keys()*100
shuffle(lis)

def get():
    for x in lis:
        options.get(x, doSomethingElse)()

def key_in_dic():
    for x in lis:
        if x in options:
            options[x]()
        else:
            doSomethingElse()

def if_else():
    for x in lis:
        if x == 'this':
            doThis()
        elif x == 'that':
            doThat()
        elif x == 'there':
            doThere()
        else:
            doSomethingElse()

结果:

>>> from so import *
>>> %timeit get()
100 loops, best of 3: 5.06 ms per loop
>>> %timeit key_in_dic()
100 loops, best of 3: 3.55 ms per loop
>>> %timeit if_else()
100 loops, best of 3: 6.42 ms per loop

对于10**5不存在的密钥和100个有效密钥:

>>> %timeit get()
10 loops, best of 3: 84.4 ms per loop
>>> %timeit key_in_dic()
10 loops, best of 3: 50.4 ms per loop
>>> %timeit if_else()
10 loops, best of 3: 104 ms per loop

因此,对于普通字典而言,在key in options这里使用键是最有效的方法:

if key in options:
   options[key]()
else:
   doSomethingElse()

I’d create a dictionary :

options = {'this': doThis,'that' :doThat, 'there':doThere}

Now use just:

options.get(something, doThisMostOfTheTime)()

If something is not found in the options dict then dict.get will return the default value doThisMostOfTheTime

Some timing comparisons:

Script:

from random import shuffle
def doThis():pass
def doThat():pass
def doThere():pass
def doSomethingElse():pass
options = {'this':doThis, 'that':doThat, 'there':doThere}
lis = range(10**4) + options.keys()*100
shuffle(lis)

def get():
    for x in lis:
        options.get(x, doSomethingElse)()

def key_in_dic():
    for x in lis:
        if x in options:
            options[x]()
        else:
            doSomethingElse()

def if_else():
    for x in lis:
        if x == 'this':
            doThis()
        elif x == 'that':
            doThat()
        elif x == 'there':
            doThere()
        else:
            doSomethingElse()

Results:

>>> from so import *
>>> %timeit get()
100 loops, best of 3: 5.06 ms per loop
>>> %timeit key_in_dic()
100 loops, best of 3: 3.55 ms per loop
>>> %timeit if_else()
100 loops, best of 3: 6.42 ms per loop

For 10**5 non-existent keys and 100 valid keys::

>>> %timeit get()
10 loops, best of 3: 84.4 ms per loop
>>> %timeit key_in_dic()
10 loops, best of 3: 50.4 ms per loop
>>> %timeit if_else()
10 loops, best of 3: 104 ms per loop

So, for a normal dictionary checking for the key using key in options is the most efficient way here:

if key in options:
   options[key]()
else:
   doSomethingElse()

回答 2

可以使用pypy吗?

保留原始代码,但在pypy上运行可使我的速度提高50倍。

CPython:

matt$ python
Python 2.6.8 (unknown, Nov 26 2012, 10:25:03)
[GCC 4.2.1 Compatible Apple Clang 3.0 (tags/Apple/clang-211.12)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> from timeit import timeit
>>> timeit("""
... if something == 'this': pass
... elif something == 'that': pass
... elif something == 'there': pass
... else: pass
... """, "something='foo'", number=10000000)
1.728302001953125

pypy:

matt$ pypy
Python 2.7.3 (daf4a1b651e0, Dec 07 2012, 23:00:16)
[PyPy 2.0.0-beta1 with GCC 4.2.1] on darwin
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``a 10th of forever is 1h45''
>>>>
>>>> from timeit import timeit
>>>> timeit("""
.... if something == 'this': pass
.... elif something == 'that': pass
.... elif something == 'there': pass
.... else: pass
.... """, "something='foo'", number=10000000)
0.03306388854980469

Are you able to use pypy?

Keeping your original code but running it on pypy gives a 50x speed-up for me.

CPython:

matt$ python
Python 2.6.8 (unknown, Nov 26 2012, 10:25:03)
[GCC 4.2.1 Compatible Apple Clang 3.0 (tags/Apple/clang-211.12)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> from timeit import timeit
>>> timeit("""
... if something == 'this': pass
... elif something == 'that': pass
... elif something == 'there': pass
... else: pass
... """, "something='foo'", number=10000000)
1.728302001953125

Pypy:

matt$ pypy
Python 2.7.3 (daf4a1b651e0, Dec 07 2012, 23:00:16)
[PyPy 2.0.0-beta1 with GCC 4.2.1] on darwin
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``a 10th of forever is 1h45''
>>>>
>>>> from timeit import timeit
>>>> timeit("""
.... if something == 'this': pass
.... elif something == 'that': pass
.... elif something == 'there': pass
.... else: pass
.... """, "something='foo'", number=10000000)
0.03306388854980469

回答 3

这里是将动态条件转换为字典的if的示例。

selector = {lambda d: datetime(2014, 12, 31) >= d : 'before2015',
            lambda d: datetime(2015, 1, 1) <= d < datetime(2016, 1, 1): 'year2015',
            lambda d: datetime(2016, 1, 1) <= d < datetime(2016, 12, 31): 'year2016'}

def select_by_date(date, selector=selector):
    selected = [selector[x] for x in selector if x(date)] or ['after2016']
    return selected[0]

这是一种方法,但可能不是最Python的方法,因为对于不熟练使用Python的人来说可读性较差。

Here an example of a if with dynamic conditions translated to a dictionary.

selector = {lambda d: datetime(2014, 12, 31) >= d : 'before2015',
            lambda d: datetime(2015, 1, 1) <= d < datetime(2016, 1, 1): 'year2015',
            lambda d: datetime(2016, 1, 1) <= d < datetime(2016, 12, 31): 'year2016'}

def select_by_date(date, selector=selector):
    selected = [selector[x] for x in selector if x(date)] or ['after2016']
    return selected[0]

It is a way, but may not be the most pythonic way to do it because is less readable for whom is not fluent in Python.


回答 4

人们exec出于安全原因发出警告,但这是一个理想的案例。
这是一个简单的状态机。

Codes = {}
Codes [0] = compile('blah blah 0; nextcode = 1')
Codes [1] = compile('blah blah 1; nextcode = 2')
Codes [2] = compile('blah blah 2; nextcode = 0')

nextcode = 0
While True:
    exec(Codes[nextcode])

People warn about exec for security reasons, but this is an ideal case for it.
It’s an easy state machine.

Codes = {}
Codes [0] = compile('blah blah 0; nextcode = 1')
Codes [1] = compile('blah blah 1; nextcode = 2')
Codes [2] = compile('blah blah 2; nextcode = 0')

nextcode = 0
While True:
    exec(Codes[nextcode])

一行if-condition-assignment

问题:一行if-condition-assignment

我有以下代码

num1 = 10
someBoolValue = True

我需要将to的值设置num120if someBoolValueis True; 否则就什么也不做。所以,这是我的代码

num1 = 20 if someBoolValue else num1

有什么办法可以避免...else num1使它看起来更干净的部分吗?相当于

if someBoolValue:
    num1 = 20

我尝试将其替换为...else passnum1=20 if someBoolValue else pass。我所得到的只是语法错误。我也不能忽略这一...else num1部分。

I have the following code

num1 = 10
someBoolValue = True

I need to set the value of num1 to 20 if someBoolValue is True; and do nothing otherwise. So, here is my code for that

num1 = 20 if someBoolValue else num1

Is there someway I could avoid the ...else num1 part to make it look cleaner? An equivalent to

if someBoolValue:
    num1 = 20

I tried replacing it with ...else pass like this: num1=20 if someBoolValue else pass. All I got was syntax error. Nor I could just omit the ...else num1 part.


回答 0

我认为这在Python中是不可能的,因为您实际尝试执行的操作可能会扩展为以下内容:

num1 = 20 if someBoolValue else num1

如果您排除else num1,则会收到语法错误,因为我非常确定分配实际上必须返回某些内容。

正如其他人已经提到的那样,您可以执行此操作,但是这样做很不好,因为您可能在下次阅读该代码段时最终会感到困惑:

if someBoolValue: num1=20

num1 = someBoolValue and 20 or num1出于完全相同的原因,我不是超级粉丝。我实际上必须对这条线在做什么进行三思。

实际实现您想要做的最好的方法是原始版本:

if someBoolValue:
    num1 = 20

最好的版本是因为您想做什么很明显,并且您不会混淆自己,或者其他人以后将与该代码联系。

另外,请注意,num1 = 20 if someBoolValue有效的Ruby代码是有效的,因为Ruby的工作方式略有不同。

I don’t think this is possible in Python, since what you’re actually trying to do probably gets expanded to something like this:

num1 = 20 if someBoolValue else num1

If you exclude else num1, you’ll receive a syntax error since I’m quite sure that the assignment must actually return something.

As others have already mentioned, you could do this, but it’s bad because you’ll probably just end up confusing yourself when reading that piece of code the next time:

if someBoolValue: num1=20

I’m not a big fan of the num1 = someBoolValue and 20 or num1 for the exact same reason. I have to actually think twice on what that line is doing.

The best way to actually achieve what you want to do is the original version:

if someBoolValue:
    num1 = 20

The reason that’s the best verison is because it’s very obvious what you want to do, and you won’t confuse yourself, or whoever else is going to come in contact with that code later.

Also, as a side note, num1 = 20 if someBoolValue is valid Ruby code, because Ruby works a bit differently.


回答 1

用这个:

num1 = 20 if someBoolValue else num1

Use this:

num1 = 20 if someBoolValue else num1

回答 2

一行:

if someBoolValue: num1 = 20

但是不要那样做。通常不希望使用此样式。人们更喜欢较长的形式,以保持清晰度和一致性。

if someBoolValue:
    num1 = 20

(同样,应避免使用骆驼帽。因此请使用some_bool_value。)

注意,不存在没有部分的内联表达式 不存在,因为如果谓词为false,则不会有返回值。但是,在所有情况下,表达式必须具有明确定义的返回值。这与Ruby或Perl中的用法不同。some_value if predicateelse

In one line:

if someBoolValue: num1 = 20

But don’t do that. This style is normally not expected. People prefer the longer form for clarity and consistency.

if someBoolValue:
    num1 = 20

(Equally, camel caps should be avoided. So rather use some_bool_value.)

Note that an in-line expression some_value if predicate without an else part does not exist because there would not be a return value if the predicate were false. However, expressions must have a clearly defined return value in all cases. This is different from usage as in, say, Ruby or Perl.


回答 3

您可以使用以下之一:

(falseVal, trueVal)[TEST]

TEST and trueVal or falseVal

you can use one of the following:

(falseVal, trueVal)[TEST]

TEST and trueVal or falseVal

回答 4

不,我想您希望类似的方法num1 = 20 if someBoolValue可以工作,但是不行。我认为最好的方法就是if编写语句:

if someBoolValue:
    num1 = 20

No. I guess you were hoping that something like num1 = 20 if someBoolValue would work, but it doesn’t. I think the best way is with the if statement as you have written it:

if someBoolValue:
    num1 = 20

回答 5

num1 = 10 + 10*(someBoolValue is True)

那是我新的最终答案。先前的答复如下,并且对于所述问题过大。Getting_too_clever == not Good。这是先前的答案…如果您想为Truecond 添加一件事,为False:添加另一件事,还是不错的:

num1 = 10 + (0,10)[someBoolValue is True]

您提到num1的值已经应该保留。我假设num1 = 10因为这是该帖子的第一条语句,所以要进行的操作20是add 10

num1 = 10
someBoolValue = True

num1 = 10 + (0,10)[someBoolValue is True]

print(f'num1 = {num1}\nsomeBoolValue = {someBoolValue}')

产生了这个输出

num1 = 20
someBoolValue = True
num1 = 10 + 10*(someBoolValue is True)

That’s my new final answer. Prior answer was as follows and was overkill for the stated problem. Getting_too_clever == not Good. Here’s the prior answer… still good if you want add one thing for True cond and another for False:

num1 = 10 + (0,10)[someBoolValue is True]

You mentioned num1 would already have a value that should be left alone. I assumed num1 = 10 since that’s the first statement of the post, so the operation to get to 20 is to add 10.

num1 = 10
someBoolValue = True

num1 = 10 + (0,10)[someBoolValue is True]

print(f'num1 = {num1}\nsomeBoolValue = {someBoolValue}')

produced this output

num1 = 20
someBoolValue = True

回答 6

num1 = 20 * someBoolValue or num1
num1 = 20 * someBoolValue or num1

回答 7

如果希望在某个布尔值为true的情况下调用方法,则可以else None终止三进制。

>>> a=1
>>> print(a) if a==1 else None
1
>>> print(a) if a==2 else None
>>> a=2
>>> print(a) if a==2 else None
2
>>> print(a) if a==1 else None
>>>

If you wish to invoke a method if some boolean is true, you can put else None to terminate the trinary.

>>> a=1
>>> print(a) if a==1 else None
1
>>> print(a) if a==2 else None
>>> a=2
>>> print(a) if a==2 else None
2
>>> print(a) if a==1 else None
>>>

回答 8

如果肯定要为您编写一行代码,Python 3.8会引入亲切的表达式,被称为“海象运算符”。

:=

someBoolValue and (num := 20)

如果第一个布尔表达式为,20则将分配给。赋值必须在括号内,否则会出现语法错误。numTrue

num = 10
someBoolValue = True

someBoolValue and (num := 20)
print(num) # 20

num = 10
someBoolValue = False

someBoolValue and (num := 20)
print(num) # 10

If one line code is definitely going to happen for you, Python 3.8 introduces assignment expressions affectionately known as “the walrus operator”.

:=

someBoolValue and (num := 20)

The 20 will be assigned to num if the first boolean expression is True. The assignment must be inside parentheses here otherwise you will get a syntax error.

num = 10
someBoolValue = True

someBoolValue and (num := 20)
print(num) # 20

num = 10
someBoolValue = False

someBoolValue and (num := 20)
print(num) # 10

回答 9

对于来自Google的未来旅行者来说,这是一种新方法(可从python 3.8开始使用):

b = 1
if a := b:
    # this section is only reached if b is not 0 or false.
    # Also, a is set to b
    print(a, b)

For the future time traveler from google, here is a new way (available from python 3.8 onward):

b = 1
if a := b:
    # this section is only reached if b is not 0 or false.
    # Also, a is set to b
    print(a, b)

回答 10

如果需要,您绝对可以使用num1 =(如果someBoolValue为20,则为num1)。

You can definitely use num1 = (20 if someBoolValue else num1) if you want.


回答 11

这是我可以建议的。使用另一个变量派生if子句并将其分配给num1。

码:

num2 =20 if someBoolValue else num1
num1=num2

Here is what i can suggest. Use another variable to derive the if clause and assign it to num1.

Code:

num2 =20 if someBoolValue else num1
num1=num2

回答 12

其他方式 num1 = (20*boolVar)+(num1*(not boolVar))

Another way num1 = (20*boolVar)+(num1*(not boolVar))


回答 13

您可以这样进行。

try:
    a = [i for i in [20] if False][0]
except IndexError:
    print("Do what ever you want here")

您可以通过这种方式解决问题,但是,使用“ try / except block”不是python的最佳实践。

You can do it this way.

try:
    a = [i for i in [20] if False][0]
except IndexError:
    print("Do what ever you want here")

You can solve your problem this way but, using ‘try/except block’ is not the best practice for python.


更好地“尝试”某些东西并捕获异常或测试是否有可能首先避免异常?

问题:更好地“尝试”某些东西并捕获异常或测试是否有可能首先避免异常?

我应该测试if某种东西是有效的还是只是try为了做它并捕获异常?

  • 有没有可靠的文档说首选方法?
  • 还有一种方法更pythonic吗?

例如,我应该:

if len(my_list) >= 4:
    x = my_list[3]
else:
    x = 'NO_ABC'

要么:

try:
    x = my_list[3]
except IndexError:
    x = 'NO_ABC'

一些想法…
PEP 20说:

错误绝不能默默传递。
除非明确地保持沉默。

应该使用a try而不是an if解释为无声传递的错误吗?如果是这样,您是否通过以这种方式使用它来明确使其静音,从而使其正常运行?


不是指只能以一种方式做事的情况;例如:

try:
    import foo
except ImportError:
    import baz

Should I test if something is valid or just try to do it and catch the exception?

  • Is there any solid documentation saying that one way is preferred?
  • Is one way more pythonic?

For example, should I:

if len(my_list) >= 4:
    x = my_list[3]
else:
    x = 'NO_ABC'

Or:

try:
    x = my_list[3]
except IndexError:
    x = 'NO_ABC'

Some thoughts…
PEP 20 says:

Errors should never pass silently.
Unless explicitly silenced.

Should using a try instead of an if be interpreted as an error passing silently? And if so, are you explicitly silencing it by using it in this way, therefore making it OK?


I’m not referring to situations where you can only do things 1 way; for example:

try:
    import foo
except ImportError:
    import baz

回答 0

你应该更喜欢try/exceptif/else如果结果

  • 加快速度(例如,通过防止额外的查询)
  • 更清晰的代码(行数更少/更易于阅读)

通常,它们并存。


加速

如果尝试通过以下方式在长列表中查找元素:

try:
    x = my_list[index]
except IndexError:
    x = 'NO_ABC'

index可能在列表中并且通常不引发IndexError 时,尝试除外是最好的选择。这样,您就可以避免进行额外的查询if index < len(my_list)

Python鼓励使用异常,可以使用Dive Into Python中的短语来处理异常。您的示例不仅(优美地)处理异常,而不是让其静默通过,而且仅在未找到索引的特殊情况下才发生异常(因此,单词异常!)。


清洁代码

Python的官方文档中提到了EAFP比获得许可更容易获得宽恕Rob Knight指出捕获错误而不是避免错误可以使代码简洁,更易于阅读。他的示例如下所示:

更差(LBYL“跳前先看”)

#check whether int conversion will raise an error
if not isinstance(s, str) or not s.isdigit():
    return None
elif len(s) > 10:    #too many digits for int conversion
    return None
else:
    return int(s)

更好(EAFP:寻求宽恕比获得许可更容易)

try:
    return int(s)
except (TypeError, ValueError, OverflowError): #int conversion failed
    return None

You should prefer try/except over if/else if that results in

  • speed-ups (for example by preventing extra lookups)
  • cleaner code (fewer lines/easier to read)

Often, these go hand-in-hand.


speed-ups

In the case of trying to find an element in a long list by:

try:
    x = my_list[index]
except IndexError:
    x = 'NO_ABC'

the try, except is the best option when the index is probably in the list and the IndexError is usually not raised. This way you avoid the need for an extra lookup by if index < len(my_list).

Python encourages the use of exceptions, which you handle is a phrase from Dive Into Python. Your example not only handles the exception (gracefully), rather than letting it silently pass, also the exception occurs only in the exceptional case of index not being found (hence the word exception!).


cleaner code

The official Python Documentation mentions EAFP: Easier to ask for forgiveness than permission and Rob Knight notes that catching errors rather than avoiding them, can result in cleaner, easier to read code. His example says it like this:

Worse (LBYL ‘look before you leap’):

#check whether int conversion will raise an error
if not isinstance(s, str) or not s.isdigit():
    return None
elif len(s) > 10:    #too many digits for int conversion
    return None
else:
    return int(s)

Better (EAFP: Easier to ask for forgiveness than permission):

try:
    return int(s)
except (TypeError, ValueError, OverflowError): #int conversion failed
    return None

回答 1

在这种情况下,您应该完全使用其他方法:

x = myDict.get("ABC", "NO_ABC")

不过,通常来说:如果您希望测试经常失败,请使用if。如果测试相对于尝试操作并失败则捕获异常而言代价高昂,请使用try。如果以上条件均不适用,则更容易阅读。

In this particular case, you should use something else entirely:

x = myDict.get("ABC", "NO_ABC")

In general, though: If you expect the test to fail frequently, use if. If the test is expensive relative to just trying the operation and catching the exception if it fails, use try. If neither one of these conditions applies, go with whatever reads easier.


回答 2

使用tryexcept直接,而不是内侧if后卫应该始终是否有竞争条件的可能性来完成。例如,如果要确保目录存在,请不要执行以下操作:

import os, sys
if not os.path.isdir('foo'):
  try:
    os.mkdir('foo')
  except OSError, e
    print e
    sys.exit(1)

如果另一个线程或进程在isdir和之间创建目录,则将mkdir退出。相反,请执行以下操作:

import os, sys, errno
try:
  os.mkdir('foo')
except OSError, e
  if e.errno != errno.EEXIST:
    print e
    sys.exit(1)

仅当无法创建’foo’目录时,该命令才会退出。

Using try and except directly rather than inside an if guard should always be done if there is any possibility of a race condition. For example, if you want to ensure that a directory exists, do not do this:

import os, sys
if not os.path.isdir('foo'):
  try:
    os.mkdir('foo')
  except OSError, e
    print e
    sys.exit(1)

If another thread or process creates the directory between isdir and mkdir, you’ll exit. Instead, do this:

import os, sys, errno
try:
  os.mkdir('foo')
except OSError, e
  if e.errno != errno.EEXIST:
    print e
    sys.exit(1)

That will only exit if the ‘foo’ directory can’t be created.


回答 3

如果在进行某些操作之前先检查一下是否会失败,那么您可能应该赞成这样做。毕竟,构造异常(包括相关的回溯)需要花费时间。

异常应用于:

  1. 出乎意料的事情,或者…
  2. 您需要跳到不只一个逻辑层次的事情(例如a break不能使您走得太远),或者…
  3. 您不确切知道该如何提前处理异常的事情,或者…
  4. 提前检查故障的成本很高(相对于尝试操作而言)

请注意,通常,真正的答案是“都不是”-例如,在第一个示例中,您真正应该做的只是.get()提供默认值:

x = myDict.get('ABC', 'NO_ABC')

If it’s trivial to check whether something will fail before you do it, you should probably favor that. After all, constructing exceptions (including their associated tracebacks) takes time.

Exceptions should be used for:

  1. things that are unexpected, or…
  2. things where you need to jump more than one level of logic (e.g. where a break doesn’t get you far enough), or…
  3. things where you don’t know exactly what is going to be handling the exception ahead of time, or…
  4. things where checking ahead of time for failure is expensive (relative to just attempting the operation)

Note that oftentimes, the real answer is “neither” – for instance, in your first example, what you really should do is just use .get() to provide a default:

x = myDict.get('ABC', 'NO_ABC')

回答 4

正如其他职位所提到的,这取决于情况。使用try / except代替预先检查数据的有效性存在一些危险,尤其是在较大的项目中使用时。

  • 在try块中的代码可能有机会在捕获异常之前进行各种破坏-如果您事先使用if语句主动进行检查,则可以避免这种情况。
  • 如果在try块中调用的代码引发了一个常见的异常类型(如TypeError或ValueError),则您实际上可能没有捕获到您期望捕获的相同异常-可能是其他原因导致甚至在进入之前或之后引发相同的异常类可能引发异常的行。

例如,假设您有:

try:
    x = my_list[index_list[3]]
except IndexError:
    x = 'NO_ABC'

IndexError没有任何关于尝试获取index_list或my_list元素时是否发生的信息。

As the other posts mention, it depends on the situation. There are a few dangers with using try/except in place of checking the validity of your data in advance, especially when using it on bigger projects.

  • The code in the try block may have a chance to wreak all sorts of havoc before the exception is caught – if you proactively check beforehand with an if statement you can avoid this.
  • If the code called in your try block raises a common exception type, like TypeError or ValueError, you may not actually catch the same exception you were expecting to catch – it may be something else that raise the same exception class before or after even getting to the line where your exception may be raised.

e.g., suppose you had:

try:
    x = my_list[index_list[3]]
except IndexError:
    x = 'NO_ABC'

The IndexError says nothing about whether it occurred when trying to get an element of index_list or my_list.


回答 5

应该使用try而不是if来解释为无声传递的错误吗?如果是这样,您是否通过以这种方式使用它来明确使其静音,从而使其正常运行?

使用try表示可能会通过错误,这与使其静默通过相反。使用except导致它根本不通过。

try: except:if: else:逻辑更为复杂的情况下,首选使用。简单胜于复杂。复杂胜于复杂;要求宽恕比允许容易。

警告:“错误永远都不能静默传递”,是代码可能引发您所知道的异常,并且您的设计承认存在这种可能性的情况,但您并未以处理异常的方式进行设计。在我看来,明确地消除错误将像passexcept块中那样进行,仅应在了解“不做任何事情”确实是特定情况下的正确错误处理的情况下进行操作。(这是我真正需要使用编写良好的代码进行注释的少数几次。)

但是,在您的特定示例中,都不适合:

x = myDict.get('ABC', 'NO_ABC')

每个人都指出这一点的原因-即使您承认您希望总体上理解并且无法提出更好的例子-是在很多情况下实际上存在等效的避让,而寻找它们是解决问题的第一步。

Should using a try instead of an if be interpreted as an error passing silently? And if so, are you explicitly silencing it by using it in this way, therefore making it OK?

Using try is acknowledging that an error may pass, which is the opposite of having it pass silently. Using except is causing it not to pass at all.

Using try: except: is preferred in cases where if: else: logic is more complicated. Simple is better than complex; complex is better than complicated; and it’s easier to ask for forgiveness than permission.

What “errors should never pass silently” is warning about, is the case where code could raise an exception that you know about, and where your design admits the possibility, but you haven’t designed in a way to deal with the exception. Explicitly silencing an error, in my view, would be doing something like pass in an except block, which should only be done with an understanding that “doing nothing” really is the correct error handling in the particular situation. (This is one of the few times where I feel like a comment in well-written code is probably really needed.)

However, in your particular example, neither is appropriate:

x = myDict.get('ABC', 'NO_ABC')

The reason everyone is pointing this out – even though you acknowledge your desire to understand in general, and inability to come up with a better example – is that equivalent side-steps actually exist in quite a lot of cases, and looking for them is the first step in solving the problem.


回答 6

每当try/except用于控制流时,请问自己:

  1. 是否容易看到该try块何时成功以及何时失败?
  2. 您是否知道该区块内的所有副作用try
  3. 您是否知道该块引发异常的所有情况try
  4. 如果该try块的实现发生更改,您的控制流是否仍将按预期运行?

如果对这些问题中的一个或多个的回答为“否”,则可能会有很多宽容的要求。最有可能来自您未来的自我。


一个例子。我最近在一个更大的项目中看到了如下代码:

try:
    y = foo(x)
except ProgrammingError:
    y = bar(x)

与程序员交谈后,发现预期的控制流程为:

如果x是整数,则y = foo(x)。

如果x是整数列表,则y = bar(x)。

之所以foo可行,是因为进行了数据库查询,如果x为整数,则查询将成功,如果为列表,ProgrammingError则将抛出if x

try/except在这里使用是一个不好的选择:

  1. 异常的名称ProgrammingError不会给出实际的问题(x不是整数),这使得很难看到发生了什么。
  2. ProgrammingError数据库调用,浪费时间内上升。如果事实证明是foo在引发异常之前将某些内容写入数据库或更改了其他系统的状态,那么事情将变得非常可怕。
  3. 尚不清楚是否ProgrammingError仅在x整数列表时才引发。例如,假设foo的数据库查询中有错字。这可能还会引发一个ProgrammingError。结果是,bar(x)x是整数时,现在也称为。这可能会引发神秘异常或产生不可预见的结果。
  4. try/except块为的所有未来实现增加了要求foo。每当我们进行更改时foo,我们现在都必须考虑它如何处理列表,并确保它引发一个错误,ProgrammingError而不是一个AttributeError或根本不引发一个错误。

Whenever you use try/except for control flow, ask yourself:

  1. Is it easy to see when the try block succeeds and when it fails?
  2. Are you aware of all side effects inside the try block?
  3. Are you aware of all cases in which the try block throws the exception?
  4. If the implementation of the try block changes, will your control flow still behave as expected?

If the answer to one or more of these questions is ‘no’, there might be a lot of forgiveness to ask for; most likely from your future self.


An example. I recently saw code in a larger project that looked like this:

try:
    y = foo(x)
except ProgrammingError:
    y = bar(x)

Talking to the programmer it turned that the intended control flow was:

If x is an integer, do y = foo(x).

If x is a list of integers, do y = bar(x).

This worked because foo made a database query and the query would be successful if x was an integer and throw a ProgrammingError if x was a list.

Using try/except is a bad choice here:

  1. The name of the exception, ProgrammingError, does not give away the actual problem (that x is not an integer), which makes it difficult to see what is going on.
  2. The ProgrammingError is raised during a database call, which wastes time. Things would get truly horrible if it turned out that foo writes something to the database before it throws an exception, or alters the state of some other system.
  3. It is unclear if ProgrammingError is only raised when x is a list of integers. Suppose for instance that there is a typo in foo‘s database query. This might also raise a ProgrammingError. The consequence is that bar(x) is now also called when x is an integer. This might raise cryptic exceptions or produce unforeseeable results.
  4. The try/except block adds a requirement to all future implementations of foo. Whenever we change foo, we must now think about how it handles lists and make sure that it throws a ProgrammingError and not, say, an AttributeError or no error at all.

回答 7

对于一般含义,您可以考虑阅读Python中的成语和反成语:异常

在您的特定情况下,如其他人所述,您应该使用dict.get()

get(key [,默认])

如果key在字典中,则返回key的值,否则返回默认值。如果未提供default,则默认为None,因此此方法永远不会引发KeyError。

For a general meaning, you may consider reading Idioms and Anti-Idioms in Python: Exceptions.

In your particular case, as others stated, you should use dict.get():

get(key[, default])

Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.


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

问题:避免“ if x:return x”语句的Python方法

我有一个方法可以依次调用其他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

这似乎是很多行李代码。与其执行每行2行的if语句,不如执行以下操作:

x and return x

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

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.

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.


回答 0

您可以使用循环:

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

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

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

但是,如果更具可读性,则值得商bat。

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

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)

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:

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)

回答 1

除了Martijn的好答案之外,您还可以连锁or。这将返回第一个真实值,或者None如果没有真实值,则返回:

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

演示:

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

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

回答 2

不要改变

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

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.


回答 3

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

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

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

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

回答 4

根据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

这样可以避免:

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

…同时保持线性,易于阅读的流程。

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

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

  • 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.


回答 5

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

可以使用内置功能来代替循环any

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

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

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

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.


回答 6

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

这与您所做的一样,没有什么重复,但是IMNSHO它的读起来相当流畅。

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

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.


回答 7

我很惊讶没有人提到any为此目的而设计的内置功能:

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

请注意,尽管此实现可能是最清晰的,但即使第一个是,它也会评估所有检查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)

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

在您的情况下:

  • lambda a, f: a or f()是用于检查累加器a或当前检查f()是否为的函数True。请注意,如果aTruef()则不会进行评估。
  • checks包含检查功能(flambda中的项目)
  • False 是初始值,否则将不进行检查并且结果始终为 True

anyreduce对于函数式编程的基本工具。我强烈建议您训练这些技巧,以及map哪些也很棒!

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:

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

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.
  • checks contains check functions (the f item from the lambda)
  • False is the initial value, otherwise no check would happen and the result would always be 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!


回答 8

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

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

我认为,如果您仔细看,这看起来很好看。

演示:

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:


回答 9

对我来说,最好的答案是@ 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

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

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

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

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.

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

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.

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


回答 10

理想情况下,我将重写check_ 函数以返回True或返回False值。然后您的支票变成

if check_size(x):
    return x
#etc

假设您x不是一成不变的,则您的函数仍可以对其进行修改(尽管他们不能重新分配它)-但是一个被调用的函数check实际上不应进行任何修改。

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

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.


回答 11

上面的Martijns第一个示例略有变化,避免了if循环内:

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

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

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

回答 12

我喜欢@timgeb。在此期间,我想补充一点,表达Nonereturn语句没有必要为收集or分离的语句进行评估,并在第一无为零,无空,则返回无-无,如果没有任何然后None返回是否有None

所以我的check_all_conditions()函数看起来像这样:

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

使用timeitwith时,number=10**7我查看了一些建议的运行时间。为了进行比较,我仅使用该random.random()函数返回字符串或None基于随机数。这是完整的代码:

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()

结果如下:

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

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!

So my check_all_conditions() function looks like this:

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:

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

回答 13

这种方法有点开箱即用,但是我认为最终结果很简单,易读并且看起来不错。

基本思想是raise当其中一个函数的评估结果为“真”并返回结果时出现异常。这是它的外观:

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

assertFalsey当一个调用的函数参数评估为真时,您将需要一个引发异常的函数:

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

可以修改上述内容,以便也为要评估的功能提供参数。

当然,您将需要TruthyException自身。此异常提供了object触发异常的:

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

当然,您可以将原始功能转换为更通用的功能:

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)

这可能会慢一些,因为您同时使用了一条if语句和一个异常。但是,该异常最多只能处理一次,因此对性能的影响应该很小,除非您希望运行检查并获得True成千上万次的值。

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:

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:

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:

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.


回答 14

pythonic方式是使用reduce(如已经提到的)或itertools(如下所示),但是在我看来,仅使用or运算符的短路会产生更清晰的代码

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

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

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

我要跳进这里,从来没有写过Python的任何一行,但是我认为这if x = check_something(): return x是有效的吗?

如果是这样的话:

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

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?

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

回答 16

或使用max

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

Or use max:

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

回答 17

过去,我见过一些用dicts进行switch / 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

select函数消除了每次调用check_FUNCTION两次的需要,即避免check_FUNCTION() if check_FUNCTION() else next添加另一个函数层。这对于长时间运行的功能很有用。字典中的lambda会延迟其值的执行,直到while循环为止。

作为奖励,您可以修改执行顺序,甚至可以通过更改k和跳过某些测试,s例如k='c',s={'c':'b','b':None}减少测试数量)和颠倒原始处理顺序。

timeit研究员可能讨价还价增加额外的一层或两层堆栈,为字典成本抬头,但你似乎更关心的是代码的可爱的成本。

另外,一个更简单的实现可能如下:

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. 我的意思不是用pep8而是用一个简洁的描述性词代替句子。授予OP可能遵循某些编码约定,使用一些现有代码库或不在乎其代码库中的简洁术语。

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))

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.

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.

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.

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.