python对象的比较 is、==、浅拷贝、深拷贝、参数传递、值传递、引用传递

177 阅读5分钟

'==' VS 'is'

  • '=='操作符 比较对象之间的值是否相等
  • 'is'操作符 比较的是对象的ID是否相等,是否指向同一个内存地址

效率: 'is'效率优于'=='

  • 'is'操作符无法被重载,执行'is'操作只是简单的获取对象的 ID,并进行比较
  • '=='操作符则会递归地遍历对象的所有值,并逐一比较

注意:

  • python 维护一个缓存 记录-5 到 256 范围内的整型数字, 所以赋值-5 到 256 时会直接引用 python缓存, 而不是重新开辟空间, 所以两个变量的id相等
  • 两个int 都是 -5 到 256 范围内的同一个数字, id相等
  • 两个字符串相等的情况下没有空格时 id是相等的, 有空格时 id不相等

## id相等
In [7]: a="wangfei"

In [8]: b="wangfei"

In [9]: id(a)
Out[9]: 139661013803872

In [10]: id(b)
Out[10]: 139661013803872

In [11]: a==b
Out[11]: True

In [12]: a is b 
Out[12]: True

In [13]: 


## id不相等
In [13]: c = "wangfei is a pythoner"

In [14]: d = "wangfei is a pythoner"

In [15]: c==d
Out[15]: True

In [16]: c is d
Out[16]: False

浅拷贝

浅拷贝 copy.copy()

重新分配一块内存,创建一个新的对象,里面的元素是原对象中子对象的引用, 将原数据中的 "引用关系" 复制了一份

如果原对象是可以变类型 原对象改变后 浅拷贝得对象也变了 可能会出现数据不一致

浅拷贝例子

##  l2是l1的浅拷贝
In [38]: l1 = [[1, 2], (30, 40)]

In [39]: l2 = list(l1)

In [40]: l1 is l2
Out[40]: False

## 可以看到两个list中的元素 ID完全一致 说明L2中的元素全部引用了l1的元素
In [58]: id(l1[0])
Out[58]: 139661005243744

In [59]: id(l2[0])
Out[59]: 139661005243744

In [60]: id(l1[1])
Out[60]: 139661013758824

In [61]: id(l2[1])
Out[61]: 139661013758824


## l1新增一个元素 不会对l2影响 因为是两个不同的对象
In [41]: l1.append(100)
## 因为l2[0]引用 l1[0] ,  l1[0] 变化了 所以l2[0]也变化
In [42]: l1[0].append(3)

## ID依然不变
In [63]: id(l1[0])
Out[63]: 139661005243744

In [64]: id(l2[0])
Out[64]: 139661005243744

In [43]: l1
Out[43]: [[1, 2, 3], (30, 40), 100]

In [44]: l2
Out[44]: [[1, 2, 3], (30, 40)]



## l1[1]是元组类型 不可变 所以l1[1] += (50, 60) 等于新键了一个(30, 40, 50, 60)对象 但是之前的(30, 40)对象没有变化 继续被l2[1]引用
In [45]: l1[1] += (50, 60)

In [46]: l1
Out[46]: [[1, 2, 3], (30, 40, 50, 60), 100]

In [47]: l2
Out[47]: [[1, 2, 3], (30, 40)]

深拷贝

深拷贝 cpy.deepcopy()

是指重新分配一块内存,创建一个新的对象,新对象和原对象没有任何关系了, 并且将原对象中的元素 以递归的方式 通过创建新的子对象拷贝到新对象中

深度拷贝中会维护一个字典,记录已经拷贝的对象及其 ID,来提高效率并防止无限递归的发生。

深拷贝例子

无论 l1 如何变化,l2 都不变。因为此时的 l1 和 l2 完全独立,没有任何联系。

import copy

In [70]: l1 = [[1, 2], (30, 40)]

In [71]: l2 = copy.deepcopy(l1)

## l1 和 l2元素的ID完全不一致 
In [72]: id(l1[0])
Out[72]: 139661023654040

In [73]: id(l2[0])
Out[73]: 139661023639528


## l1 和 l2元素的ID完全一致 因为元组类型不可变 
In [74]: id(l1[1])
Out[74]: 139661014224840

In [75]: id(l2[1])
Out[75]: 139661014224840

总结:

  • 要学会用id方法 判断对象得内存地址

值传递

所谓值传递,通常就是拷贝参数的值,然后传递给函数里的新变量。这样,原变量和新变量之间互相独立,互不影响 类似深拷贝

引用传递

所谓引用传递,通常是指把参数的引用传给新的变量,这样,原变量和新变量就会指向同一块内存地址。 类似指针

Python 参数传递

是赋值传递 (pass by assignment),或者叫作对象的引用传递(pass by object reference)

让 新变量 与 原变量 指向相同的对象

对象引用,实参指向一个内存地址,传参到函数时,是让形参也指向这个地址,如果参数是可变对象,则直接对这个内存地址中的值进行操作,如果参数是不可变对象,则复制这个值并对其进行函数内的操作再存入新的内存地址,并使形参指向这个地址