Python对象的赋值、浅复制、深复制

Python对象的赋值、浅复制、深复制

目录


关于Python的‘一切皆对象’

    ‘一切皆对象’说的是python内的变量,不管数据类型如何,当在引用该变量时,这个引用都是指向的内存空间,和该变量所指向的内存空间是一样的,即均指向了这个内存空间内存储的对象

Python中的数据类型

Python中有 6 个标准数据类型:

Number:数字    [包括:int、float、bool、complex]
String:字符串   [由’或”括起来的内容]
List:列表       [在方括号([])之间,逗号分隔开的元素列表]
Tuple:元祖     [在小括号(())之间,逗号分隔开的元素列表]
Sets:集合      [在大括号({})之间,逗号分隔开的元素列表]
Dictionary:字典 [由键key值value结对构成的集合]

注意:

  1. 查看变量的数据类型可用type(变量名)、或isinstance(变量名,数据类型)
  2. Number不是数据类型,int、float、bool、complex才是,只是将这几类归为Number而已,所以使用isinstance判断是用number是不对的
  3. 具体数据类型知识可参考‘菜鸟教程-Python3 基本数据类型’

这6个数据类型又分为2类:可变数据类型、不可变数据类型

不可变数据类型:
    Number、String、Tuple
可变数据类型:
    Sets、List、Dictionary
    还包括numpy创建的数组array类型和矩阵matrix
注:关于array和matrix的使用和区别,可以参考python中的矩阵运算以及python矩阵中matrix()和array()函数区别


Python的‘一切皆对象’的具体说明
  1. 对于不可变数据类型:
#第一步:建立变量指向对象---------------------------
#数据类型随意,我们以int为例
x = 1
#然后用id(x)查看x所在的物理地址:
id(x)
>>>4507856240
#现在,我们将x变量赋值给y,再用id(x)、id(y)观察x和y的物理地址
y = x
id(x),id(y)
>>>(4507856240, 4507856240)
#这里我们可以发现一些东西:int类型的1就是一个对象
#变量x只是指向了存储这个对象的物理地址:4507856240,
#当我们把x的值赋值给y之后,y也指向了这个物理地址:4507856240
#第二步:改变变量,观察对象-------------------------
#我们将x的值改变,并观察id(x),id(y)
x = 2
id(x),id(y)
>>>(4507856272, 4507856240)
#即:我们新生成了一个对象:2,并存储在了物理地址4507856272之下
#x新指向了这个地址,即新的物理空间
#而y依旧指向之前的对象所在的地址4507856240,没有变化
#我们继续改变y的值,继续观察id(y)
y = 3
id(y)
>>>4507856304
#可以发现,y指向了新的物理地址(即新的内存空间),这个物理地址存储了新的对象:3
#而1依旧保留在原来的物理地址(4507856240)内

通过上面的例子可以发现,对于不可变数据类型,我们在改变变量的值时,并不会对原始对象进行任何改变,而是将变量指向新的内存空间,并在新的空间存储新的变量值
2. 列表内容
对于可变数据类型, 我们以列表为例子,步骤同上,不进行一一叙述

#----建立变量,观察内存空间----------
>>>L1 = ['a','b','c','d']
>>>id(L1)
4530761992
>>>L2 = L1
>>>id(L2)
4530761992
#----改变L1,看L1的值,看L1、L2的地址,以及L2的值------
>>>L1[2] = 'niu'
>>>L1
['a', 'b', 'niu', 'd']
>>>id(L1),id(L2)
(4530761992, 4530761992)
#可以看到,L1的指向的列表对象内容发生了改变,但是L1所指向的物理地址还是没有变化,所以继续看L2的值:
>>>L2
['a', 'b', 'niu', 'd']
#--继续改变L2的值,并观察L1、L2的地址,以及L1的值-----
>>>L2[1] = 'nx'
>>>L2
['a', 'nx', 'niu', 'd']
>>>id(L1),id(L2)
(4530761992, 4530761992)
>>>L1
['a', 'nx', 'niu', 'd']

即,对于可变的数据类型,当我们队变量进行改变时,就是在改变该变量所指向的对象,并且改变了其他也指向这个目标地址的其他变量


所以:Python如何对一个对象进行复制?[浅复制、深复制]

所以,我们如何对一个对象进行复制,而不会造成修改复制后的变量时改变原对象呢?
从上述实验可知:

对于不可变数据类型:
用 = 进行赋值时,只是将变量x的内存地址传给了y,改变x或y所指的对象时,只是让x、y指向了新的内存地址。
对于可变数据类型:
用 = 进行赋值时,只是同样将x的内存地址传给了y,但是改变x或y所指的对象时,都会对原对象进行改变。

为了实现对变量的复制,并且在改变复制的对象时不改变原对象,我们引入copy包,使用copy(),或deepcopy()进行复制操作。

#--载入copy包---------------
import copy
>>>a = [1,1.5,'nx',[1,2,'x']]
>>>b = copy.copy(a)
>>>c = copy.deepcopy(a)
#--观察a、b、c的值-----------
>>>a
[1, 1.5, 'nx', [1, 2, 'x']]
>>> b
[1, 1.5, 'nx', [1, 2, 'x']]
>>> c
[1, 1.5, 'nx', [1, 2, 'x']]
#--观察a、b、c的物理地址------
>>> id(a)
4530727496
>>> id(b)
4530693576
>>> id(c)
4530727304
#--改变a内的不可变数据类型:int、float、string--
#观察a、b、c的值和物理地址----------------
>>> a[0] = 2
>>> a[1] = 2.5
>>> a[2] = 'my'
>>> a
[2, 2.5, 'my', [1, 2, 'x']]
>>> b
[1, 1.5, 'nx', [1, 2, 'x']]
>>> c
[1, 1.5, 'nx', [1, 2, 'x']]
>>> id(a)
4530727496
>>> id(b)
4530693576
>>> id(c)
4530727304
#--改变a内的可变数据类型:List------------
#观察a、b、c的值和物理地址----------------
>>> a[3][1] = 5
>>> a
[2, 2.5, 'my', [1, 5, 'x']]
>>> b
[1, 1.5, 'nx', [1, 5, 'x']]
>>> c
[1, 1.5, 'nx', [1, 2, 'x']]
>>> id(a)
4530727496
>>> id(b)
4530693576
>>> id(c)
4530727304
#--我们发现b内列表元素跟着变化了------------
>>> id(a[3])
4530727368
>>> id(b[3])
4530727368
>>> id(c[3])
4530727240

根据上述试验可知:
b = copy.copy(a):浅复制

当对象a内的元素为不可变数据类型时,浅复制会让b指向另一个物理地址存储新的、复制的对象
当对象a内的元素为可变数据类型时,浅复制后,a内可变元素所在的物理位置和b内对应的元素位置指向的物理位置相同
即:id(a[3]) == id(b[3])

c = copy.deepcopy(a):深复制

a内的可变数据类型、不可变数据类型对象,均会被复制到新的物理地址,实现真正的复制,所以对a的任何更改都不会影响到c

猜你喜欢

转载自blog.csdn.net/qq_22022063/article/details/80555333