python中如何使两个序列相加不改变内存地址的几种方式

Posted 洗洗睡吧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python中如何使两个序列相加不改变内存地址的几种方式相关的知识,希望对你有一定的参考价值。

# 方式1
a = [1,2,3]
print(a) # 4551311680
a.extend([4,5])
print(a) # 4551311680

# 方式2
b = [1,2,3]
print(b) # 4494299456
b += [4,5,6]
print(b) # 4494299456

# 重点讲解方式2
+=的方式是因为内部实现了__iadd__()魔法方法,内部行为类似于a.extend(b)。但是要区别于b = b + [4,5,6]这种,这种方式的内存地址是不一样的。

python 浅拷贝与深拷贝

python变量在内存中是这样存储的: 

技术分享图片

在python中,一切都是对象,对象的存储是引用存储,存储的只是变量的值所在的内存地址,而不是这个变量的值本身。 


技术分享图片


如果对值进行修改,其实是在内存中新创建一个值,把变量指向这个值的地址


技术分享图片


可以看出地址发生了改变

如果是两个值相等,那么改变其中一个不会影响另一个

技术分享图片


a=b时a,b指向同一个地址

改变a的值后b不会改变

技术分享图片


说明是在内存中新创建了一个值,a指向了这个值的地址 

技术分享图片



这是基本类型的存储

python中可以分两大类数据类型,一种是基础数据类型,如 int str float,long ,bool等基础类型,另一种是list,tulpe,dict,set等复杂类型,list,tulpe,dict,set中包含基础类型

由于python中的变量都是采用的引用存储,而list,tuple,dict这种数据结构可以包含基础数据类型,这就导致了每个变量中都存储了这个变量的地址,而不是值本身;对于复杂的数据结构来说,里面的存储的也只是每个元素的地址而已。

如果是list列表的存储形式:

L = [1,2,3,'a','b','c']

print(id(L))

变量L的地址为



技术分享图片

技术分享图片

每个元素的地址为

L = [1,2,3,'a','b','c']

#print(id(L))

print(id(L[0]))

print(id(L[1]))

print(id(L[2]))

print(id(L[3]))

print(id(L[4]))

技术分享图片

技术分享图片


技术分享图片

技术分享图片


L所存的就是这些元素的地址

在内存表现形式为:

技术分享图片

将列表L的内容赋值给另一个变量和字符的操作一样

L = [1,2,3,'a','b','c']

Y = L

print(L)

print(Y)

print(id(L))

print(id(Y))


技术分享图片


技术分享图片


对L追加元素:

L = [1,2,3,'a','b','c']

Y = L

print(L)

print(Y)

print(id(L))

print(id(Y))

L.append("f")

print(L)

print(Y)

print(id(L))

print(id(Y))


技术分享图片


技术分享图片

如果重新赋值或者说重新初始化,那么会重新分配新的内存空间

L = [1,2,3,'a','b','c']

Y = L

print(L)

print(Y)

print(id(L))

print(id(Y))

L.append("f")

print(L)

print(Y)

print(id(L))

print(id(Y))

L = ['a','b','c']

print(L)

print(Y)

print(id(L))

print(id(Y))


技术分享图片

技术分享图片


也就是说如果在地址上操作那么指向这两个地址的变量的内容都会改变但是地址指向不变

如果重新赋值,那么重新赋值的那个变量地址就会指向新的内存地址

这就是变量赋值的过程

浅拷贝

浅拷贝:不管多么复杂的数据结构,浅拷贝都只会copy一层。

基础数据的拷贝,不管浅拷贝还是深拷贝都一样都会重新分配内存地址

#基础数据拷贝

import copy

L = [1,2,3,'a','b']

M = copy.copy(L)

print(L)

print(M)

print(id(L))

print(id(M))


技术分享图片


技术分享图片


import copy

L = [1,2,3,'a','b']

M = copy.copy(L)

print(L)

print(M)

print(id(L))

print(id(M))

#给L追加一个元素M不会变

L.append("c")

print(L)

print(M)

print(id(L))

print(id(M))


技术分享图片


技术分享图片


深拷贝基础数据:

import copy

L = [1,2,3,'a','b']

M = copy.deepcopy(L)

print(L)

print(M)

print(id(L))

print(id(M))

#给L追加一个元素M不会变

L.append("c")

print(L)

print(M)

print(id(L))

print(id(M))


技术分享图片


技术分享图片

结果和浅拷贝一样

复杂的数据:

仅仅对外层修改拷贝后L改变M不变

import copy

L = [1,2,[3,4,'c','d'],'a','b']

M = copy.copy(L)

print(L)

print(M)

print(id(L))

print(id(M))

#给L追加一个元素M不会变

L.append("c")

print(L)

print(M)

print(id(L))

print(id(M))


技术分享图片


技术分享图片


对内层改变,那么拷贝后都会变

import copy

L = [1,2,[3,4,'c','d'],'a','b']

M = copy.copy(L)

print(L)

print(M)

print(id(L))

print(id(M))

L[2].append("e")

print(L)

print(M)

print(id(L))

print(id(M))

技术分享图片

技术分享图片

这说明对内层数据的修改会改变另一个拷贝的对象

在内存中的形式为:


技术分享图片

上图可以看出,内层list[1,2]也是一个地址,里面存的就是元素1,2的地址

所以对L的内层操作就是对内层list[1,2]地址的操作,这样就会改变M

深度拷贝:

import copy

L = [1,2,[3,4,'c','d'],'a','b']

M = copy.deepcopy(L)

print(L)

print(M)

print(id(L))

print(id(M))

#给L追加一个元素M不会变

L[2].append("e")

print(L)

print(M)

print(id(L))

print(id(M))


技术分享图片

技术分享图片

可以看出L改变M不变 

在内存中的形式:

技术分享图片


相当于所有的都拷贝一份

1、对于复杂的数据结构copy.copy 浅拷贝 只拷贝父对象(最外一层),不会拷贝对象的内部(内层数据)的子对象。

2、copy.deepcopy 深拷贝 拷贝父对象(外层数据)及其子对象(内层数据)

3、对于基本数据类型深拷贝浅拷贝都一样,都会产生新的数据空间

以上是关于python中如何使两个序列相加不改变内存地址的几种方式的主要内容,如果未能解决你的问题,请参考以下文章

Python当中的array数组对象

Python字符串相加以及字符串格式化

Ozon Tech Challenge 2020

Python从菜鸟到高手(14):序列的加法和乘法

Python:序列的增量赋值

最小中间和