举例讲解Python中is和id的用法


Posted in Python onApril 03, 2015

(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 [2]。根据文档:

    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 的文档[3]:

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

Python 相关文章推荐
python获取当前计算机cpu数量的方法
Apr 18 Python
Django中URL视图函数的一些高级概念介绍
Jul 20 Python
python3 破解 geetest(极验)的滑块验证码功能
Feb 24 Python
Python中将dataframe转换为字典的实例
Apr 13 Python
PyQt5每天必学之带有标签的复选框
Apr 19 Python
读取json格式为DataFrame(可转为.csv)的实例讲解
Jun 05 Python
Python元组常见操作示例
Feb 19 Python
Python I/O与进程的详细讲解
Mar 08 Python
python GUI库图形界面开发之PyQt5窗口背景与不规则窗口实例
Feb 25 Python
Django-xadmin后台导入json数据及后台显示信息图标和主题更改方式
Mar 11 Python
python 模拟登陆163邮箱
Dec 15 Python
python中redis包操作数据库的教程
Apr 19 Python
详解Python2.x中对Unicode编码的使用
Apr 03 #Python
对于Python中线程问题的简单讲解
Apr 03 #Python
python BeautifulSoup设置页面编码的方法
Apr 03 #Python
用Python编写一个简单的FUSE文件系统的教程
Apr 02 #Python
用Python中的__slots__缓存资源以节省内存开销的方法
Apr 02 #Python
用Python的线程来解决生产者消费问题的示例
Apr 02 #Python
用实例分析Python中method的参数传递过程
Apr 02 #Python
You might like
Laravel中扩展Memcached缓存驱动实现使用阿里云OCS缓存
2015/02/10 PHP
laravel 5.4 + vue + vux + element的环境搭配过程介绍
2018/04/26 PHP
node.js正则表达式获取网页中所有链接的代码实例
2014/06/03 Javascript
javascript常见操作汇总
2014/09/03 Javascript
Egret引擎开发指南之视觉编程
2014/09/03 Javascript
jquery 实现两Select 标签项互调示例代码
2014/09/25 Javascript
jQuery+AJAX实现网页无刷新上传
2015/02/22 Javascript
jquery中cookie用法实例详解(获取,存储,删除等)
2016/01/04 Javascript
JS中改变this指向的方法(call和apply、bind)
2016/03/26 Javascript
仿百度换肤功能的简单实例代码
2016/07/11 Javascript
详解使用JS如何制作简单的ASCII图与单极图
2017/03/31 Javascript
详解Angular 开发环境搭建
2017/06/22 Javascript
详解基于webpack2.x的vue2.x的多页面站点
2017/08/21 Javascript
微信小程序实现手势图案锁屏功能
2018/01/30 Javascript
layui监听下拉选框选中值变化的方法(包含监听普通下拉选框)
2019/09/24 Javascript
小谈angular ng deploy的实现
2020/04/07 Javascript
JavaScript设计模式之观察者模式与发布订阅模式详解
2020/05/07 Javascript
微信小程序连续签到7天积分获得功能的示例代码
2020/08/20 Javascript
[01:06:39]DOTA2上海特级锦标赛主赛事日 - 1 胜者组第一轮#1Liquid VS Alliance第三局
2016/03/02 DOTA
pytyon 带有重复的全排列
2013/08/13 Python
python算法学习之计数排序实例
2013/12/18 Python
Python 中 Virtualenv 和 pip 的简单用法详解
2017/08/18 Python
selenium跳过webdriver检测并模拟登录淘宝
2019/06/12 Python
树莓派使用USB摄像头和motion实现监控
2019/06/22 Python
Python如何使用turtle库绘制图形
2020/02/26 Python
python正则表达式re.match()匹配多个字符方法的实现
2021/01/27 Python
HTML5 解析规则分析
2009/08/14 HTML / CSS
input file上传文件样式支持html5的浏览器解决方案
2012/11/14 HTML / CSS
全球最大的在线橄榄球商店:Lovell Rugby
2018/05/20 全球购物
审计主管岗位职责
2014/01/31 职场文书
委托书模板
2014/04/04 职场文书
2014年教师节寄语
2014/08/11 职场文书
酒店收银员岗位职责
2015/04/07 职场文书
2015年语文教师工作总结
2015/05/25 职场文书
校运会班级霸气口号
2015/12/24 职场文书
《静夜思》教学反思
2016/02/17 职场文书