在python函数中使用True,False和None作为返回值

Vor*_*ity 39 python

我认为我完全理解这一点,但我只是想确保,因为我总是看到别人说绝对不要测试反对True,FalseNone.他们建议例程应该引发错误而不是返回False或None.无论如何,我有很多情况我只想知道是否设置了一个标志,所以我的函数返回True或False.如果没有有用的结果,还有其他情况我有一个函数返回None.从我的想法来看,只要我意识到我永远不应该使用,就不会有问题:

if foo == True
if foo == False
if foo == None
Run Code Online (Sandbox Code Playgroud)

而应该使用:

if foo is True
if foo is False
if foo is None
Run Code Online (Sandbox Code Playgroud)

因为True,False和None都是单例,并且总是会在使用"is"而不是"=="时评估我的预期方式.我错了吗?

沿着相同的路线,修改有时返回None的函数会更加pythonic,以便它们引发错误吗?假设我有一个名为"get_attr()"的实例方法,它从某个文件中检索属性.如果它发现我请求的属性不存在则返回None是否合适?让他们提出错误并在以后捕获它会更好吗?

Ben*_*Ben 63

该建议是不是说你不应该使用 True,FalseNone.只是你不应该使用if x == True.

if x == True是愚蠢的,因为==它只是一个二元运算符!它的返回值为True或者False,取决于它的参数是否相等.if condition如果condition是真的,将继续进行.因此,当您编写if x == TruePython时,将首先进行评估x == True,True如果xTrueFalse否则将成为,然后如果结果为真则继续.但是,如果你希望x成为无论是TrueFalse,为什么不使用if x直接!

同样,x == False通常可以替换为not x.

在某些情况下您可能想要使用x == True.这是因为if声明条件是"在布尔上下文中计算",以查看它是否"真实"而不是完全反对True.例如,非空字符串,列表和字典都被if语句认为是真实的,以及非零数字值,但这些都不等于True.因此,如果您想测试一个任意值是否正好是该值True,而不仅仅是它是否真实,何时使用if x == True.但我几乎从来没有看到过这方面的用途.如果你真的需要写这篇文章,这是非常罕见的,值得添加评论,以便未来的开发人员(包括可能你自己)不要只是假设它== True是多余的并删除它.


x is True相反,使用实际上更糟糕.永远不要使用is基本的内置不可变类型,如布尔(True,False),数字和字符串.原因是对于这些类型我们关心的是价值观,而不是身份.==测试这些类型的值是否相同,同时is始终测试身份.

测试身份而不是值是不好的,因为实现理论上可以构造新的布尔值而不是找到现有的布尔值,从而导致您有两个True具有相同值但存储在内存中的不同值且具有不同身份的值.在实践中我很确定True并且False总是被Python解释器重用,所以这不会发生,但这确实是一个实现细节.这个问题一直伴随着字符串,因为直接出现在程序源中的短字符串和文字字符串被Python回收,所以'foo' is 'foo'总是返回True.但是以两种不同的方式构造相同的字符串很容易,Python让它们具有不同的身份.请注意以下事项:

>>> stars1 = ''.join('*' for _ in xrange(100))
>>> stars2 = '*' * 100
>>> stars1 is stars2
False
>>> stars1 == stars2
True
Run Code Online (Sandbox Code Playgroud)

编辑:事实证明,Python在布尔值上的平等有点出乎意料(至少对我而言):

>>> True is 1
False
>>> True == 1
True
>>> True == 2
False
>>> False is 0
False
>>> False == 0
True
>>> False == 0.0
True
Run Code Online (Sandbox Code Playgroud)

正如在Python 2.3.5中引入bool的注释中所解释的那样,基本原理是使用整数1和0来表示True和False的旧行为是好的,但我们只是想要更多描述性的名称来表示我们想要的数字代表真理价值观.

实现这一目标的一种方法就是简单地拥有True = 1False = 0内置; 然后1和True真的无法区分(包括is).但这也意味着返回的函数True1在交互式解释器中显示,因此所做的是将其创建bool为子类型int.唯一不同的boolstrrepr; bool实例仍然具有与int实例相同的数据,并且仍以相同的方式比较相等性,因此True == 1.

因此,x is Truex某些代码期望"True只是拼写1的另一种方式" 时,使用它是错误的,因为有很多方法可以构造等于True但不具有相同身份的值:

>>> a = 1L
>>> b = 1L
>>> c = 1
>>> d = 1.0
>>> a == True, b == True, c == True, d == True
(True, True, True, True)
>>> a is b, a is c, a is d, c is d
(False, False, False, False)
Run Code Online (Sandbox Code Playgroud)

x == True当使用x可能是任意Python值并且您只想知道它是否是布尔值时,使用它是错误的True.我们唯一确定的是,x当你只是想测试"真实性"时,只使用它是最好的.值得庆幸的是,这通常都是必需的,至少在我写的代码中!

一个更确定的方式x == True and type(x) is bool.但是对于一个相当模糊的案例来说,这个问题变得非常冗长.它通过显式类型检查看起来也不像Pythonic ......但是当你试图精确测试True而不是真正的时候,这就是你正在做的事情.duck typing方式是接受truthy值并允许任何用户定义的类声明自己是真实的.

如果你正在处理这个非常精确的真理概念,你不仅不认为非空集合是真的,而且也不认为1是真的,那么只是使用x is True可能是好的,因为大概你知道这x不是来自认为1是真实的代码.我不认为有任何纯粹的python方式来提出另一个True生活在不同的内存地址(尽管你可能从C开始),所以这不应该打破,尽管理论上是"错误"的事情做.

而我以前认为布尔很简单!

结束编辑


None然而,在使用成语的情况下if x is None.在许多情况下你可以使用if not x,因为它None是一个if声明的"假"值.但是,如果您想要以None相同的方式处理所有虚假值(零值数字类型,空集合和),则最好只执行此操作.如果您正在处理某个可能的其他值或None表示"无值"的值(例如函数None在失败时返回),那么使用它更好,if x is None这样您就不会意外地认为函数失败了它恰好返回一个空列表,或数字0.

我使用==而不是is不可变值类型的论据表明你应该使用if x == None而不是if x is None.但是,在NonePython 的情况下,确实明确保证None整个Universe中只有一个,并且正常的惯用Python代码使用is.


关于是否返回None或提出异常,取决于具体情况.

对于像你的get_attr例子这样的东西我会期望它引发一个例外,因为我会打电话给它do_something_with(get_attr(file)).调用者的正常期望是他们将获得属性值,并让他们得到None并假设属性值比忘记处理异常更危险,如果属性不能是你可以实际继续找到.另外,返回None以指示失败意味着None该属性不是有效值.在某些情况下这可能是个问题.

对于一个虚构的函数see_if_matching_file_exists,我们提供一个模式,并检查几个地方以查看是否有匹配,如果找到一个匹配或None不匹配,它可以返回匹配.但另外它可以返回一个匹配列表; 然后没有匹配只是空列表(这也是"假的";这是我只是if x用来看看我有什么回来的情况之一).

因此,当在异常之间进行选择并None指示失败时,您必须确定是否None是预期的非失败值,然后查看调用该函数的代码的期望.如果"正常"期望是返回有效值,并且只是偶尔调用者能够正常工作,无论是否返回有效值,那么您应该使用异常来指示失败.如果没有有效值是很常见的,那么调用者将期望处理这两种可能性,然后你可以使用None.

  • @Vorticity:说实话,Python很可能*确保它们的id总是相同的,即使它不能保证我99%确定它们在实践中总是一样的.因此,如果您有更好的事情要做(比如编写新代码:),我不一定急于编辑所有代码中的`x is True`.但通常你只想使用带有可变对象的`is`测试(或者`None`,这是证明规则的例外). (2认同)
  • @Vorticity是的,一个直的`if x`将采用与'13'相同的路径和'True`.如果你需要区分它们,那么你需要明确地测试`True`.但要注意,例如`False或3`返回`3`,而不是'True`!"和"和"或"的契约是它们会适当地返回一个真实或虚假的值,而不是它们总是会给你"真"或"假". (2认同)
  • 我在某处读到True和False对象是唯一的,所以我认为使用`x is True`应该可以... (2认同)

str*_*cat 13

使用if fooif not foo,不需要任何一个==is那个.

对于核对无,is Noneis not None建议.这允许您将其与False(或评估为False的事物,例如""[])区分开来.

是否get_attr应该返回None将取决于上下文.您可能有一个值为None的属性,您将无法执行此操作.我会将其解释None为"未设置",并且KeyError意味着密钥在文件中不存在.


Kim*_*ais 7

如果检查真相:

if foo
Run Code Online (Sandbox Code Playgroud)

为假:

if not foo
Run Code Online (Sandbox Code Playgroud)

没有:

if foo is None
Run Code Online (Sandbox Code Playgroud)

对于非人:

if foo is not None
Run Code Online (Sandbox Code Playgroud)

因为getattr()正确的行为不是返回None而是引发AttributError错误 - 除非你的类是类似的defaultdict


wim*_*wim 6

关于是否提出异常或返回None:它取决于用例.两者都可以是pythonic.dict比如python的类,x[y]挂钩dict.__getitem__并且它会引发一个KeyErrorif键不存在.但是如果key不存在,dict.get方法将返回第二个参数(默认为None). 它们都很有用.

要考虑的最重要的事情是在docstring中记录该行为,并确保您的get_attr()方法完成它所说的功能.

要解决您的其他问题,请使用以下约定:

if foo:
    # For testing truthiness

if not foo:
    # For testing falsiness

if foo is None:
    # Testing .. Noneliness ?

if foo is not None:
    # Check explicitly avoids common bugs caused by empty sequences being false
Run Code Online (Sandbox Code Playgroud)

返回的函数TrueFalse应该具有一个名称,使其显而易见,以提高代码的可读性

def is_running_on_windows():
    return os.name == 'nt'
Run Code Online (Sandbox Code Playgroud)

在python3中你可以"输入提示":

>>> def is_running_on_windows() -> bool:
...     return os.name == 'nt'
... 
>>> is_running_on_windows.__annotations__
{'return': bool}
Run Code Online (Sandbox Code Playgroud)