栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

“ is”运算符对非缓存整数的行为异常

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

“ is”运算符对非缓存整数的行为异常

tl; dr:

如参考手册所述:

块是作为单元执行的一段Python程序文本。以下是块:模块,函数体和类定义。 交互键入的每个命令都是一个块。

这就是为什么,在功能的情况下,你有一个 单一的 代码模块包含一个 单一 的数字文本对象

1000
,所以
id(a) ==id(b)
会产生
True

在第二种情况下,您有 两个不同的代码对象, 每个 对象 都有它们自己的文字对象

1000
so
id(a) != id(b)

请注意,这种行为并不仅

int
限于文字,您还会得到类似的结果,例如,
float
文字。

当然,比较对象(除显式

is None
测试外)应始终使用等于运算符
==
不是
is

这里所说的一切都适用于最流行的Python CPython实现。 其他实现可能会有所不同,因此在使用它们时不做任何假设。


更长的答案:

为了获得更清晰的视图并验证这种 看似奇怪的
行为,我们可以

pre
使用
dis
模块针对每种情况直接在对象中查找。

对于功能

func

除所有其他属性外,函数对象还具有一个

__pre__
属性,可让您查看该函数的已编译字节码。使用
dis.pre_info
我们可以获得给定功能的代码对象中所有存储的属性的漂亮视图:

>>> print(dis.pre_info(func))Name:   funcFilename:          <stdin>Argument count:    0Kw-only arguments: 0Number of locals:  2Stack size:        2Flags:  OPTIMIZED, NEWLOCALS, NOFREEConstants:   0: None   1: 1000Variable names:   0: a   1: b

我们只对

Constants
function的条目感兴趣
func
。在其中,我们可以看到我们有两个值
None
(始终存在)和
1000
。我们只有一个
单一的 ,代表的整型实例
1000
。这是该值
a
b
将要分配被调用函数时。

可以很容易地访问此值

func.__pre__.co_consts[1]
,因此,
a is b
在函数中查看我们的评估的另一种方式如下:

>>> id(func.__pre__.co_consts[1]) == id(func.__pre__.co_consts[1])

当然,

True
由于我们指的是同一对象,因此得出的结果是。

对于每个交互式命令:

如前所述,每个交互式命令都被解释为单个代码块:分别进行分析,编译和评估。

我们可以通过

compile
内置的命令获取每个命令的代码对象:

>>> com1 = compile("a=1000", filename="", mode="single")>>> com2 = compile("b=1000", filename="", mode="single")

对于每个赋值语句,我们将获得一个外观类似的代码对象,如下所示:

>>> print(dis.pre_info(com1))Name:   <module>Filename:          Argument count:    0Kw-only arguments: 0Number of locals:  0Stack size:        1Flags:  NOFREEConstants:   0: 1000   1: NoneNames:   0: a

同样的命令

com2
看起来一样,但 有一个根本的区别
:每个代码的对象
com1
,并
com2
具有代表字面不同INT实例
1000
。这就是为什么在这种情况下,当我们
a isb
通过
co_consts
参数进行操作时,实际上得到:

>>> id(com1.co_consts[0]) == id(com2.co_consts[0])False

这与我们实际得到的一致。

不同的代码对象,不同的内容。


注意: 我对源代码中到底发生了什么感到有些好奇,在深入研究后,我相信我终于找到了它。

在编译阶段,该

co_consts

属性由字典对象表示。在
compile.c
实际上我们可以看到初始化:

u->u_lineno = 0;u->u_col_offset = 0;u->u_lineno_set = 0;u->u_consts = PyDict_New();

在编译期间,将检查是否已存在常量。有关更多信息,请参见下面的@Raymond
Hettinger的答案。


注意事项:

  • 链接的语句将评估为的身份检查
    True

现在应该更清楚地说明以下内容为何准确地得出

True

    >>> a = 1000; b = 1000;>>> a is b

在这种情况下,通过将两个分配命令链接在一起,我们告诉解释器将它们 一起
编译。与功能对象的情况一样,将只为文字

1000
创建一个对象,并
True
在评估时产生一个值。

  • 在模块级别执行将
    True
    再次产生:

如前所述,参考手册指出:

…以下是块: 一个模块

因此,适用相同的前提:我们将有一个代码对象(用于模块),因此,将为每个不同的文字存储单个值。

  • 同样 适用于 可变 对象:

这意味着除非我们显式初始化为同一可变对象(例如,使用a = b = []),否则对象的身份永远不会相等,例如:

    a = []; b = []a is b  # always returns false

同样,在文档中,指定了以下内容:

在a = 1之后;b = 1,取决于实现,a和b可以或可以不使用值1引用同一对象,但是在c = []之后;d =
[],保证c和d引用两个不同的,唯一的,新创建的空列表。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/640344.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号