python对象赋值、浅复制、深复制的区别

python对象赋值、浅复制、深复制的区别

前言

与任何编程语言一样,python的对象是存放在某个内存块当中的。python的id函数的作用是求对象的内存地址。例如,

a = 1
id(a)
1727064528

a = 'hello'
id(a)
2336504646432

python有“一切皆对象”的口号。在python中,哪怕是在别的面向对象语言中被定义为基本数据类型的变量在python中也是一个对象(这点与java中保留了int, float等基本数据类型不同)。一个有趣的现象是,python中给同一个变量名的变量两次赋不同的常数值,变量的id是不同的。

a = 0
id(a)
1727064496

a = 1
id(a)
1727064528

而且可以注意到,同样的数值,不论变量名是什么,对应的id是一样的。

这与C/C++对int变量的处理截然不同。在C/C++中,对一个int变量赋不同的值,该变量的地址是不会变化的。

#include<iostream>
using namespace std;

int main()
{
    int a = 0;
    cout << &a << endl;	// 0x6dfefc
    a = 1;
    cout << &a << endl;	// 0x6dfefc
    return 0;
}

python中这种对于数值常量的处理与java对于字符串常量类似。

public class Main
{
	public static void main(String args[])
	{
		String s1 = "hello";
		System.out.println(System.identityHashCode(s1));	// 366712642
		s1 = "world";
		System.out.println(System.identityHashCode(s1));	// 1829164700
	}
}

赋值

python中的赋值 x = y 是对对象引用的赋值,并没有产生新的对象。赋值之后引用x与y指向同一个对象,一切对x的修改,对y同样生效,反之亦然。图解如下,

avatar

浅复制

与赋值不同,对象的浅复制会构造新的对象,并把原对象内存地址中的内容复制到新对象中,是一种”按比特的复制”。“按比特”的意思是,浅复制得到的新对象的内容与原对象是完全一样的,如果原对象的A属性值为a,则新对象的A属性值也是a。

但是,正如其名字所揭示的那样,浅复制不能实现对对象的完全复制。问题出在当对象的属性包含对象的引用时。下面代码中的Class类的属性含有对Stu类对象的引用,所以Class对象就属于“包含对象引用的对象”。

class Stu:
	def __init__(self, sid):
		self.sid = sid
		
	def set_id(self, sid):
		self.sid = sid
		
	def get_id(self):
		return self.sid
	
	def set_name(self, name):
		self.name = name
		
	def get_name(self):
		return self.name

		
class Class:
	def __init__(self):
		self.stu_num = 50
		self.monitor = Stu(1)
		self.monitor.set_name("XiaoHong")
	
	def set_stu_num(self, num):
		self.stu_num = num
	
	def set_monitor(self, mid, mname):
		self.monitor.set_id(mid)
		self.monitor.set_name(mname)
		
	def display(self):
		print(f'stu_num: {self.stu_num}; monitor: {self.monitor.get_id()}, {self.monitor.get_name()}')

如上文所述,浅复制对于对象中的内容会“逐比特”拷贝,那么对象中的引用(monitor)也被直接拷贝了,即是说,对于对象中的属性对象(Stu monitor),浅复制进行了类似直接赋值的操作,并没有新的属性对象被构造,原对象和新对象共享同一个属性对象(Stu monitor)。因此,当原对象的属性对象发生变化时,新对象的属性对象也随之发生变化:这种变化很多时候是我们所不期望的。

图解如下,

avatar

python中复制对象需要导入模块

import copy

浅复制的代码是

y = copy.copy(x)

深复制

与浅复制不同,遇到对象内部的属性对象时,深复制会递归地构造新对象并复制属性对象的内容,而非简单地复制属性对象的引用。因此,深复制可以解决浅复制共享属性对象的问题。图解如下,

avatar

深复制的python代码是

import copy

y = copy.deepcopy(x)

一个例子

下面用一个例子比较赋值、深复制、浅复制的差异

import copy

class Stu:
	def __init__(self, sid):
		self.sid = sid
		
	def set_id(self, sid):
		self.sid = sid
		
	def get_id(self):
		return self.sid
	
	def set_name(self, name):
		self.name = name
		
	def get_name(self):
		return self.name

		
class Class:
	def __init__(self):
		self.stu_num = 50
		self.monitor = Stu(1)
		self.monitor.set_name("XiaoHong")
	
	def set_stu_num(self, num):
		self.stu_num = num
	
	def set_monitor(self, mid, mname):
		self.monitor.set_id(mid)
		self.monitor.set_name(mname)
		
	def display(self):
		print(f'stu_num: {self.stu_num}; monitor: {self.monitor.get_id()}, {self.monitor.get_name()}')
		
if __name__ == '__main__':
	# assign
	print('Assign')
	c1 = Class()
	c2 = c1
	print(f'c1 id: {id(c1)}, c2 id: {id(c2)}')	# id(c1) == id(c2)
	c1.set_stu_num(40)
	c1.set_monitor(0, 'XiaoMing')
	print('c1:')								# c1 EQUALS c2
	c1.display()
	print('c2:')
	c2.display()
	print()
		
	# shallow copy
	print('Shallow Copy')
	c1 = Class()
	c2 = copy.copy(c1)
	print(f'c1 id: {id(c1)}, c2 id: {id(c2)}')	# id(c1) != id(c2)
	print(f'c1.monitor id: {id(c1.monitor)}, c2.monitor id: {id(c2.monitor)}')					# id(c1.monitor) == id(c2.monitor)
	c1.set_stu_num(40)
	c1.set_monitor(0, 'XiaoMing')
	print('c1:')					# c1.stu_num != c2.stu_num
	c1.display()					# c1.monitor EQUALS c2.monitor
	print('c2:')
	c2.display()
	print()
	
	# deep copy
	print('Deep Copy')
	c1 = Class()
	c2 = copy.deepcopy(c1)
	print(f'c1 id: {id(c1)}, c2 id: {id(c2)}')	# id(c1) != id(c2)
	print(f'c1.monitor id: {id(c1.monitor)}, c2.monitor id: {id(c2.monitor)}')					# id(c1.monitor) != id(c2.monitor)
	c1.set_stu_num(40)
	c1.set_monitor(0, 'XiaoMing')
	print('c1:')					# c1.stu_num != c2.stu_num
	c1.display()					# c1.monitor NOT EQUAL c2.monitor
	print('c2:')
	c2.display()

这段代码的输出如下

Assign
c1 id: 2427692355480, c2 id: 2427692355480
c1:
stu_num: 40; monitor: 0, XiaoMing
c2:
stu_num: 40; monitor: 0, XiaoMing

Shallow Copy
c1 id: 2427692355536, c2 id: 2427692442288
c1.monitor id: 2427692443296, c2.monitor id: 2427692443296
c1:
stu_num: 40; monitor: 0, XiaoMing
c2:
stu_num: 50; monitor: 0, XiaoMing

Deep Copy
c1 id: 2427692355480, c2 id: 2427692442344
c1.monitor id: 2427692355424, c2.monitor id: 2427692443240
c1:
stu_num: 40; monitor: 0, XiaoMing
c2:
stu_num: 50; monitor: 1, XiaoHong

猜你喜欢

转载自blog.csdn.net/da_kao_la/article/details/85405456