Strange use of “and” / “or” operator
我在学习python,遇到了一些很好很短但不完全有意义的代码。
背景是:
1 2 | def fn(*args): return len(args) and max(args)-min(args) |
我知道它在做什么,但是为什么python要这样做——即返回值而不是真/假?
1 | 10 and 7-2 |
返回5。类似地,更改和到或将导致功能更改。所以
1 | 10 or 7 - 2 |
将返回10。
这是合法/可靠的风格,还是有什么问题?
DR
我们首先总结两个逻辑运算符
and Return the first Falsy value if there are any, else return the last
value in the expression.
or Return the first Truthy value if there are any, else return the last
value in the expression.
这些行为也在文档中进行了总结,特别是在下表中:
返回布尔值而不考虑其操作数的唯一运算符是
声明
1 | len(args) and max(args) - min(args) |
是一种非常简洁(并且可以说可读性较低)的说法,"如果
1 | exp1 and exp2 |
应该(粗略地)翻译成:
1 2 3 | r1 = exp1 if not r1: r1 = exp2 |
或者,等价地,
1 | r1 = exp1 if exp1 else exp2 |
其中
但什么是真实?它指的是在条件表达式中使用时如何计算对象。@帕特里克豪格在这篇文章中很好地总结了真实性。
All values are considered"truthy" except for the following, which are
"falsy":
None False 0 0.0 0j Decimal(0) Fraction(0, 1) [] - an emptylist {} - an emptydict () - an emptytuple '' - an emptystr b'' - an emptybytes set() - an emptyset - an empty
range , likerange(0) - objects for which
obj.__bool__() returnsFalse obj.__len__() returns0 A"truthy" value will satisfy the check performed by
if orwhile
statements. We use"truthy" and"falsy" to differentiate from the
bool valuesTrue andFalse .
我们以OP的问题为基础,进一步讨论了这些操作符在这些情况下是如何工作的。
Given a function with the definition
1
2 def foo(*args):
...How do I return the difference between the minimum and maximum value
in a list of zero or more arguments?
查找最小值和最大值很容易(使用内置函数!).这里唯一的障碍是适当地处理角情况,其中参数列表可能为空(例如,调用
1 2 | def foo(*args): return len(args) and max(args) - min(args) |
1 2 3 4 5 | foo(1, 2, 3, 4, 5) # 4 foo() # 0 |
由于使用了
在上述函数中,如果
请注意,编写此函数的另一种方法是:
1 2 3 4 5 | def foo(*args): if not len(args): return 0 return max(args) - min(args) |
或者更简而言之,
1 2 | def foo(*args): return 0 if not args else max(args) - min(args) |
当然,这些函数都不执行任何类型检查,因此除非您完全信任所提供的输入,否则不要依赖于这些构造的简单性。
我用一个人为的例子以类似的方式解释了
Given a function with the definition
1
2 def foo(*args):
...How would you complete
foo to return all numbers over9000 ?
我们用
1 2 3 4 5 6 7 8 | def foo(*args): return [x for x in args if x > 9000] or 'No number over 9000!' foo(9004, 1, 2, 500) # [9004] foo(1, 2, 3, 4) # 'No number over 9000!' |
使用条件,我们可以将此函数重新编写为,
1 2 3 4 5 6 | def foo(*args): r = [x for x in args if x > 9000] if not r: return 'No number over 9000!' return r |
和以前一样,这种结构在错误处理方面更加灵活。
从python文档引用
Note that neither
and noror restrict the value and type they return
toFalse andTrue , but rather return the last evaluated argument. This
is sometimes useful, e.g., ifs is a string that should be replaced by
a default value if it is empty, the expressions or 'foo' yields the
desired value.
所以,这就是设计Python来评估布尔表达式的方法,上面的文档让我们了解了为什么要这样做。
要得到一个布尔值,只需输入它。
1 | return bool(len(args) and max(args)-min(args)) |
为什么?
短路。
例如:
1 2 | 2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too 0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all |
对于
python不返回硬核
要知道什么是真是假,请检查帕特里克·豪的回答。
和或执行布尔逻辑,但在比较时返回实际值之一。使用和时,将在从左到右的布尔上下文中计算值。在布尔上下文中,0、""、[]、()、和none为false;其他都为true。
如果布尔上下文中的所有值都为真,则返回最后一个值。
1 2 3 4 | >>> 2 and 5 5 >>> 2 and 5 and 10 10 |
如果布尔上下文中的任何值为假,则返回第一个假值。
1 2 3 4 | >>> '' and 5 '' >>> 2 and 0 and 5 0 |
所以代码
1 | return len(args) and max(args)-min(args) |
返回EDOCX1的值(0),如果有其他参数,则返回EDOCX1的值(1),即0。
Is this legit/reliable style, or are there any gotchas on this?
这是合法的,它是最后一个返回值的短路评估。
你提供了一个很好的例子。如果没有传递参数,函数将返回
另一种使用方法是将none参数默认为可变原语,如空列表:
1 2 3 | def fn(alist=None): alist = alist or [] .... |
如果将某个非真实值传递给
高查斯
是的,有一些问题。
首先,如果
1 2 3 4 5 6 | >>> fn() 0 >>> fn(3) 0 >>> fn(3, 3, 3) 0 |
然后,python是一种动态语言。它在任何地方都没有指定
最后,逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | >>> fn('1') Traceback (most recent call last): File"<stdin>", line 1, in <module> File"<stdin>", line 2, in fn TypeError: unsupported operand type(s) for -: 'str' and 'str' >>> fn(1, '2') Traceback (most recent call last): File"<stdin>", line 1, in <module> File"<stdin>", line 2, in fn TypeError: '>' not supported between instances of 'str' and 'int' >>> fn('a', 'b') Traceback (most recent call last): File"<stdin>", line 1, in <module> File"<stdin>", line 2, in fn TypeError: unsupported operand type(s) for -: 'str' and 'str' |
可能的替代方案
以下是根据"请求原谅比请求允许更容易"原则编写函数的方法:
1 2 3 4 5 6 7 | def delta(*numbers): try: return max(numbers) - min(numbers) except TypeError: raise ValueError("delta should only be called with numerical arguments") from None except ValueError: raise ValueError("delta should be called with at least one numerical argument") from None |
举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | >>> delta() Traceback (most recent call last): File"<stdin>", line 1, in <module> File"<stdin>", line 7, in delta ValueError: delta should be called with at least one numerical argument >>> delta(3) 0 >>> delta('a') Traceback (most recent call last): File"<stdin>", line 1, in <module> File"<stdin>", line 5, in delta ValueError: delta should only be called with numerical arguments >>> delta('a', 'b') Traceback (most recent call last): File"<stdin>", line 1, in <module> File"<stdin>", line 5, in delta ValueError: delta should only be called with numerical arguments >>> delta('a', 3) Traceback (most recent call last): File"<stdin>", line 1, in <module> File"<stdin>", line 5, in delta ValueError: delta should only be called with numerical arguments >>> delta(3, 4.5) 1.5 >>> delta(3, 5, 7, 2) 5 |
如果您真的不想在没有任何参数的情况下调用
1 2 3 4 5 6 7 8 9 10 11 | >>> def delta(*numbers): ... try: ... return max(numbers) - min(numbers) ... except TypeError: ... raise ValueError("delta should only be called with numerical arguments") from None ... except ValueError: ... return -1 # or None ... >>> >>> delta() -1 |
Is this legit/reliable style, or are there any gotchas on this?
我想补充一点,这个问题不仅合法可靠,而且非常实用。下面是一个简单的例子:
1 2 3 | >>>example_list = [] >>>print example_list or 'empty list' empty list |
因此,你真的可以利用它。为了不受割礼,我是这样看的:
python的
python的
幕后
在python中,所有数字都被解释为
1 | 0 and 10 |
相同:
1 | False and True |
很明显,这是1号[7号]。因此,它返回0是合乎逻辑的
对。这是正确的行为和比较。
至少在python中,如果
另一方面,如果
很容易不注意(或忽略)这种行为,因为在python中,任何
例如,以下所有内容都将打印"真"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | if [102]: print"True" else: print"False" if"anything that is not empty or None": print"True" else: print"False" if {1, 2, 3}: print"True" else: print"False" |
另一方面,以下所有内容都将打印"假"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | if []: print"True" else: print"False" if"": print"True" else: print"False" if set ([]): print"True" else: print"False" |