我正在尝试学习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。
这是合法/可靠的风格,还是有任何问题?
我们首先总结两个逻辑运算符and
和or
的两个行为。这些习语将成为我们下面讨论的基础。
and
如果有则返回第一个Falsy值,否则返回表达式中的最后一个值。
or
如果有的话,返回第一个Truthy值,否则返回表达式中的最后一个值。
行为也在the docs中进行了总结,特别是在此表中:
无论操作数如何,唯一返回布尔值的运算符是not
运算符。
该声明
len(args) and max(args) - min(args)
是一个非常
Python的
简洁(可以说是不太可读)的方式说“如果args
不是空的,返回max(args) - min(args)
的结果”,否则返回0
。一般来说,它是if-else
表达式的更简洁的表示。例如,
exp1 and exp2
应该(粗略地)翻译成:
r1 = exp1
if not r1:
r1 = exp2
或者,等效地,
r1 = exp1 if exp1 else exp2
其中exp1
和exp2
是任意python对象,或返回某些对象的表达式。理解逻辑and
和or
运算符的用法的关键是理解它们不限于操作或返回布尔值。任何具有真值的对象都可以在这里进行测试。这包括int
,str
,list
,dict
,tuple
,set
,NoneType
和用户定义的对象。短路规则仍然适用。
但是什么是真实性? 它指的是在条件表达式中使用对象的方式。 @Patrick Haugh在this post很好地总结了真实性。
所有值都被认为是“真实的”,除了以下是“假的”:
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__()
返回0
“truthy”值将满足
if
或while
声明所执行的检查。我们使用“truthy”和“falsy”来区分bool
值True
和False
。
and
Works我们以OP的问题为基础,讨论这些运算符在这些情况下的运作方式。
给定定义的函数
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
,如果第一个表达式是True
,则还必须评估第二个表达式。请注意,如果第一个表达式被评估为真实,则返回值始终是第二个表达式的结果。如果第一个表达式被评估为Falsy,则返回的结果是第一个表达式的结果。
在上面的函数中,如果foo
收到一个或多个参数,则len(args)
大于0
(正数),因此返回的结果是max(args) - min(args)
。 OTOH,如果没有参数传递,len(args)
是0
,这是Falsy,并返回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
Works我用一个人为的例子以类似的方式解释了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
对名单进行过滤以保留9000
上的所有数字。如果存在任何这样的数字,列表推导的结果是一个非空的列表,它是Truthy,因此它被返回(这里的短路动作)。如果不存在这样的数字,则列表comp的结果是[]
,这是Falsy。因此,现在评估第二个表达式(非空字符串)并返回。
使用条件,我们可以重写这个函数,
def foo(*args):
r = [x for x in args if x > 9000]
if not r:
return 'No number over 9000!'
return r
和以前一样,这种结构在错误处理方面更加灵活。
请注意,
and
和or
都不会限制它们返回False
和True
的值和类型,而是返回最后一个评估的参数。这有时是有用的,例如,如果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的表达式,因为评估表达式的其余部分是多余的。
Python不会返回硬核True
或False
,而是返回Truthy或Falsey,无论如何都要评估为True
或False
。您可以按原样使用表达式,它仍然可以使用。
要知道什么是Truthy和Falsey,请查看Patrick Haugh's answer
和/或执行布尔逻辑,但它们在比较时返回一个实际值。使用和时,从左到右在布尔上下文中计算值。布尔上下文中0,'',[],(),{}和None为false;其他一切都是真的。
如果布尔上下文中的所有值都为true,则返回最后一个值。
>>> 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。
这是合法/可靠的风格,还是有任何问题?
这是合法的,它是一个short circuit evaluation,其中返回最后一个值。
你提供了一个很好的例子。如果没有传递参数,该函数将返回0
,并且代码不必检查是否传递了没有参数的特殊情况。
使用它的另一种方法是默认无参数可变基元,如空列表:
def fn(alist=None):
alist = alist or []
....
如果将一些非真实值传递给alist
,则默认为空列表,方便避免使用if
语句和mutable default argument pitfall
是的,有一些陷阱。
fn() == fn(3) == fn(4, 4)
首先,如果fn
返回0
,您无法知道它是否在没有任何参数的情况下被调用,具有一个参数或具有多个相等的参数:
>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0
fn
是什么意思?然后,Python是一种动态语言。它没有指定fn
做什么,它的输入应该是什么以及它的输出应该是什么样的。因此,正确命名函数非常重要。同样,参数不必称为args
。 delta(*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
时引发异常,你可以返回一些其他方法无法实现的值(例如-1
或None
):
>>> 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
这是合法/可靠的风格,还是有任何问题?
我想补充一点,这个问题不仅合法可靠,而且非常实用。这是一个简单的例子:
>>>example_list = []
>>>print example_list or 'empty list'
empty list
因此,你可以真正利用它。为了让人知道这是我的看法:
Or
运营商
Python or
运算符返回第一个Truthy值或最后一个值,然后停止
And
运营商
Python的and
运算符返回第一个False-y值或最后一个值,然后停止
在幕后
在python中,除了0之外,所有数字都被解释为True
。因此,说:
0 and 10
是相同的:
False and True
这显然是False
。因此,它返回0是合乎逻辑的
是。这是正确的行为和比较。
至少在Python中,A and B
返回B
,如果A
基本上是True
,包括如果A
不是空的,不是None
不是空容器(例如空的list
,dict
等)。 A
返回IFF A
基本上是False
或None
或空或Null。
另一方面,A or B
返回A
,如果A
基本上是True
,包括如果A
不是空,而不是None
不是空容器(如空的list
,dict
等),否则它返回B
。
很容易不注意(或忽略)这种行为,因为在Python中,任何non-null
非空对象求值为True都被视为布尔值。
例如,以下所有内容都将打印为“True”
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"
另一方面,以下所有将打印“假”
if []:
print "True"
else:
print "False"
if "":
print "True"
else:
print "False"
if set ([]):
print "True"
else:
print "False"
以简单的方式理解,
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
和=>如果有人假,那将是假的。如果两者都是真的那么只有它才会成真
或者:if first_val is False return second_val else first_value
原因是,如果第一个是假的,则检查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
或=>如果有人假,那将是真的。因此,如果第一个值为假,则无论假定的值是多少。所以它会返回第二个值。
如果有人是真的那么它将成为现实。如果两者都是假的,那么它将变为假。