Cloning Objects for Fun and Profit

简介: Cloning Objects for Fun and Profit

Cloning Objects for Fun and Profit
Aassignment statements in Python do not create copies of objects, they only bind names to an object. For immutable objects, that usually doesn’t make a difference.

But for working with mutable objects or collections of mutable objects, you might be looking for a way to create “real copies” or “clones” of these objects.

Essentially, you’ll sometimes want copies that you can modify without

Let’s start by looking at how to copy Python’s built-in collections. Python’s built-in mutable collections like lists, dicts, and sets can be copied by calling their factory functions on an existing collection:

In [1]: new_list = list(original_list)
In [2]: new_dict = dict(original_dict)
In [3]: new_set = set(original_set)

However, this method won’t work for custom objects and, on top of that, it only creates shallow copies. For mutable collections like lists, dicdts, and sets, there’s an important difference between shallow and deep copying:

A shallow copying means constructing a new collection object and then populating it with references to the child objects found in the original, In essence, a shallow copy is only one level deep. The copying process does not recurse and therefore won’t create copies of the child objects themselves.

A deep copy

I know, that was a bit of a mouthful. So let’s look at some examples to drive home this difference between deep and shallow copies.

Making Shallow Copies

In the example below, we’ll create a new nested list and then shallowly

In [2]: xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

In [3]: ys = list(xs)  # Make a shallow copy

This means ys will now be a new and independent object with the same contents as xs. You can verify this by inspecting both objects:

In [5]: xs
Out[5]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
In [4]: ys
Out[4]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

To confirm ys really is independent from the original, let’s devise a little experiment. You could try and add a new sublist to the original (xs) and then check to make sure this modification didn’t affect the copy(ys):

In [6]: xs.append(['new sublist'])
In [7]: xs
Out[7]: [[1, 2, 3], [4, 5, 6], [7, 8, 9], ['new sublist']]

In [8]: ys
Out[8]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

As you can see, this had the expected effect. Modifying the copied list at a “superficial” level was no problem at all.

However, because we only created a shallow copy

These children were not

Therefore, when you modify one of the child objects in xs, this modification will be reflected in ys as well–that’s because both lists share the same child objects.

In [9]: xs[1][0] = 'X'

In [10]: xs
Out[10]: [[1, 2, 3], ['X', 5, 6], [7, 8, 9], ['new sublist']]

In the above example we(seemingly) only made a change to xs. But it turns out that both sublists at index 1 in xs and ys were modified. Again, this happened because we had only created a shallow

Had we created a deep copy of xs in the first step, both objects would’ve been fully independent. This is the practical difference between shallow and deep copies of objects.

Now you know how to create shallow copies of some of the built-in collection classes, and you know the diffrence between shallow and deep copying. The questions we still want answers for are:

How can you create deep copies of built-in collections?
How can you create copies(shallow and deep) of arbitrary objects, including custom classes?
The answer to these questions lies in the copy module in the Python standard library. This module provides a simple interface for creating shallow and deep copies of arbitrary Python objects.

Making Deep Copies

Let’s repeat the previous list-copying example, but with one important difference. This time we’re going to create a deep

In [13]: import copy
In [14]: xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
In [16]: zs = copy.deepcopy(xs)

When you inspect xs and its clone zs that we created with copy.deepcopy(), you’ll see that they both look identical again - just like in the previous example:

In [17]: xs
Out[17]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

In [18]: zs
Out[18]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

However, if you make a modification to one of the child objects in the original object(xs). You’ll see that this modification won’t affect the deep copy (zs).

Both objects, the original and the copy, are fully independent this time. xs was cloned recursively, including all of its child objects:

In [20]: xs[1][0] = 'X'

In [21]: xs
Out[21]: [[1, 2, 3], ['X', 5, 6], [7, 8, 9]]
In [22]: zs
Out[22]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

You might want to take some time to sit down with Python interpreter and play through these examples right about now. Wrapping your head around copying objects is easier when you get to experience and play with the examples firsthand.

By the way, you can also create shallow copies using a function in the copy module. The copy.copy() function creates shallow copies of objects.

This is useful if you need to clearly communicate that you’re creating a shallow copy somewhere in your code. Using copy.copy() lets you indicate this fact. However, for built-in collections it’s considered more Pythonic to simply use the list, dict, and set factory functions to creat shallow copies.

Copying Arbitrary Objects

The question we still need to answer is how do we create copies( shallow and deep) of arbitrary objects, including custom classes. Let’s take a look at that now.

Again the copy module comes to our rescue. Its copy.copy() and copy.deepcopy() functions can be used to duplicate any object.

Once again, the best way to understand how to use these is with a simple experiment. I’m going to base this on the previous list-copying example. Let’s start by defining a simple 2D point class:

In [23]: class Point:
    ...:     def __init__(self, x, y):
    ...:         self.x = x
    ...:         self.y = y
    ...:     def __repr__(self):
    ...:         return f'Point({self.x!r}, {self.y!r})'

I hope you agree that this was pretty straightforward. I added a repr() implementation so that we can easily inspect objects created from this class in the Python interpreter.

Next up, we’ll create a Point instance and then (shallowly) copy it, using the copy module:

In [25]: a = Point(23, 42)

In [26]: b = copy.copy(a

If we inspect the contents of the original Point object and its (shallow) clone, we see what we’d expect:

In [27]: a
Out[27]: Point(23, 42)

In [28]: b
Out[28]: Point(23, 42)

In [29]: a is b
Out[29]: False

Here’s something else to keep in mind. Because our point object uses immutable types(ints) for its coordinates, there’s no difference between a shallow and a deep copy in this case. But I’ll expand the example in a second.

Let’s move on to a more complex example. I’m going to define another class to represent 2D rectangles. I’ll do it in a way that allows us to create a more complex object hierarchy- my rectangles will use Point objects to represent their coordinates:

In [32]: class Rectangle:
    ...:     def __init__(self, topleft, bottomright):
    ...:         self.topleft = topleft
    ...:         self.bottomright = bottomright
    ...:     def __repr__(self):
    ...:         return (f'Rectangle({self.topleft!r}),'
    ...:                 f'{self.bottomright!r})')

Again, first we’re going to attempt to create a shallow copy of a rectangle instance:

In [33]: rect = Rectangle(Point(0, 1), Point(5, 6))

In [34]: srect = copy.copy(rect)

If you inspect the original rectangle and its copy, you’ll see how nicely the repr() override is working out, and that the shallow copy process worked as expected:

In [35]: rect
Out[35]: Rectangle(Point(0, 1), Point(5, 6))
In [36]: srect
Out[36]: Rectangle(Point(0, 1), Point(5, 6))
In [37]: rect is srect
Out[37]: False

Remember how the previous list example illustrated the difference between deep and shallow copies? I’m going to use the same approach here. I’ll modify an object futher down in the object hierarchy, and then you’ll see this change refelcted in the (shallow) copy as well:

In [39]: rect
Out[39]: Rectangle(Point(999, 1)),Point(5, 6))
In [40]: srect
Out[40]: Rectangle(Point(999, 1)),Point(5, 6))

I hope this behaved how yo expected it to. Next, I’ll create a deep copy of the original rectangle. Then I’ll apply another modification and you’ll see which objects are affected:

In [41]: drect = copy.deepcopy(srect)
In [43]: drect.topleft.x = 222

In [44]: drect
Out[44]: Rectangle(Point(222, 1)),Point(5, 6))

In [45]: rect
Out[45]: Rectangle(Point(999, 1)),Point(5, 6))

In [46]: srect
Out[46]: Rectangle(Point(999, 1)),Point(5, 6))

Voila! This time the deep copy (direct) is fully independent of the original(rect) and the shallow copy(secret).

We’ve covered a lot of ground here, and there are still some finer points to copying objects.

It pays to go deep(ha!) on this topic, so yo may want to study up on the copy module documentation, and possibly even the module’s source code. For example, objects can control how they’re copied by defining the sepcial methods copy() and deepcopy() on them. Have fun!☕️

相关文章
|
6月前
|
JSON API 开发者
python实战 | 如何利用海外代理IP,实现Facebook内容营销自动化
本文探讨了Facebook营销自动化中的挑战与解决方案。首先分析了账号风控、IP受限及手动操作效率低等问题,随后介绍了通过Python编程结合高质量海外代理IP(如青果网络)实现自动化的技术路径。内容涵盖环境配置、代理IP使用、Facebook开发者账号注册及两种自动化方法:Graph API动态发布与Selenium模拟用户操作。最后总结指出,该方案可扩展至其他平台,助力全球矩阵营销。
python实战 | 如何利用海外代理IP,实现Facebook内容营销自动化
|
7月前
|
人工智能 自然语言处理 运维
【新模型速递】PAI一键云上零门槛部署DeepSeek-V3-0324、Qwen2.5-VL-32B
PAI-Model Gallery 集成国内外 AI 开源社区中优质的预训练模型,涵盖了 LLM、AIGC、CV、NLP 等各个领域,用户可以通过 PAI 以零代码方式实现从训练到部署再到推理的全过程,获得更快、更高效、更便捷的 AI 开发和应用体验。 现阿里云PAI-Model Gallery已同步接入DeepSeek-V3-0324、Qwen2.5-VL-32B-Instruct两大新模型,提供企业级部署方案。
|
10月前
|
消息中间件 运维 安全
C5GAME 游戏饰品交易平台借助 RocketMQ Serverless 保障千万级玩家流畅体验
游戏行业蓬勃发展,作为国内领先的 STEAM 游戏饰品交易的服务平台,看 C5GAME 如何利用 RocketMQ Serverless 技术,为千万级玩家提供流畅的游戏体验,同时降低成本并提升运维效率。
548 144
C5GAME 游戏饰品交易平台借助 RocketMQ Serverless 保障千万级玩家流畅体验
|
12月前
|
小程序 前端开发 测试技术
微信小程序的开发完整流程是什么?
微信小程序的开发完整流程是什么?
1788 7
|
12月前
|
Java 关系型数据库 MySQL
如何用java的虚拟线程连接数据库
本文介绍了如何使用Java虚拟线程连接数据库,包括设置JDK版本、创建虚拟线程的方法和使用虚拟线程连接MySQL数据库的示例代码。
214 6
如何用java的虚拟线程连接数据库
|
12月前
|
SQL 存储 人工智能
OceanBase CTO杨传辉谈AI时代下数据库技术的创新演进路径!
在「DATA+AI」见解论坛上,OceanBase CTO杨传辉先生分享了AI与数据库技术融合的最新进展。他探讨了AI如何助力数据库技术演进,并介绍了OceanBase一体化数据库的创新。OceanBase通过单机分布式一体化架构,实现了从小规模到大规模的无缝扩展,具备高可用性和高效的数据处理能力。此外,OceanBase还实现了交易处理、分析和AI的一体化,大幅提升了系统的灵活性和性能。杨传辉强调,OceanBase的目标是成为一套能满足80%工作负载需求的系统,推动AI技术在各行各业的广泛应用。关注我们,深入了解AI与大数据的未来!
OceanBase CTO杨传辉谈AI时代下数据库技术的创新演进路径!
|
12月前
|
Java
方法重载和方法重写有什么区别?
本文解释了Java中方法重载(Method Overloading)和方法重写(Method Overriding)的区别,包括它们的定义位置、参数要求、返回值要求、业务功能以及绑定方式的不同,并提供了示例代码。
284 2
方法重载和方法重写有什么区别?
|
算法
PID算法原理分析及优化
今天为大家介绍一下经典控制算法之一的PID控制方法。PID控制方法从提出至今已有百余年历史,其由于结构简单、易于实现、鲁棒性好、可靠性高等特点,在机电、冶金、机械、化工等行业中应用广泛。 在大学期间,参加的智能汽车竞赛中就使用到了PID经典控制算法,对于智能小车的调试更加的方便。 一、PID原理 PID控制方法将偏差的比例(proportional)、积分(integral)、微分(derivative)通过线性组合构成控制量,对被控对象进行控制。 常规的PID控制系统如图所示: 系统的输入r(t)为控制量的目标输出值,输出y(t)为控制量的实际输出值,e(t)为输出量目标值与实际值
303 1
|
12月前
|
NoSQL 测试技术 Redis
第一次面试总结 - 迈瑞医疗 - 软件测试
本文是作者对迈瑞医疗软件测试岗位的第一次面试总结,面试结果非常好,但面试过程中没有提问太多技术性问题,主要围绕个人介绍、互相了解、个人规划和项目亮点进行,因此作者认为这次面经的学习意义不大。作者还提到了实习岗位的待遇和工作内容,以及对不同阶段求职者的建议。
327 2
|
12月前
|
Web App开发 Java 测试技术
使用selenium+chromedriver+xpath爬取动态加载信息(一)
使用selenium+chromedriver+xpath爬取动态加载信息(一)
248 1