Python中的is和id用法分析


Posted in Python onJanuary 26, 2015

本文实例讲述了Python中的is和id用法。分享给大家供大家参考。具体分析如下:

(ob1 is ob2) 等价于 (id(ob1) == id(ob2))

首先id函数可以获得对象的内存地址,如果两个对象的内存地址是一样的,那么这两个对象肯定是一个对象。和is是等价的。Python源代码为证。

static PyObject *

 cmp_outcome(int op, register PyObject *v, register PyObject *w)

{

 int res = 0;

 switch (op) {

 case PyCmp_IS:

  res = (v == w);

 break;

 case PyCmp_IS_NOT:

res = (v != w);

 break;

但是请看下边代码的这种情况怎么会出现呢?

In [1]: def bar(self, x):

...:     return self.x + y

...: 

 

In [2]: class Foo(object):

...:     x = 9

...:     def __init__(self ,x):

...:         self.x = x

...:     bar = bar

...:     

 

In [3]: foo = Foo(5)

 

In [4]: foo.bar is Foo.bar

Out[4]: False

 

In [5]: id(foo.bar) == id(Foo.bar)

Out[5]: True

两个对象用is判断是False,用id判断却是True,这与我们已知的事实不符啊,这种现象该如何解释呢?遇到这种情况最好的解决方法就是调用dis模块去看下两个比较语句到底做了什么。

In [7]: dis.dis("id(foo.bar) == id(Foo.bar)")

          0 BUILD_MAP       10340

          3 BUILD_TUPLE     28527

          6 <46>           

          7 DELETE_GLOBAL   29281 (29281)

         10 STORE_SLICE+1 

         11 SLICE+2       

         12 DELETE_SUBSCR  

         13 DELETE_SUBSCR  

         14 SLICE+2       

         15 BUILD_MAP       10340

         18 PRINT_EXPR     

         19 JUMP_IF_FALSE_OR_POP 11887

         22 DELETE_GLOBAL   29281 (29281)

         25 STORE_SLICE+1 

 

In [8]: dis.dis("foo.bar is Foo.bar")

          0 BUILD_TUPLE     28527

          3 <46>           

          4 DELETE_GLOBAL   29281 (29281)

          7 SLICE+2       

          8 BUILD_MAP        8307

         11 PRINT_EXPR     

         12 JUMP_IF_FALSE_OR_POP 11887

         15 DELETE_GLOBAL   29281 (29281)

真实情况是当执行.操作符的时候,实际是生成了一个proxy对象,foo.bar is Foo.bar的时候,两个对象顺序生成,放在栈里相比较,由于地址不同肯定是False,但是id(foo.bar) == id(Foo.bar)的时候就不同了,首先生成foo.bar,然后计算foo.bar的地址,计算完之后foo.bar的地址之后,就没有任何对象指向foo.bar了,所以foo.bar对象就会被释放。然后生成Foo.bar对象,由于foo.bar和Foo.bar所占用的内存大小是一样的,所以又恰好重用了原先foo.bar的内存地址,所以id(foo.bar) == id(Foo.bar)的结果是True。

下面内容由邮件Leo Jay大牛提供,他解释的更加通透。

用id(expression a) == id(expression b)来判断两个表达式的结果是不是同一个对象的想法是有问题的。

foo.bar 这种形式叫 attribute reference [1],它是表达式的一种。foo是一个instance object,bar是一个方法,这个时候表达式foo.bar返回的结果叫method object。根据文档:

When an instance attribute is referenced that isn't a data attribute,
its class is searched. If the name denotes a valid class attribute
that is a function object, a method object is created by packing
(pointers to) the instance object and the function object just found
together in an abstract object: this is the method object.

foo.bar本身并不是简单的名字,而是表达式的计算结果,是一个 method object,在id(foo.bar)这样的表达式里,method object只是一个临时的中间变量而已,对临时的中间变量做id是没有意义的。

一个更明显的例子是,

print id(foo.bar) == id(foo.__init__)

输出的结果也是True

看 id 的文档:

Return the “identity” of an object. This is an integer (or long
integer) which is guaranteed to be unique and constant for this object
during its lifetime. Two objects with non-overlapping lifetimes may
have the same id() value.
CPython implementation detail: This is the address of the object in memory.

只有你能保证对象不会被销毁的前提下,你才能用 id 来比较两个对象。所以,如果你非要比的话,得这样写:

fb = foo.bar 

Fb = Foo.bar 

print id(fb) == id(Fb)

即把两个表达式的结果绑定到名字上,再来比是不是同一个对象,你才能得到正确的结果。

is表达式也是一样的,你现在得到了正确的结果,完全是因为 CPython 现在的实现细节决定的。现在的is的实现,是左右两边的对象都计算出来,然后再比较这两个对象的地址是否一样。万一哪天改成了,先算左边,保存地址,把左边释放掉,再算右边,再比较的话,你的is的结果可能就错了。官方文档里也提到了这个问题 。我认为正确的方法也是像id那样,先把左右两边都计算下来,并显式绑定到各自的名字上,然后再用is判断。

希望本文所述对大家的Python程序设计有所帮助。

Python 相关文章推荐
python修改字典内key对应值的方法
Jul 11 Python
Python随机生成均匀分布在单位圆内的点代码示例
Nov 13 Python
Django进阶之CSRF的解决
Aug 01 Python
Python3调用百度AI识别图片中的文字功能示例【测试可用】
Mar 13 Python
对pyqt5之menu和action的使用详解
Jun 20 Python
在SQLite-Python中实现返回、查询中文字段的方法
Jul 17 Python
python图片剪裁代码(图片按四个点坐标剪裁)
Mar 10 Python
python tkinter实现下载进度条及抖音视频去水印原理
Feb 07 Python
Python基础知识之变量的详解
Apr 14 Python
Pytest之测试命名规则的使用
Apr 16 Python
教你用python控制安卓手机
May 13 Python
Python破解极验滑动验证码详细步骤
May 21 Python
Python的批量远程管理和部署工具Fabric用法实例
Jan 23 #Python
推荐11个实用Python库
Jan 23 #Python
17个Python小技巧分享
Jan 23 #Python
Windows8下安装Python的BeautifulSoup
Jan 22 #Python
Python实现抓取百度搜索结果页的网站标题信息
Jan 22 #Python
Python中使用异常处理来判断运行的操作系统平台方法
Jan 22 #Python
Python实现把utf-8格式的文件转换成gbk格式的文件
Jan 22 #Python
You might like
基于PHP常用函数的用法详解
2013/05/10 PHP
php多重接口的实现方法
2015/06/20 PHP
PHP实现关键字搜索后描红功能示例
2019/07/03 PHP
jquery中dom操作和事件的实例学习 仿yahoo邮箱登录框的提示效果
2011/11/30 Javascript
JS代码优化技巧之通俗版(减少js体积)
2011/12/23 Javascript
jq实现酷炫的鼠标经过图片翻滚效果
2014/03/12 Javascript
使用Node.js实现一个简单的FastCGI服务器实例
2014/06/09 Javascript
javascript关于运动的各种问题经典总结
2015/04/27 Javascript
jQuery获取URL请求参数的方法
2015/07/18 Javascript
情人节单身的我是如何在敲完代码之后收到12束玫瑰的(javascript)
2015/08/21 Javascript
微信小程序 实现拖拽事件监听实例详解
2016/11/16 Javascript
nodejs结合socket.io实现websocket通信功能的方法
2018/01/12 NodeJs
Vue数据双向绑定的深入探究
2018/11/27 Javascript
jQuery无冲突模式详解
2019/01/17 jQuery
js设置默认时间跨度过程详解
2019/07/17 Javascript
JS实现的进制转换,浮点数相加,数字判断操作示例
2019/11/09 Javascript
vue 动态组件用法示例小结
2020/03/06 Javascript
[01:07]2015国际邀请赛 中国区预选赛精彩回顾
2015/06/15 DOTA
python网络编程学习笔记(六):Web客户端访问
2014/06/09 Python
详解Python的Django框架中manage命令的使用与扩展
2016/04/11 Python
python TCP Socket的粘包和分包的处理详解
2018/02/09 Python
pandas删除行删除列增加行增加列的实现
2019/07/06 Python
python IDLE 背景以及字体大小的修改方法
2019/07/12 Python
Python数据分析pandas模块用法实例详解
2019/11/20 Python
pytorch中tensor.expand()和tensor.expand_as()函数详解
2019/12/27 Python
使用tensorflow DataSet实现高效加载变长文本输入
2020/01/20 Python
Corelle官方网站:购买康宁餐具
2016/11/02 全球购物
简述安装Slackware Linux系统的过程
2012/05/08 面试题
多媒体专业自我鉴定
2014/02/28 职场文书
经典而简洁的婚礼主持词
2014/03/13 职场文书
公关活动策划方案
2014/05/25 职场文书
2015年秘书个人工作总结
2015/04/25 职场文书
2019年员工晋升管理制度范本!
2019/07/08 职场文书
Goland使用Go Modules创建/管理项目的操作
2021/05/06 Golang
dubbo服务整合zipkin详解
2021/07/26 Java/Android
Python利用FlashText算法实现替换字符串
2022/03/31 Python