前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >==和is的区别 | Python基础

==和is的区别 | Python基础

作者头像
程序员荒生
发布2022-03-04 11:43:10
2600
发布2022-03-04 11:43:10
举报

is==都是对对象进行比较判断的操作符,但比较判断的内容并不相同。下面具体看看两者区别在哪?

基本概念

Python设计之初就是一门面向对象的语言,即一切皆对象

  • 数组、字符串、元组、列表、字典、函数、方法、类、模块等等都是对象,包括代码

Python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)

  • 对象是堆上分配的一个内存空间,存储具体的对象值
  • 引用是从变量到对象的指针

技术上说,每个对象有两个标准的头部信息,一个类型标识符来标识类型,还有一个引用的计数器,用于决定是否需要回收对象

代码语言:javascript
复制
 import sys
 sys.getrefcount(1)
  • 可以通过 sys 模块中的 getrefcount() 函数查询一个对象计数器的值

具体区别

  • is比较的是两个对象的id值是否相等,也就是比较两个对象是否为同一个实例对象,是否指向同一个内存地址
  • ==比较的是两个对象的内容是否相等,默认会调用对象的__eq__()方法,==是python标准操作符中的比较操作符
代码语言:javascript
复制
  a = [1, 2, 3]
  b = a
  print(id(a)) # 140275598064704
  print(id(b)) # 140275598064704
  print(b is a) # True
  print(b == a) # True

  b = a[:]
  print(id(a)) # 140275598064704
  print(id(b)) # 140275598064832
  print(b is a) # False
  print(b == a) # True

Python性能优化

  • 出于对性能的考虑,Python内部做了很多的优化工作,对于整数对象,Python把一些频繁使用的整数对象缓存起来,保存到一个叫small_ints的链表中,在Python的整个生命周期内,任何需要引用这些整数对象的地方,都不再重新创建新的对象,而是直接引用缓存中的对象
  • Python把可能频繁使用[-5, 256]之间的整数小对象放在small_ints中,但凡需要用些小整数时,就从这里面取,而不再去临时创建新的对象
代码语言:javascript
复制
  a = 256
  b = 128*2
  print(a is b) # True
  print(a == b) # True
  a = 258
  b = 129*2
  print(a is b) # False
  print(a == b) # True

small_ints源码分析(Python2.7)

  • Python自身的main函数,会调用Py_Initialize函数初始化Python内部一系列模块,Modules/main.c,551行
  • 初始化过程中,_PyInt_Init会被调用,Python/pythonrun.c,210行
  • _PyInt_Init的唯一作用就是初始化small_ints数组,Objects/intobject.c,1452行,具体代码如下:
代码语言:javascript
复制
int
_PyInt_Init(void)
{
    PyIntObject *v;
    int ival;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
    for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++) {
        if (!free_list && (free_list = fill_free_list()) == NULL)
            return 0;
        /* PyObject_New is inlined */
        v = free_list;
        free_list = (PyIntObject *)Py_TYPE(v);
        (void)PyObject_INIT(v, &PyInt_Type);
        v->ob_ival = ival;
        small_ints[ival + NSMALLNEGINTS] = v;
    }
#endif
    return 1;
}
  • NSMALLNEGINTS 和NSMALLPOSINTS 宏定义,Objects/intobject.c,68行,具体代码如下:
代码语言:javascript
复制
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS           257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS           5
#endif
  • Python诸如 a = 5 这样的语句,最终会落到PyInt_FromLong这个函数里,Objects/intobject.c,86行,具体代码如下:
代码语言:javascript
复制
PyObject *
PyInt_FromLong(long ival)
{
    register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
    if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
        v = small_ints[ival + NSMALLNEGINTS];
        Py_INCREF(v);
#ifdef COUNT_ALLOCS
        if (ival >= 0)
            quick_int_allocs++;
        else
            quick_neg_int_allocs++;
#endif
        return (PyObject *) v;
    }
#endif
    if (free_list == NULL) {
        if ((free_list = fill_free_list()) == NULL)
            return NULL;
    }
    /* Inline PyObject_New */
    v = free_list;
    free_list = (PyIntObject *)Py_TYPE(v);
    (void)PyObject_INIT(v, &PyInt_Type);
    v->ob_ival = ival;
    return (PyObject *) v;
}
  • 从_PyInt_Init的实现上,我们可以看到被放入small_ints的数字范围是-5到256。因此,你可以通过修改源代码的方式,将这个范围任意的扩展。

small_ints源码(Python3.9)

  • _PyLong_Init初始化small_ints数组,Objects/longobject.c,5754行,具体代码如下:
代码语言:javascript
复制
int
_PyLong_Init(PyThreadState *tstate)
{
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
    for (Py_ssize_t i=0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++) {
        sdigit ival = (sdigit)i - NSMALLNEGINTS;
        int size = (ival < 0) ? -1 : ((ival == 0) ? 0 : 1);

        PyLongObject *v = _PyLong_New(1);
        if (!v) {
            return -1;
        }

        Py_SET_SIZE(v, size);
        v->ob_digit[0] = (digit)abs(ival);

        tstate->interp->small_ints[i] = v;
    }
#endif

    if (_Py_IsMainInterpreter(tstate)) {
        _PyLong_Zero = PyLong_FromLong(0);
        if (_PyLong_Zero == NULL) {
            return 0;
        }

        _PyLong_One = PyLong_FromLong(1);
        if (_PyLong_One == NULL) {
            return 0;
        }

        /* initialize int_info */
        if (Int_InfoType.tp_name == NULL) {
            if (PyStructSequence_InitType2(&Int_InfoType, &int_info_desc) < 0) {
                return 0;
            }
        }
    }

    return 1;
}
  • 两个宏:NSMALLNEGINTSNSMALLPOSINTS的定义,Objects/longobject.c,20行,具体代码如下:
代码语言:javascript
复制
#define NSMALLPOSINTS           _PY_NSMALLPOSINTS
#define NSMALLNEGINTS           _PY_NSMALLNEGINTS
  • 具体定义跳转至 Include/internal/pycore_interp.h,67行,具体代码如下:
代码语言:javascript
复制
#define _PY_NSMALLPOSINTS           257
#define _PY_NSMALLNEGINTS           5

特殊情况

  • 示例A:
代码语言:javascript
复制
  a = 'pythontab.com'
  b = 'pythontab.com'
  print(a is b)   # False
  print(a == b)   # True
  a = 'pythontabcom'
  b = 'pythontabcom'
  print(a is b)   # True
  print(a == b)   # True
  • 不同字符串类型is和==结果不完全相同,这个和解释器实现有关。
  • 示例B:
代码语言:javascript
复制
  a = (1, 2, 3)
  b = (1, 2, 3)
  print(a is b)   # False
  print(a == b)   # True

  a = [1, 2, 3]
  b = [1, 2, 3]
  print(a is b)   # False
  print(a == b)   # True

  a = {'python': 100, 'com': 1}
  b = {'python': 100, 'com': 1}
  print(a is b)   # False
  print(a == b)   # True

  a = set([1, 2, 3])
  b = set([1, 2, 3])
  print(a is b)   # False
  print(a == b)   # True
  • 当变量是数字、字符串、元组,列表,字典时,is和==都不相同,不能互换使用
  • 当比较值时,要使用==,比较是否是同一个内存地址时应该使用is

扩展

  • Java机制,缓存范围 -128~127
代码语言:javascript
复制
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.

参考文献

  • https://www.pythontab.com/html/2017/pythonhexinbiancheng_1219/1205.html
  • https://zhuanlan.zhihu.com/p/33907983
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-04-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 李三十一 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基本概念
  • 具体区别
  • Python性能优化
    • small_ints源码分析(Python2.7)
      • small_ints源码(Python3.9)
      • 特殊情况
      • 扩展
      • 参考文献
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档