赋值,浅复制与深复制

在python中,一切皆对象(object),这是说python将所有数据类型例如整数,浮点数,字符串甚至函数都当作对象处理。它们都有自己的地址,数据类型,值(以及方法)。

对象(object)就像一个黑盒,里面装着数据。对象有不同的类型,类型决定了可以对它进行的操作。

我们要明白一点,在python中,对象在赋值之前就已经创建了;是把变量分配给对象,而不是反过来。

x=5
y=[1,2,3]
print(id(x),type(x),x)
print(id(y),type(y),y)

'''
140711537158048 <class 'int'> 5
2232107974208 <class 'list'> [1, 2, 3]
'''

根据对象的更新方式,即对象本身是否可以改变,python数据类型可以分为两类:可变对象(它装着的数据是允许被修改的)和不可变对象(数据是不可修改的常量)。

  • 不可变对象有:整数,浮点数,字符串,元组。对象的地址,值不可变。如果变量引用了新的对象,原对象将直接被回收。
  • 可变对象有:列表,字典,集合。对象的值可变,地址不变。

 

x=[1,2,3,4,5]
print(id(x))
x=[1,2,3,4,5]#重新赋值
print(id(x))
x.pop(0)#删
print(id(x))
x.append(6)#增
print(id(x))
x+=[7,8,9]#改
print(id(x))
x=[2,3,4,5,89,75]#重新赋值
print(id(x))
'''
1975804145408
1975804142208不变
1975804142208不变
1975804142208不变
1975804142208不变
1975804145408

'''

如果将一个数据赋给多个变量(不是变量赋给变量)会怎样呢?

  • 对不可变对象的变量名进行修改,也就是将一个不可变类型数据赋给多个变量,不会影响到数据在内存中的位置。
  • 而将一个可变类型数据赋给多个变量时,会开辟新的内存空间,它们的地址不同。这意味着它们是两个完全不同的对象,包括父对象和嵌套的子对象。

python 中比较两个对象的是否相等,通常有两种形式:

1. `is`:判断两个变量所指对象是否相同,即地址是否相同(同一个对象)。

2. `==`:判断两个变量所指对象的值是否相同。

 

x=5
y=5
print(id(x),id(y),x is y)#整数,相同
x=[1]
y=[1]
print(id(x),id(y),x is y)#列表,不同
x=(1)
y=(1)
print('truple:',id(x),id(y))#元组,相同
x={1:3}
y={1:3}
print('dictionary:',id(x),id(y))#字典,不同
x=[1]
y=[1]
print('list:',id(x),id(y))#列表,不同

'''
140732431083424 140732431083424 True
1733390548096 1733390544832 False
truple: 140732431083296 140732431083296
dictionary: 1685228416896 1685228416960
list: 1685346823360 1685346820160

'''

在为函数传值时,函数不会对不可变对象产生影响,类似于值传递。但会修改可变对象,类似引用传递。

 

def fun0(x):
    x+=1
x=5
fun0(x)
print(x)
'''
5
'''

def fun1(x):
    x[0]=1
    x+=[4,5,6]
x=[2,3]
fun1(x)
print(x)
'''
[1, 3, 4, 5, 6]

'''

 

下面来看变量赋给变量。

1.赋值:python使用=进行赋值。

直接赋值实际上是传递了对象的引用,即地址。对新变量的操作(可变对象)会影响到所有引用该对象的变量。所有变量都是对象的地址引用(无论是数据赋值还是变量赋值)

python中的变量仅仅是个名字而已,赋值操作不会实际复制值,它只是为对象取个名字。变量就是贴在对象上的标签。

 

x={1:3,2:4}
y=x
print(id(x),id(y))#相同
print(x is y)#True
print(x==y)#True
x.pop(2)#y同时也被修改
print(y)

'''
2809043152768 2809043152768
True
True
{1:3}
'''

2.浅复制:使用copy模块的copy函数,对象的copy等方法实现。浅复制创建一个新对象,但对象中的子对象不变,仍是引用。

 

x=[1,2,[1,2,3]]
y=copy.copy(x)#浅复制
print(id(x),id(y))
del x[2][0]
print(id(x))
print(y)

'''
1798056423360 1798056423872
1798056423360
[1, 2, [2, 3]]
'''

可以看出,浅复制仅仅复制第一层,子对象仍是引用。

  • 对不可变类型的浅复制:
    import copy
    x=5#元组,浮点数,字符串等是同样的效果
    y=copy.copy(x)
    print(x is y)
    print(id(x),id(y))
    '''
    True
    140732427085728 140732427085728
    
    '''

     

     

    对不可变类型的浅复制不改变对象地址,只是换了变量名称;它们仍指向同一个对象。
  • 对可变类型的浅复制:
    import copy
    x=[1,2,[4,5]]
    y=copy.copy(x)
    print(x is y)
    print(id(x),id(y))
    print(x[0] is y[0])
    print(x[2] is y[2])
    
    '''
    False
    2481531232832 2481531233280
    True
    True
    '''
    可以看出,列表与浅复制的对象地址不同,但其子对象即元素地址相同,仍然是引用。修改了嵌套的可变对象数据值,复制的对象也会被改变。

3.深复制

  • 对不可变类型的深复制:
    import copy
    x='libeiqun'
    y=copy.deepcopy(x)
    print(id(x),id(y))
    print(x is y)
    
    '''
    2153490540528 2153490540528
    True
    
    '''

     

    与浅复制,赋值并无区别。
  • 对可变类型的深复制:
    import copy
    x=[1,2,[4,5]]
    y=copy.deepcopy(x)
    print(id(x),id(y))#不同
    print(x is y)#False
    print(id(x[0]),id(y[0]))#相同,因为是不可变类型
    print(id(x[2]),id(y[2]))#不同,可变类型
    print(x[2] is y[2])#False
    
    '''
    2176108699840 2176108700288
    False
    140732431083296 140732431083296
    2176108703040 2176108686912
    False
    
    '''
    
    x=[[[[1]]]]
    y=copy.deepcopy(x)
    print(id(x[0]),id(y[0]))
    print(id(x[0][0]),id(y[0][0]))
    print(id(x[0][0][0]),id(y[0][0][0]))
    
    '''
    2397555564672 2397555565248
    2397555564160 2397555566080
    2397555567424 2397555565056
    '''
    #深复制嵌套可变类型,每一层嵌套都不同。而浅复制则相同
    1.深复制复制最外层数据,并请求新的内存地址来存放。2.深复制对里面的不可变数据类型直接复制数据和地址,和可变类型的浅复制效果相同。

总结:

  1. 对于不可变对象,只要一建立,系统中只会存在一个,如果没有变量与之关联,系统将会立刻回收;所有的与之关联的变量都指向同一个对象,无论它是不是被嵌套在可变对象里。对其进行深浅拷贝结果相同,地址相同。
  2. 对于可变对象:浅复制和深复制都会申请一块新的内存,但浅复制只复制最外一层可变数据;深复制复制所有嵌套可变类型数据。不可变类型仍然指向原来的对象。

 

posted @ 2021-08-09 15:30  zeroy610  阅读(59)  评论(0编辑  收藏  举报