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 相关文章推荐
打开电脑上的QQ的python代码
Feb 10 Python
Python3网络爬虫之使用User Agent和代理IP隐藏身份
Nov 23 Python
python email smtplib模块发送邮件代码实例
Apr 26 Python
Python读取数据集并消除数据中的空行方法
Jul 12 Python
详解flask表单提交的两种方式
Jul 21 Python
Python列表推导式与生成器用法分析
Aug 02 Python
使用Python实现一个栈判断括号是否平衡
Aug 23 Python
利用Python将文本中的中英文分离方法
Oct 31 Python
Python实现元素等待代码实例
Nov 11 Python
python基于scrapy爬取京东笔记本电脑数据并进行简单处理和分析
Apr 14 Python
python非标准时间的转换
Jul 25 Python
python index() 与 rindex() 方法的使用示例详解
Dec 24 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中sql注入漏洞示例 sql注入漏洞修复
2014/01/24 PHP
php中关于socket的系列函数总结
2015/05/18 PHP
Yii2中datetime类的使用
2016/12/17 PHP
Yii2结合Workerman的websocket示例详解
2018/09/10 PHP
Javascript 继承机制的实现
2009/08/12 Javascript
jQuery获取文本节点之 text()/val()/html() 方法区别
2011/03/01 Javascript
JQuery 1.6发布 性能提升,同时包含大量破坏性变更
2011/05/10 Javascript
JS判断字符串长度的5个方法(区分中文和英文)
2014/03/18 Javascript
判断访客终端类型集锦
2015/06/05 Javascript
JavaScript检测上传文件大小的方法
2015/07/22 Javascript
基于jquery实现人物头像跟随鼠标转动
2015/08/23 Javascript
聊一聊Vue.js过渡效果
2016/09/07 Javascript
AngularJS验证信息框架的封装插件用法【w5cValidator扩展插件】
2016/11/03 Javascript
jQuery的三种bind/One/Live/On事件绑定使用方法
2017/02/23 Javascript
JS实现网页抢购功能(触发,终止脚本)
2017/11/27 Javascript
Vue shopCart 组件开发详解
2018/01/26 Javascript
Javascript实现运算符重载详解
2018/04/07 Javascript
微信小程序异步API为Promise简化异步编程的操作方法
2018/08/14 Javascript
node.js使用mongoose操作数据库实现购物车的增、删、改、查功能示例
2019/12/23 Javascript
微信小程序实现菜单左右联动
2020/05/19 Javascript
Python压缩和解压缩zip文件
2015/02/14 Python
Python中的Numpy矩阵操作
2018/08/12 Python
Python 脚本获取ES 存储容量的实例
2018/12/27 Python
Python实现KNN(K-近邻)算法的示例代码
2019/03/05 Python
Python爬取阿拉丁统计信息过程图解
2020/05/12 Python
Python 判断时间是否在时间区间内的实例
2020/05/16 Python
python3.6.5基于kerberos认证的hive和hdfs连接调用方式
2020/06/06 Python
将pycharm配置为matlab或者spyder的用法说明
2020/06/08 Python
Python将字典转换为XML的方法
2020/08/01 Python
Biblibili视频投稿接口分析并以Python实现自动投稿功能
2021/02/05 Python
详解CSS3中border-image的使用
2015/07/18 HTML / CSS
财务助理岗位职责
2013/11/10 职场文书
房地产项目建议书
2014/03/12 职场文书
大学生职业生涯规划书汇总
2014/03/20 职场文书
2015年教师业务工作总结
2015/05/26 职场文书
如何用JS实现网页瀑布流布局
2021/04/24 Javascript