python 深复制对象_Python对象的浅复制与深复制

python 深复制对象

Assignment 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.

Python中的赋值语句不会创建对象的副本,它们只会将名称绑定到对象。 对于不可变的对象,通常没有什么不同。

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 automatically modifying the original at the same time. In this article I’m going to give you the rundown on how to copy or “clone” objects in Python 3 and some of the caveats involved.

本质上,您有时会希望获得可以修改的副本,而无需同时自动修改原始副本。 在本文中,我将向您简要介绍如何在Python 3中复制或“克隆”对象以及其中的一些注意事项。

Note: This tutorial was written with Python 3 in mind but there is little difference between Python 2 and 3 when it comes to copying objects. When there are differences I will point them out in the text.

注意:本教程是在考虑Python 3的情况下编写的,但是在复制对象方面,Python 2和3之间没有什么区别。 如果存在差异,我会在文本中指出。

Free Bonus: Click here to get access to a chapter from Python Tricks: The Book that shows you Python’s best practices with simple examples you can apply instantly to write more beautiful + Pythonic code.

免费红利: 单击此处可访问Python技巧的一章:该书通过简单的示例向您展示了Python的最佳实践,您可以立即应用这些示例编写更精美的Pythonic代码。

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:

让我们从如何复制Python的内置集合开始。 可以通过在现有集合上调用它们的工厂函数来复制Python的内置可变集合(如列表,字典和集合):

 new_list new_list = = listlist (( original_listoriginal_list )
)
new_dict new_dict = = dictdict (( original_dictoriginal_dict )
)
new_set new_set = = setset (( original_setoriginal_set )
)

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

但是,此方法不适用于自定义对象,最重要的是,它仅创建浅表副本。 对于列表,字典和集合之类的复合对象,浅表复制和深表复制之间存在重要区别:

  • A shallow copy 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 makes the copying process recursive. It means first constructing a new collection object and then recursively populating it with copies of the child objects found in the original. Copying an object this way walks the whole object tree to create a fully independent clone of the original object and all of its children.

  • 浅表副本意味着构造一个新的集合对象,然后使用对原始对象中子对象的引用来填充它。 本质上,浅表副本仅深一层。 复制过程不会递归,因此不会创建子对象本身的副本。

  • 深度复制使复制过程是递归的。 这意味着首先构造一个新的集合对象,然后以递归方式填充原始对象中找到的子对象的副本。 以这种方式复制对象会遍历整个对象树,以创建原始对象及其所有子对象的完全独立的克隆。

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.

我知道,这有点令人mouth舌。 因此,让我们看一些示例来了解深层副本和浅层副本之间的差异。

进行浅拷贝 (Making Shallow Copies)

In the example below, we’ll create a new nested list and then shallowly copy it with the list() factory function:

在下面的示例中,我们将创建一个新的嵌套列表,然后使用list()工厂函数对其进行浅表复制:

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:

这意味着ys现在将是一个新的独立对象,其内容与xs相同。 您可以通过检查两个对象来验证这一点:

 >>> >>>  xs
xs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> >>>  ys
ys
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[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):

为了确认ys确实独立于原始数据,让我们进行一些实验。 您可以尝试向原始文件( xs )添加一个新的子列表,然后检查以确保此修改不会影响副本( ys ):

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 of the original list, ys still contains references to the original child objects stored in xs.

但是,由于我们仅创建了原始列表的浅表副本,因此ys仍包含对存储在xs的原始子对象的引用。

These children were not copied. They were merely referenced again in the copied list.

这些孩子没有被复制。 在复制的列表中仅再次引用了它们。

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. The copy is only a shallow, one level deep copy:

因此,当您修改xs一个子对象时,此修改也将反映在ys ,这是因为两个列表共享相同的子对象。 该副本只是一个浅层次的深副本:

 >>> >>>  xsxs [[ 11 ][][ 00 ] ] = = 'X'
'X'
>>> >>>  xs
xs
[[1, 2, 3], ['X', 5, 6], [7, 8, 9], ['new sublist']]
[[1, 2, 3], ['X', 5, 6], [7, 8, 9], ['new sublist']]
>>> >>>  ys
ys
[[1, 2, 3], ['X', 5, 6], [7, 8, 9]]
[[1, 2, 3], ['X', 5, 6], [7, 8, 9]]

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 copy of the original list.

在上面的示例中,我们(貌似)仅对xs进行了更改。 但事实证明, xsys中位于索引1的两个子列表均已修改。 再次发生这种情况是因为我们只创建了原始列表的浅表副本。

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.

如果我们在第一步中创建了xs的深层副本,那么这两个对象将是完全独立的。 这是对象的浅层副本与深层副本之间的实际区别。

Now you know how to create shallow copies of some of the built-in collection classes, and you know the difference 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.

这些问题的答案位于Python标准库中的copy模块中。 该模块提供了一个简单的接口,用于创建任意Python对象的浅层副本和深层副本。

进行深拷贝 (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 copy using the deepcopy() function defined in the copy module instead:

让我们重复前面的列表复制示例,但有一个重要区别。 这次,我们将使用在copy模块中定义的deepcopy()函数来创建深层副本:

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:

当检查xs及其使用copy.deepcopy()创建的克隆zs ,您会发现它们看上去再次相同,就像前面的示例一样:

 >>> >>>  xs
xs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> >>>  zs
zs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[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).

但是,如果您对原始对象( xs )中的一个子对象进行了修改,则会看到此修改不会影响深层副本( zs )。

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

这两个对象(原始对象和副本)这次都是完全独立的。 xs是递归克隆的,包括其所有子对象:

You might want to take some time to sit down with the 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.

您可能需要花一些时间坐在Python解释器上,并立即浏览这些示例。 当您亲身体验和玩这些示例时,将头放在复制对象上会更容易。

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.

顺便说一句,您还可以使用copy模块中的功能创建浅表副本。 copy.copy()函数创建对象的浅表副本。

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 create shallow copies.

如果您需要清楚地表明您正在代码中的某个位置创建浅表副本,这将非常有用。 使用copy.copy()可以表明这一事实。 但是,对于内置集合,认为仅使用列表,字典和设置工厂函数来创建浅表副本就更像Python。

复制任意Python对象 (Copying Arbitrary Python 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.

copy模块再次为我们提供了帮助。 它的copy.copy()copy.deepcopy()函数可用于复制任何对象。

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:

再一次,了解如何使用它们的最佳方法是一个简单的实验。 我将基于前面的列表复制示例。 让我们从定义一个简单的2D点类开始:

 class class PointPoint :
    :
    def def __init____init__ (( selfself , , xx , , yy ):
        ):
        selfself .. x x = = x
        x
        selfself .. y y = = y

    y

    def def __repr____repr__ (( selfself ):
        ):
        return return ff 'Point('Point( {self.x!r}{self.x!r} , ,  {self.y!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.

我希望您同意这很简单。 我添加了__repr__()实现,以便我们可以轻松地检查在Python解释器中从此类创建的对象。

Note: The above example uses a Python 3.6 f-string to construct the string returned by __repr__. On Python 2 and versions of Python 3 before 3.6 you’d use a different string formatting expression, for example:

注意:上面的示例使用Python 3.6 f字符串构造__repr__返回的字符串。 在Python 2和3.6之前的Python 3版本上,您将使用不同的字符串格式表达式,例如:

 def def __repr____repr__ (( selfself ):
    ):
    return return 'Point('Point( %r%r , ,  %r%r )' )' % % (( selfself .. xx , , selfself .. yy )
)

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

接下来,我们将创建一个Point实例,然后使用copy模块(浅)复制它:

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

如果检查原始Point对象及其(浅)克隆的内容,我们将看到期望的结果:

 >>> >>>  a
a
Point(23, 42)
Point(23, 42)
>>> >>>  b
b
Point(23, 42)
Point(23, 42)
>>> >>>  a a is is b
b
False
False

Here’s something else to keep in mind. Because our point object uses primitive 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.

这里还有其他需要注意的地方。 因为我们的点对象使用原始类型(ints)作为其坐标,所以在这种情况下,浅表副本和深表副本之间没有区别。 但我将在稍后扩展该示例。

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:

让我们继续一个更复杂的例子。 我将定义另一个类来表示2D矩形。 我将以允许我们创建更复杂的对象层次结构的方式来进行操作-我的矩形将使用Point对象代表其坐标:

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

同样,首先,我们将尝试创建矩形实例的浅表副本:

 rect rect = = RectangleRectangle (( PointPoint (( 00 , , 11 ), ), PointPoint (( 55 , , 66 ))
))
srect srect = = copycopy .. copycopy (( rectrect )
)

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:

如果检查原始矩形及其副本,您将看到__repr__()重写的效果如何,并且浅表复制过程按预期工作:

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 deeper in the object hierarchy, and then you’ll see this change reflected in the (shallow) copy as well:

还记得前面的列表示例如何说明深层副本和浅层副本之间的区别吗? 我将在这里使用相同的方法。 我将在对象层次结构的更深处修改一个对象,然后您还将看到此更改也反映在(浅)副本中:

 >>> >>>  rectrect .. toplefttopleft .. x x = = 999
999
>>> >>>  rect
rect
Rectangle(Point(999, 1), Point(5, 6))
Rectangle(Point(999, 1), Point(5, 6))
>>> >>>  srect
srect
Rectangle(Point(999, 1), Point(5, 6))
Rectangle(Point(999, 1), Point(5, 6))

I hope this behaved how you 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:

我希望这能像您期望的那样表现。 接下来,我将创建原始矩形的深层副本。 然后,我将应用另一种修改,您将看到受影响的对象:

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

瞧! 这次,深层副本( drect )完全独立于原始副本( rect )和浅层副本( srect )。

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 you may want to study up on the copy module documentation. For example, objects can control how they’re copied by defining the special methods __copy__() and __deepcopy__() on them.

深入了解该主题很值得(ha!),因此您可能需要研究copy模块文档 。 例如,对象可以通过在对象上定义特殊方法__copy__()__deepcopy__()来控制如何复制它们。

3件事要记住 (3 Things to Remember)

  • Making a shallow copy of an object won’t clone child objects. Therefore, the copy is not fully independent of the original.
  • A deep copy of an object will recursively clone child objects. The clone is fully independent of the original, but creating a deep copy is slower.
  • You can copy arbitrary objects (including custom classes) with the copy module.
  • 制作对象的浅表副本不会克隆子对象。 因此,副本并不完全独立于原件。
  • 对象的深层副本将递归克隆子对象。 克隆完全独立于原始副本,但创建深层副本的速度较慢。
  • 您可以使用copy模块复制任意对象(包括自定义类)。

Free Bonus: Click here to get access to a chapter from Python Tricks: The Book that shows you Python’s best practices with simple examples you can apply instantly to write more beautiful + Pythonic code.

免费红利: 单击此处可访问Python技巧的一章:该书通过简单的示例向您展示了Python的最佳实践,您可以立即应用这些示例编写更精美的Pythonic代码。

翻译自: https://www.pybloggers.com/2018/01/shallow-vs-deep-copying-of-python-objects/

python 深复制对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值