【问题标题】:Use of True, False, and None as return values in Python functions在 Python 函数中使用 True、False 和 None 作为返回值
【发布时间】:2012-03-18 15:51:08
【问题描述】:

我认为我完全理解这一点,但我只是想确定一下,因为我一直看到人们说永远不要测试 TrueFalseNone

他们建议例程应该引发错误而不是返回 False 或 None。无论如何,我有很多情况我只是想知道是否设置了标志,因此我的函数返回 True 或 False。在其他情况下,如果没有有用的结果,我有一个函数 return None。根据我的想法,只要我意识到我永远不应该使用,这两者都不是问题:

if foo == True
if foo == False
if foo == None

应该改用:

if foo is True
if foo is False
if foo is None

因为 True、False 和 None 都是单例,并且在使用“is”而不是“==”时总是会评估我所期望的方式。我错了吗?

按照同样的思路,修改有时返回 None 的函数以便它们引发错误是否更符合 Python 风格?

假设我有一个名为“get_attr()”的实例方法,它从某个文件中检索属性。在发现我请求的属性不存在的情况下,返回None是否合适?让他们提出错误并在以后捕获它会更好吗?

【问题讨论】:

    标签: python


    【解决方案1】:

    建议并不是说您永远不要使用TrueFalseNone。只是你不应该使用if x == True

    if x == True 很傻,因为== 只是一个二元运算符!它的返回值为TrueFalse,具体取决于其参数是否相等。如果condition 为真,if condition 将继续。因此,当您编写if x == True 时,Python 将首先评估x == True,如果xTrueFalse,则它将变为True,否则,如果结果为真则继续。但是,如果您希望 xTrueFalse,为什么不直接使用 if x

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

    在某些情况下,您可能需要使用x == True。这是因为if 语句条件是“在布尔上下文中评估”以查看它是否“真实”,而不是完全针对True 进行测试。例如,非空字符串、列表和字典都被 if 语句视为真值,以及非零数值,但它们都不等于 True。因此,如果您想测试任意值是否完全True,而不仅仅是它是否真实,何时使用if x == True。但我几乎从来没有看到它的用途。这种情况非常罕见,如果您确实需要写它,那么值得添加评论,这样未来的开发人员(可能包括您自己)就不会仅仅认为== True 是多余的并将其删除。 p>


    使用x is True 实际上更糟。永远不要将is 与基本的内置不可变类型一起使用,例如布尔值(TrueFalse)、数字和字符串。原因是对于这些类型,我们关心的是,而不是身份== 测试这些类型的值是否相同,而is 始终测试身份。

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

    >>> stars1 = ''.join('*' for _ in xrange(100))
    >>> stars2 = '*' * 100
    >>> stars1 is stars2
    False
    >>> stars1 == stars2
    True
    

    编辑:所以事实证明 Python 在布尔值上的相等性有点出乎意料(至少对我来说):

    >>> True is 1
    False
    >>> True == 1
    True
    >>> True == 2
    False
    >>> False is 0
    False
    >>> False == 0
    True
    >>> False == 0.0
    True
    

    正如the notes when bools were introduced in Python 2.3.5 中所解释的,这样做的基本原理是使用整数 1 和 0 来表示 True 和 False 的旧行为是好的,但我们只是想要更多描述性的名称来表示我们打算表示真值的数字。

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

    因此,当 x 可能由一些期望“True 只是拼写 1 的另一种方式”的代码设置时使用 x is True 是错误的,因为有很多方法可以构造等于 @ 的值987654380@但与它的身份不同:

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

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

    更可靠的方法是x == True and type(x) is bool。但是对于一个非常晦涩的案例来说,这变得非常冗长。通过进行显式类型检查,它看起来也不是很 Pythonic……但这确实是您尝试精确测试 True 而不是真话时所做的;鸭子类型的方式是接受真值并允许任何用户定义的类声明自己是真值。

    如果您正在处理这种极其精确的真理概念,您不仅不认为非空集合为真,而且也不认为 1 为真,那么只使用 x is True 可能没问题, 因为大概你知道x 不是来自认为 1 为真的代码。我认为没有任何纯 python 方法可以提出另一个位于不同内存地址的True(尽管您可能可以从 C 中做到这一点),因此尽管理论上是“错误的”,但它永远不会中断" 要做的事情。

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

    结束编辑


    但是,对于None,习惯用法是使用if x is None。在许多情况下,您可以使用if not x,因为Noneif 语句的“错误”值。但最好仅在您想以相同方式处理所有虚假值(零值数字类型、空集合和None)时才这样做。如果您正在处理的值要么是某个可能的其他值,要么是 None 以指示“无值”(例如当函数在失败时返回 None),那么要好得多使用if x is None,这样您就不会在函数恰好返回一个空列表或数字 0 时意外假设该函数失败。

    对于不可变值类型使用== 而不是is 的论点建议您应该使用if x == None 而不是if x is None。但是,在None 的情况下,Python 确实明确保证整个宇宙中只有一个None,并且正常的惯用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,这是证明规则的例外)。
    • > 原因是对于这些类型,我们关心的是值,而不是身份。 >>> False == 0 -> True >>> False is 0 -> False。在某些情况下,我想确保函数返回False 而不是0,所以我不能使用if a() == False
    • @Vorticity 是的,直的if x 将采用与True 相同的路径13。如果您需要将它们区分开来,那么您需要针对True 进行显式测试。但请注意,例如False or 3 返回 3,而不是 Trueandor 的约定是他们会适当地返回一个真值或假值,而不是他们总是给你TrueFalse
    • 我在某处读到 True 和 False 对象是独一无二的,所以我认为使用 x is True 应该没问题...
    • > 用一句话总结 True 和 False:它们是拼写整数值 1 和 0 的替代方法 docs.python.org/release/2.3.5/whatsnew/section-bool.html 我认为它不适合使用 True/False喜欢if x is True,但没有什么能阻止你。
    【解决方案2】:

    使用if fooif not foo。不需要==is

    为了检查无,建议使用is Noneis not None。这使您可以将其与 False(或评估为 False 的事物,例如 ""[])区分开来。

    get_attr 是否应该返回 None 取决于上下文。您可能有一个值为 None 的属性,而您将无法做到这一点。我会将None 解释为“未设置”,KeyError 表示文件中不存在密钥。

    【讨论】:

    • 感谢您的回答和关于“if foo”的建议。我想我总是出于习惯避免它,因为您指出的正是“没有”。我从来没有真正想过“如果 foo 为真”过长。
    【解决方案3】:

    如果检查真相:

    if foo
    

    为假:

    if not foo
    

    无:

    if foo is None
    

    对于非无:

    if foo is not None
    

    对于getattr(),正确的行为是返回None,而是引发AttributError错误——除非你的类类似于defaultdict

    【讨论】:

      【解决方案4】:

      关于是引发异常还是返回None:这取决于用例。两者都可以是 Pythonic。

      以 Python 的 dict 类为例。 x[y] 挂钩到 dict.__getitem__,如果密钥不存在,它会引发 KeyError。但是如果 key 不存在,dict.get 方法会返回第二个参数(默认为 None)。 它们都很有用。

      要考虑的最重要的事情是在文档字符串中记录该行为,并确保您的 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
      

      返回 TrueFalse 的函数可能应该有一个名称,以提高代码的可读性:

      def is_running_on_windows():
          return os.name == 'nt'
      

      在 Python 3 中,您可以“输入提示”:

      >>> def is_running_on_windows() -> bool:
      ...     return os.name == 'nt'
      ... 
      >>> is_running_on_windows.__annotations__
      {'return': bool}
      

      【讨论】:

        【解决方案5】:

        您可以直接检查变量是否包含值,例如if varnot var

        【讨论】:

          【解决方案6】:

          PEP 8 (Style Guide for Python Code) document 的示例中,我看到foo is Nonefoo is not None 被用来代替foo == Nonefoo != None

          在本文档中建议使用if boolean_value,而不是if boolean_value == Trueif boolean_value is True。所以我认为这是否是官方的 Python 方式。我们 Python 人也应该这样走下去。

          【讨论】:

            【解决方案7】:

            对于您虚构的getattr 函数,如果请求的属性应该始终可用但不可用,则抛出错误。如果属性是可选的,则返回None

            【讨论】:

              【解决方案8】:

              为真,不是无:

              if foo:
              

              为假,无:

              if not foo:
              

              【讨论】:

                【解决方案9】:

                要确保的一件事是没有任何东西可以重新分配您的变量。如果最终不是布尔值,依赖真实性会导致错误。动态类型语言中条件编程的美妙之处:)。

                以下打印“no”。

                x = False
                if x:
                    print 'yes'
                else:
                    print 'no'
                

                现在让我们改变 x

                x = 'False'
                

                现在语句打印“yes”,因为字符串是真实的。

                if x:
                    print 'yes'
                else:
                    print 'no'
                

                但是,此语句正确输出“no”。

                if x == True:
                    print 'yes'
                else:
                    print 'no'
                

                【讨论】:

                  猜你喜欢
                  • 2021-07-02
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-06-25
                  • 2020-10-17
                  • 1970-01-01
                  • 1970-01-01
                  • 2022-06-28
                  • 1970-01-01
                  相关资源
                  最近更新 更多