在 Python 中为 True 定义值时的奇怪行为

Posted

技术标签:

【中文标题】在 Python 中为 True 定义值时的奇怪行为【英文标题】:Strange behavior when defining a value for True in Python 【发布时间】:2013-11-07 11:54:47 【问题描述】:

这不是一个实际问题 - 我只是对我观察到的一些奇怪行为感到好奇,想知道我是否正确理解了“is”运算符。

这是一些可预测的 Python 解释器输出:

>>> True is True
True
>>> (1==1) is True
True

现在让我们定义一个名为 True 的变量:

>>> True = 'abc'
>>> True == 'abc'
True
>>> True is 'abc'
True

对于布尔运算,解释器仍会返回“True”,但布尔运算的结果被认为既不等于 'abc' 也不等于 True。

>>> (1==1)
True
>>> (1==1) is 'abc'
False
>>> (1==1) is True
False

谁能解释这种奇怪的行为?

【问题讨论】:

奇怪的是 True 不是保留关键字(或 Python 有的任何关键字)。 @iamnotmaynard:True 已成为 Python 3 中的关键字,并且赋值会引发 SyntaxError: assignment to keyword 大多数内置插件不是关键字...您可以为几乎所有内置插件重新分配值。幸好它们通常是本地的,所以当一个粗心的程序员在他们的函数中执行它时,它会保持本地化。 @CorleyBrigman:例如id 是一个内置函数,但它经常被重新分配为对象属性(例如在 Django ORM 中)。我不确定它是否“粗心”,因为内置的 id() 除了调试之外没有多大用处。 作为对象属性,没关系 - 这不是 C,您将始终以 self.idobject.id (在命名空间中)访问它。虽然我见过(甚至写过)像for id in list_of_uids: 这样的代码,所以它很容易做到这一点非常好。 【参考方案1】:

为您自己的答案添加更多内容(应该是评论,但是,很长并且需要格式化):

python2.7
...
>>> import __builtin__
>>> id(True)
7744528
>>> id(__builtin__.True)
7744528
>>> True = 'abc'
>>> id(True)
34386540544

id 中的值(本质上)是 Python 中一个对象的内部标识,或者“真实姓名”,如果您愿意的话。 (它实际上是一个转换为整数的 C 指针。)is 测试比较对象身份。

>>> 1==1
True
>>> id(1==1)
7744528

这表明比较的布尔结果是“旧的”True,仍然可以作为 __builtin__.True 使用。

你重新绑定了名字__main__.True(你在解释器>>>提示符下的当前模块是__main__):

>>> True
'abc'
>>> __builtin__.True
True

和:

>>> import __main__
>>> id(__main__.True)
34386540544
>>> __main__.True
'abc'
>>> 

当初学者的 Python 程序编写如下函数时,同样的事情经常发生:

def foo(list):
    ...

list 是一个内置函数,但在函数foo 内部,名称已重新绑定到参数。然后在... 部分的某个地方,他们会得到一个惊喜:

    x = list(y)

他们希望这会调用__builtin__.list,但它会尝试将其局部变量作为函数来调用。

(有可能,但通常不是很好的风格,import __builtin__ 并通过这些名称来调用事物。也可以重新绑定 __builtin__ 名称,但这是一个更糟糕的主意。:-))

【讨论】:

【参考方案2】:

这就是命名空间和隐藏它的交互式控制台。

最初你有正常的True,它是__builtin__模块的一部分。

重新定义True 时,实际上是在当前模块中定义它,在这种情况下,它只是默认的__main__

因此,您实际上有两个不同的对象。 __builtin__.True__main__.True

In [1]: import __builtin__, __main__

In [2]: True = "a bad idea"

In [3]: __main__.True
Out[3]: 'a bad idea'

In [4]: __builtin__.True
Out[4]: True

【讨论】:

解释了如何隐藏它......但如果你愿意,你也可以使用__builtin__.True = 4。所以这并不能完全回答最初的问题。 @CorleyBrigman:我正在解释原始问题的代码中发生了什么。 我只是说它比这更深...你可以做__builtin__.True = 41==1 仍然会返回 True。 builtin 下面的东西指向真实对象,或者它在 python 核心中处理。【参考方案3】:

正如这里经常发生的那样,我想我在输入问题时已经找到了答案。

“True”有两种:一种是布尔值,另一种是名为True的变量;最初,它们彼此相等。这就是为什么像 (1==1) 这样的布尔运算即使在名为 True 的变量已更改时仍然可以返回 True - 它们返回布尔值 True。但是它们不等于“True”变量的新值,它是一个字符串。

【讨论】:

我喜欢这种情况! @JMK 是的,我想,我是第一个回答这个问题的人:P 我没有看到他的回答。 True 基本上是1 加上一个花哨的repr()。 :-) True 不是 1 和花哨的 repr()。它是一个单例的内置值,恰好在某个条件下计算为 1。就解释器而言,使用同样具有这种行为的不同单例值(例如 if Ellipsis: print 3)并没有错,除了明显的晦涩之处使这在风格上令人震惊。从 Ellipsis 示例中可以看出(省略号在 numpy 数组中也有一个不同的切片函数,没有人使用),单例的值不能被认为像 1。例如,比较 True+1Ellipsis+1 isinstance(True, int)

以上是关于在 Python 中为 True 定义值时的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

通过 RegQueryValueEx 和 RegGetValue 获取注册表值时的奇怪行为 [重复]

“错误:整数的输入语法无效:”在 Redshift 表中为 SMALLINT 列插入 NULL 值时?

在 WebMvcTest 中为 Keycloak 加载自定义 SecurityConfig 时的 NPE

在 Objective C 中为自定义类实现委托时的引用计数

尝试修改单个值时,二维列表有奇怪的行为[重复]

NetSuite:启用 Line Item Shipping (MSR) 时的自定义地址字段