85、Python之鸭子类型:魔术方法&深拷贝和浅拷贝

南宫理的日志录 2024-11-11 10:46:37
引言

在Python中,一切皆对象。虽然都是对象,又可以分为可变对象和不可变对象。我们可以通过赋值操作,实现一个看似拷贝对象的作用,但是,由于“变量的标签模型”,我们知道赋值实现的拷贝,本质上只是对同一个对象的两个指向。Python中还提供了浅拷贝、深拷贝的功能,作用于不同的对象类型,又会又不同的呈现。涉及到自定义类型的拷贝行为,我们可以通过定义相应的魔术方法,从而自定义其拷贝行为。

本文的主要内容有:

1、赋值实现的对象“拷贝”

2、浅拷贝与深拷贝

3、通过魔术方法控制浅拷贝与深拷贝

赋值实现的对象“拷贝”

由于在Python一切皆对象的设计理念下,Python中的变量赋值,不再是简单的盒子模型,而是标签模型。

所以,通过赋值实现的“拷贝”,还是同一个对象,通过id()可以清楚地看到这一点。由于前面已经介绍过,这里就不再展开了。

浅拷贝与深拷贝

除了赋值、重新创建对象之外,Python中提供了copy模块,可以支持更加便捷的对象拷贝的功能。

从文档中,可以看到关于浅拷贝(shadow copy)和深拷贝(deep copy)的解释。

先说结论:

1、对于可变对象,比如列表、字典等,浅拷贝会创建一个新的对象(id不同),新的对象存储的跟老的对象存储的内容完全相同,也就是浅拷贝只会拷贝第一层,容器内部有可变类型的元素,其实是指向源对象的元素对象的。

2、对于可变对象,深拷贝会递归进行可变对象及其元素的拷贝,不断产生新的对象,直到遇到不可变对象为止。

3、对于不可变对象,浅拷贝,不会创建新的对象,也就是第一层都不会拷贝。

4、对于不可变对象,深拷贝,分两种情况,如果不可变对象的内部元素也都是不可变对象,则一层也不会拷贝。如果不可变对象的内部元素是可变对象,则会首先会创建一个新的对象,然后再递归看内部是否还有可变对象。

接下来,我们通过代码简单验证一下浅拷贝与深拷贝的使用:

运行结果:

通过运行结果,可以看到对于可变对象的浅拷贝与深拷贝的处理逻辑。

接下来,再来验证一下,不可变对象的浅拷贝与深拷贝。

首先看一下,不可变对象的内部元素也都是不可变对象的拷贝:

运行结果:

从结果可以看出,没有产生任何新的对象。

如果一个不可变对象的内部元素是可变对象呢:

运行结果:

深拷贝时,虽然从外部看元组是不可变对象,但是内部元素是列表,所以本身还是可变的,所以会穿透,直到所有的元素都是不可变对象了。

通过魔术方法控制浅拷贝与深拷贝

接下来,我们看下浅拷贝和深拷贝应用与自定义类型的默认效果:

运行结果:

从运行结果可以看出,自定义类型的浅拷贝和深拷贝机制,与内置类型是基本一致的。浅拷贝只创建一个新对象,内部全是引用源对象。深拷贝则是递归进行新对象的创建。

如果,我们需要自定义浅拷贝与深拷贝的实现机制,可以通过魔术方法进行:

1、__copy__()方法,用于实现浅拷贝的行为。

2、__deepcopy__()方法,用于实现深拷贝的行为。

我们通过代码简单演示一下:

总结

本文介绍了三种对象“拷贝”的方式,也就是赋值、浅拷贝、深拷贝。对比了浅拷贝和深拷贝的运行机制。最后,简单演示了通过魔术方法__copy__()和__deepcopy__()进行自定义浅拷贝和深拷贝行为。

感谢您的拨冗阅读,希望对您有所帮助。

0 阅读:3

南宫理的日志录

简介:深耕IT科技,探索技术与人文的交集