Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 'is'运算符对非缓存整数的行为异常_Python_Python 3.x_Integer_Identity_Python Internals - Fatal编程技术网

Python 'is'运算符对非缓存整数的行为异常

Python 'is'运算符对非缓存整数的行为异常,python,python-3.x,integer,identity,python-internals,Python,Python 3.x,Integer,Identity,Python Internals,在使用Python解释器时,我偶然发现了关于is操作符的一个冲突案例: 如果在函数中进行求值,则返回True,如果在函数外部进行求值,则返回False >>> def func(): ... a = 1000 ... b = 1000 ... return a is b ... >>> a = 1000 >>> b = 1000 >>> a is b, func() (False, True) 由于

在使用Python解释器时,我偶然发现了关于
is
操作符的一个冲突案例:

如果在函数中进行求值,则返回
True
,如果在函数外部进行求值,则返回
False

>>> def func():
...     a = 1000
...     b = 1000
...     return a is b
...
>>> a = 1000
>>> b = 1000
>>> a is b, func()
(False, True)
由于
is
运算符为所涉及的对象计算
id()
,这意味着
a
b
在函数
func
内部声明时指向相同的
int
实例,但相反,在函数
func
外部声明时指向不同的对象

为什么会这样


注意:我知道标识(
is
)和相等(
=
)操作之间的区别,如中所述。此外,我还了解python正在对
[-5256]
范围内的整数执行的缓存,如中所述。

这里的情况并非如此,因为数字超出了该范围,我确实想评估身份,而不是平等。

tl;博士: 作为缔约国:

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

这就是为什么在函数的情况下,有一个单个代码块,其中包含一个单个数值文本对象
1000
,因此
id(a)==id(b)
将产生
True

在第二种情况下,您有两个不同的代码对象,每个对象都有各自不同的文本对象
1000
so
id(a)!=id(b)

请注意,这种行为不会仅在
int
文本中出现,您将在
float
文本中得到类似的结果(请参阅)

当然,比较对象(除了显式的
is None
测试)应始终使用相等运算符
==
而不是
is

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


详细回答: 为了获得更清晰的视图并进一步验证这种看似奇怪的行为,我们可以使用模块直接查看这些情况下的对象

对于函数
func

与所有其他属性一样,函数对象还有一个
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
属性,允许您查看该函数的编译字节码。使用,我们可以获得给定函数的代码对象中所有存储属性的漂亮视图:

>>> print(dis.code_info(func))
Name:              func
Filename:          <stdin>
Argument count:    0
Kw-only arguments: 0
Number of locals:  2
Stack size:        2
Flags:             OPTIMIZED, NEWLOCALS, NOFREE
Constants:
   0: None
   1: 1000
Variable names:
   0: a
   1: b
当然,这将计算为
True
,因为我们指的是同一个对象

对于每个交互命令:

如前所述,每个交互命令都被解释为单个代码块:独立地解析、编译和计算

我们可以通过内置程序获取每个命令的代码对象:

>>> com1 = compile("a=1000", filename="", mode="single")
>>> com2 = compile("b=1000", filename="", mode="single")
对于每个赋值语句,我们将得到一个类似的代码对象,如下所示:

>>> print(dis.code_info(com1))
Name:              <module>
Filename:          
Argument count:    0
Kw-only arguments: 0
Number of locals:  0
Stack size:        1
Flags:             NOFREE
Constants:
   0: 1000
   1: None
Names:
   0: a
这与我们实际得到的一致

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


注意:我对源代码中到底是如何发生这种情况有些好奇,在深入研究之后,我相信我终于找到了它

在编译阶段,属性由dictionary对象表示。在中,我们实际上可以看到初始化:

/* snippet for brevity */

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

/* snippet for brevity */
在编译过程中,将检查已有的常量。请参阅了解更多关于此的信息


注意事项:
  • 链式语句将评估为
    True

    现在应该更清楚为什么下面的计算结果为
    True

     >>> a = 1000; b = 1000;
     >>> a is b
    
    在这种情况下,通过将两个赋值命令链接在一起,我们告诉解释器将它们编译在一起。与函数对象的情况一样,仅为文字
    1000
    创建一个对象,从而在计算时产生
    True

  • 在模块级执行会再次产生
    True

    如前所述,参考手册规定:

    。。。以下是模块:模块

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

  • 相同的不适用于可变的对象:

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

    a = []; b = []
    a is b  # always evaluates to False
同样,在中,这是规定的:

a=1后;b=1,a和b可能引用值为1的同一对象,也可能不引用,具体取决于实现,但在c=[]之后;d=[]、c和d保证引用两个不同的、唯一的、新创建的空列表


在交互提示下,条目是一次处理一个完整语句的条目。编译器本身(in)跟踪名为的字典中的常量,该字典将常量对象映射到其索引

在函数中,您可以看到,在添加新常量(并增加索引)之前,将检查dict以查看常量对象和索引是否已经存在。如果是这样,它们将被重用

简而言之,这意味着一条语句中的重复常量(例如在函数定义中)被折叠成一个单例。相反,您的
a=1000
b=1000
是两个独立的语句,因此不会发生折叠

FWIW,这是
 >>> a = 1000; b = 1000;
 >>> a is b
    a = []; b = []
    a is b  # always evaluates to False