奇怪的使用"和" /"或"操作者

时间:2017-10-30 03:16:46

标签: python operators logical-operators

我正在尝试学习python,并且遇到了一些既好又短但又没有意义的代码

上下文是:

def fn(*args):
    return len(args) and max(args)-min(args)

我得到了它正在做的事情,但为什么python会这样做 - 即返回值而不是True / False?

10 and 7-2

返回5.同样,更改和将导致或将导致功能更改。所以

10 or 7 - 2

将返回10.

这是合法/可靠的风格,还是有任何问题?

8 个答案:

答案 0 :(得分:101)

TL; DR

我们首先总结两个逻辑运算符andor的两种行为。这些习语将成为我们下面讨论的基础。

  

and

     

如果有,则返回第一个Falsy值,否则返回最后一个   表达式中的值。

  

or

     

如果有的话,返回第一个Truthy值,否则返回最后一个   表达式中的值。

此行为也在the docs中进行了总结,尤其是在此表中:

enter image description here

唯一返回布尔值的运算符,无论其操作数如何,都是not运算符。

" Tuthiness"和" Truthy"评价

声明

len(args) and max(args) - min(args)

非常 pythonic 简明(并且可以说是不太可读)的说法"如果args不为空,则返回{的结果{1}}",否则返回max(args) - min(args)。通常,它是0表达式的更简洁表示。例如,

if-else

应该(粗略地)翻译成:

exp1 and exp2

或者,相当于

r1 = exp1
if not r1:
    r1 = exp2

r1 = exp1 if exp1 else exp2 exp1是任意python对象,或返回某个对象的表达式。理解逻辑exp2and运算符的用法的关键是理解它们不限于操作或返回布尔值。任何具有真值的对象都可以在这里进行测试。其中包括orintstrlistdicttupleset和用户定义的对象。短路规则仍然适用。

但是什么是真实性?
它指的是在条件表达式中使用对象的方式。 @Patrick Haugh在this post中很好地总结了真实性。

  

所有价值观都被视为"真实的"以下是其中之一   " falsy":

     
      
  • NoneType
  •   
  • None
  •   
  • False
  •   
  • 0
  •   
  • 0.0
  •   
  • 0j
  •   
  • Decimal(0)
  •   
  • Fraction(0, 1) - 空[]
  •   
  • list - 空{}
  •   
  • dict - 空()
  •   
  • tuple - 空''
  •   
  • str - 空b''
  •   
  • bytes - 空set()
  •   
  • set,例如range
  •   
  • 对象      
        
    • range(0)返回obj.__bool__()
    •   
    • False返回obj.__len__()
    •   
  •   
     

A" truthy"值将满足0if执行的检查   声明。我们使用" truthy"和" falsy"区别于   whileboolTrue

False工作原理

我们以OP的问题为基础,讨论这些运营商在这些情况下的运作方式。

  

给定定义

的函数
and
     

如何返回最小值和最大值之间的差异   在零个或多个参数的列表中?

查找最小值和最大值很简单(使用内置函数!)。这里唯一的障碍是适当地处理参数列表可能为空的极端情况(例如,调用def foo(*args): ... )。由于foo()运算符,我们可以在一行中完成这两项操作:

and

def foo(*args):
     return len(args) and max(args) - min(args)

由于使用foo(1, 2, 3, 4, 5) # 4 foo() # 0 ,如果第一个表达式为and,则还必须评估第二个表达式。请注意,如果第一个表达式被评估为真实,则返回值始终 第二个表达式的结果。如果第一个表达式被评估为Falsy,则返回的结果是第一个表达式的结果。

在上面的函数中,如果True收到一个或多个参数,foo大于len(args)(正数),则返回的结果为0。 OTOH,如果没有传递参数,max(args) - min(args)len(args),这是Falsy,并且会返回0

请注意,编写此函数的另一种方法是:

0

或者,更简洁,

def foo(*args):
    if not len(args):
        return 0

    return max(args) - min(args)

如果当然,这些函数都不执行任何类型检查,因此除非您完全信任所提供的输入,否则依赖于这些结构的简单性。

def foo(*args): return 0 if not args else max(args) - min(args) 工作原理

我用一个人为的例子以类似的方式解释or的工作。

  

给定定义

的函数
or
     

您如何完成def foo(*args): ... 以返回foo以上的所有数字?

我们使用9000来处理角落案例。我们将or定义为:

foo

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!' 对列表执行过滤以保留foo以上的所有数字。如果存在任何这样的数字,列表推导的结果是一个非空的列表,它是Truthy,因此它被返回(这里的短路动作)。如果不存在这样的数字,则列表comp的结果是9000,这是Falsy。所以现在评估第二个表达式(非空字符串)并返回。

使用条件,我们可以将此函数重写为,

[]

和以前一样,这种结构在错误处理方面更加灵活。

答案 1 :(得分:13)

引自Python Docs

  

请注意,andor 限制 类型都不会返回   到FalseTrue,而是返回最后评估的参数。这个   有时是有用的,例如,s是一个应该被替换的字符串   如果为空,则为默认值,表达式s or 'foo'将生成   期望值。

因此,这就是Python设计用于评估布尔表达式的方法,上面的文档让我们深入了解了为什么这样做。

要获取布尔值,只需对其进行类型转换。

return bool(len(args) and max(args)-min(args))

为什么?

短路。

例如:

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

同样适用于or,也就是说,一旦找到它就会返回 Truthy 的表达式,因为评估表达式的其余部分是多余的。 / p>

Python不会返回核心TrueFalse,而是返回 Truthy Falsey ,无论如何都要评估为{{1} }或True。您可以按原样使用表达式,它仍然有效。

要知道什么是 Truthy Falsey ,请检查Patrick Haugh's answer

答案 2 :(得分:7)

以及执行布尔逻辑,但在比较时会返回其中一个实际值。使用时,将从左到右在布尔上下文中计算值。布尔上下文中 0,'',[],(),{},为false;其他一切都是真的。

如果布尔上下文中的所有值都为真,将返回最后一个值。

>>> 2 and 5
5
>>> 2 and 5 and 10
10

如果布尔上下文中的任何值为false,则将返回第一个false值。

>>> '' and 5
''
>>> 2 and 0 and 5
0

所以代码

return len(args) and max(args)-min(args)
args 时,

返回max(args)-min(args)的值,否则返回len(args)为0。

答案 3 :(得分:5)

  

这是合法/可靠的风格,还是有任何问题?

这是合法的,它是short circuit evaluation,其中返回最后一个值。

你提供了一个很好的例子。如果没有传递参数,函数将返回0,并且代码不必检查是否传递了没有参数的特殊情况。

另一种使用它的方法是默认无参数可变基元,如空列表:

def fn(alist=None):
    alist = alist or []
    ....

如果某个非真实值传递给alist,则默认为空列表,方便避免if语句和mutable default argument pitfall

答案 4 :(得分:3)

陷阱

是的,有一些问题。

fn() == fn(3) == fn(4, 4)

首先,如果fn返回0,您无法知道是否在没有任何参数的情况下调用它,使用一个参数或使用多个相等的参数:

>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0

fn是什么意思?

然后,Python是一种动态语言。它没有指定fn做什么,它的输入应该是什么以及它的输出应该是什么样的。因此,正确命名函数非常重要。同样,参数不必称为argsdelta(*numbers)calculate_range(*numbers)可能会更好地描述该功能应该做什么。

参数错误

最后,逻辑and运算符应该在没有任何参数的情况下调用时阻止函数失败。如果某些参数不是数字,它仍然会失败:

>>> 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'

可能的替代

这是根据"Easier to ask for forgiveness than permission." principle编写函数的方法:

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

举个例子:

>>> 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

如果你真的不想在没有任何参数的情况下调用delta时引发异常,你可以返回一些其他值无法实现的值(例如-1None) :

>>> 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

答案 5 :(得分:0)

是。这是正确的行为和比较。

至少在Python中,A and B如果B基本上是A则返回True,包括A是否为空,不是None NOT空容器(例如空listdict等)。 {I} A基本上是AFalse或空或空,我会返回None

另一方面,A or B如果A基本上是A,则返回True,包括A是否为空,不是None NOT空容器(例如空listdict等),否则返回B

很容易忽略(或忽略)这种行为,因为在Python中,任何non-null非空对象求值为True都被视为布尔值。

例如,以下所有内容都将打印&#34; True&#34;

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"

另一方面,以下所有内容将打印&#34; False&#34;

if []: 
    print "True"
else: 
    print "False"

if "": 
    print "True"
else: 
    print "False"

if set ([]): 
    print "True"
else: 
    print "False"

答案 6 :(得分:0)

  

这是合法/可靠的风格,还是有任何问题?

我想补充一点,这个问题不仅合法可靠,而且非常实用。这是一个简单的例子:

>>>example_list = []
>>>print example_list or 'empty list'
empty list

因此,您可以真正使用它。为了让人知道这是我的看法:

Or运营商

Python的or运算符返回第一个Truth-y值或最后一个值,然后停止

And运营商

Python的and运算符返回第一个False-y值或最后一个值,然后停止

幕后花絮

在python中,除了0之外,所有数字都被解释为True。因此,请说:

0 and 10 

与:

相同
False and True

显然False。因此,它返回0

是合乎逻辑的

答案 7 :(得分:0)

以简单的方式理解

AND: if first_val is False return first_val else second_value

例如:

1 and 2 # here it will return 2 because 1 is not False

但是

0 and 2 # will return 0 because first value is 0 i.e False

和=> 如果任何人为假,则为假。如果两者都为真,那么只有它会变为真

OR: if first_val is False return second_val else first_value

原因是,如果first为false,则检查2是否为真。

例如:

1 or 2 # here it will return 1 because 1 is not False

但是

0 or 2 # will return 2 because first value is 0 i.e False

或=> ,如果任何人为假,则为true。因此,如果第一个值是假,那么无论假设哪个是第二个值。 因此它会返回第二个值。

如果任何人是真实的,那么它将成为真实。如果两者都为假,则它将为假。