ChatGPT解决这个技术问题 Extra ChatGPT

深拷贝和浅拷贝有什么区别?

这个问题的答案是社区的努力。编辑现有答案以改进这篇文章。它目前不接受新的答案或交互。

深拷贝和浅拷贝有什么区别?


j
johan

广度与深度;考虑以您的对象为根节点的引用树。

浅的:

https://i.stack.imgur.com/49psq.png

变量 A 和 B 指代不同的内存区域,当 B 分配给 A 时,这两个变量指代相同的内存区域。以后对其中一个内容的修改会立即反映在另一个内容中,因为它们共享内容。

深的:

https://i.stack.imgur.com/DRLn7.png

变量 A 和 B 指的是不同的内存区域,当 B 分配给 A 时,A 指向的内存区域中的值被复制到 B 指向的内存区域中。以后对内容的修改对于 A 或 B 仍然是唯一的;内容不共享。


以下是该插图来自的维基百科文章,以防它在上下文中对您没有意义en.wikipedia.org/wiki/Object_copy#Shallow_copy
在浅拷贝的情况下,如果我们对数组 B 进行任何更改,这是否会反映在数组 A 中,因为 A 和 B 都指向相同的内存位置?
在单行中,它的引用复制与值复制。不知道答案是否正确!
@jasonleonhard 所以 9 年前我只是把网址放到图像中,因为不支持嵌入图像。因此 URL 引用了它的来源。社区后来将 URL 制作成嵌入式图像,而无需对其进行某种引用。 4岁的顶评论也指出了你所指出的。看看:stackoverflow.com/posts/184780/revisions 为什么不自己将引文编辑到答案中?下次有人抱怨我 10 岁的写作风格时,我可能无法联系到我。
“对于变量 A 和 B,当 B 分配给 A 时”这不是代码中的“A = B”吗?我有点困惑,因为图像反映了“B = A”。
S
S.Lott

浅拷贝尽可能少地重复。集合的浅拷贝是集合结构的副本,而不是元素的副本。使用浅拷贝,两个集合现在共享单个元素。

深拷贝复制一切。集合的深层副本是两个集合,其中原始集合中的所有元素都重复。


可能是 .NET MemberwiseClone() 实现比传统意义上的浅拷贝更多
请记住,还有混合副本(不仅如 lazy copy),它只复制其中的一部分 (here's an instance)! ;)
什么是集合结构?
@Honey Collections 可以是存储多个数据项的多种数据结构。在 python 中,我们有元组、列表、字典等
@RoyiNamir 您可能在过去 7 年中已经弄清楚了这一点,但是对于其他对此感到疑惑的人:“浅拷贝逐位复制值类型”是正确的,但这有点令人困惑。如果您有一个“具有”Address 对象的 Customer 对象,则“逐位”复制 Customer 对象意味着复制到 Address 对象的 指针/引用 .原始和副本都指向同一个 Address 对象,而深层副本将创建一个新的 Address 对象并指向该对象。
C
Community

尝试考虑下图

https://i.stack.imgur.com/AWKJa.jpg

例如 Object.MemberwiseClone 创建一个 shallow 副本link

并使用 ICloneable 界面,您可以获得 deep 复制,如 here 所述


一张图片胜过千言万语。
哦,男孩,来这里找出意义。这是唯一有帮助的答案。
这是最简单的,但只显示了必要的内容。
最好的插图
该答案解释了按引用复制与按值复制。浅拷贝与深拷贝是适用于集合的概念。请参阅此 answer 和此 answer
R
Ray

简而言之,这取决于什么指向什么。在浅拷贝中,对象 B 指向对象 A 在内存中的位置。在深拷贝中,对象 A 的内存位置中的所有内容都被复制到对象 B 的内存位置。

这篇 wiki 文章有一个很棒的图表。

http://en.wikipedia.org/wiki/Object_copy


m
mfaani

特别是对于 iOS 开发者:

如果 BA浅拷贝,那么对于原始数据,它类似于 B = [A assign];,而对于对象,它类似于 B = [A retain]

B 和 A 指向同一个内存位置

如果 BA深拷贝,那么它就像 B = [A copy];

B 和 A 指向不同的内存位置

内存地址与 A 相同

与 A 的内容相同


“B 内存地址与 A 相同” - 怎么会?
在 Deep Copy 中,“B 内存地址与 A 不同”
M
Martin York

浅拷贝:将成员值从一个对象复制到另一个对象。

深度复制:将成员值从一个对象复制到另一个对象。任何指针对象都会被复制和深度复制。

例子:

class String
{
     int   size;
     char* data;
};

String  s1("Ace");   // s1.size = 3 s1.data=0x0000F000

String  s2 = shallowCopy(s1);
 // s2.size =3 s2.data = 0X0000F000
String  s3 = deepCopy(s1);
 // s3.size =3 s3.data = 0x0000F00F
 //                      (With Ace copied to this location.)

T
Touchstone

只是为了便于理解,您可以阅读这篇文章:https://www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm

浅拷贝:

https://i.stack.imgur.com/nJJ6K.gif

深拷贝:

https://i.stack.imgur.com/R6Jlg.gif


B
Bill K

我在这里没有看到一个简短易懂的答案——所以我会试一试。

对于浅拷贝,源指向的任何对象也被目标指向(因此不会复制引用的对象)。

对于深拷贝,源指向的任何对象都会被拷贝,而目标指向的拷贝会被拷贝(所以现在每个被引用的对象都有 2 个)。这会沿着对象树递归。


h
ha9u63ar

{想象两个对象:相同类型的 A 和 B _t(相对于 C++)并且您正在考虑将 A 浅/深复制到 B}

浅拷贝:简单地将 A 的引用复制到 B 中。将其视为 A 的地址的副本。因此,A 和 B 的地址将相同,即它们将指向相同的内存位置,即数据内容。

深拷贝:简单地对A的所有成员进行拷贝,在不同的位置为B分配内存,然后将拷贝的成员分配给B,实现深拷贝。这样,如果 A 变得不存在,B 在内存中仍然有效。使用的正确术语是克隆,您知道它们完全相同,但又不同(即在内存空间中存储为两个不同的实体)。您还可以提供克隆包装器,您可以在其中通过包含/排除列表决定在深度复制期间选择哪些属性。当您创建 API 时,这是很常见的做法。

仅当您了解所涉及的风险时,您才可以选择进行浅拷贝。当您在 C++ 或 C 中处理大量指针时,对对象进行浅拷贝真的是个坏主意。

示例_OF_DEEP COPY_ 一个示例是,当您尝试进行图像处理和对象识别时,您需要将“不相关和重复运动”从您的处理区域中屏蔽掉。如果您使用的是图像指针,那么您可能有保存这些蒙版图像的规范。现在...如果您对图像进行浅拷贝,当指针引用从堆栈中被 KILLED 时,您会丢失引用及其副本,即在某些时候会出现访问冲突的运行时错误。在这种情况下,您需要的是通过 CLONING 获得图像的深层副本。通过这种方式,您可以检索掩码以备将来需要时使用。

示例_OF_SHALLOW_COPY 与 StackOverflow 中的用户相比,我的知识不是很丰富,所以请随意删除这部分并举一个很好的例子,如果你能澄清的话。但我真的认为,如果您知道您的程序将运行无限时间,即通过函数调用在堆栈上连续“push-pop”操作,那么做浅拷贝不是一个好主意。如果您正在向业余或新手演示某些东西(例如 C/C++ 教程的东西),那么它可能没问题。但是,如果您正在运行诸如监视和检测系统或声纳跟踪系统之类的应用程序,则不应该对对象进行浅层复制,因为它迟早会杀死您的程序。


J
John Dibling
char * Source = "Hello, world.";

char * ShallowCopy = Source;    

char * DeepCopy = new char(strlen(Source)+1);
strcpy(DeepCopy,Source);        

“ShallowCopy”指向与“Source”相同的内存位置。 “DeepCopy”指向内存中的不同位置,但内容相同。


s
smci

什么是浅拷贝?

https://i.stack.imgur.com/LnDQh.png

在该图中,MainObject1 具有 int 类型的字段 field1ContainObject 类型的 ContainObject1。当您对 MainObject1 进行浅拷贝时,会使用 field2 创建 MainObject2,其中包含 field1 的复制值,并且仍然指向 ContainObject1 本身。请注意,由于 field1 是原始类型,它的值被复制到 field2,但由于 ContainedObject1 是一个对象,所以 MainObject2 仍然指向 ContainObject1。因此,对 MainObject1 中的 ContainObject1 所做的任何更改都将反映在 MainObject2 中。

现在如果这是浅拷贝,让我们看看什么是深拷贝?

什么是深拷贝?

https://i.stack.imgur.com/kocda.png

在该图中,MainObject1 具有 int 类型的字段 field1ContainObject 类型的 ContainObject1。当您对 MainObject1 进行深层复制时,会创建 MainObject2,其中 field2 包含 field1 的复制值,ContainObject2 包含 ContainObject1 的复制值。请注意,对 MainObject1 中的 ContainObject1 所做的任何更改都不会反映在 MainObject2 中。

good article


这不是你的错,虽然这个例子提到了一个 field3,当它能够尝试理解像那个问题一样深的东西时,那个例子中的 #3 在哪里发生 ContainObject2
J
Jeffrey L Whitledge

在面向对象编程中,类型包括成员字段的集合。这些字段可以按值或按引用(即指向值的指针)存储。

在浅拷贝中,创建该类型的新实例并将值复制到新实例中。引用指针也像值一样被复制。因此,引用指向原始对象。对通过引用存储的成员的任何更改都会出现在原始和副本中,因为没有对引用的对象进行复制。

在深拷贝中,按值存储的字段像以前一样被复制,但指向按引用存储的对象的指针不会被复制。取而代之的是,对引用的对象进行深层复制,并存储指向新对象的指针。对这些引用对象所做的任何更改都不会影响该对象的其他副本。


J
Jean-François Fabre

深拷贝

深拷贝复制所有字段,并复制字段指向的动态分配内存。当一个对象连同它所引用的对象一起被复制时,就会发生深拷贝。

浅拷贝

浅拷贝是对象的按位拷贝。创建一个新对象,该对象具有原始对象中值的精确副本。如果对象的任何字段是对其他对象的引用,则仅复制引用地址,即仅复制内存地址。


遗憾的是,该链接不再有效 - 它现在指向 2019 年 2 月关于网页设计的一篇文章(除非作者是千里眼?)。
G
GovindaRaju

“ShallowCopy”指向与“Source”相同的内存位置。 “DeepCopy”指向内存中的不同位置,但内容相同。


这有点误导。浅拷贝和深拷贝都会将对象复制到内存中的新位置,深拷贝也会拷贝子对象,而浅拷贝只会让新对象引用旧的子对象。不参考原始对象就很难阅读。
t
thirtydot

我想举个例子而不是正式的定义。

var originalObject = { 
    a : 1, 
    b : 2, 
    c : 3,
};

这段代码显示了一个浅拷贝:

var copyObject1 = originalObject;

console.log(copyObject1.a);         // it will print 1 
console.log(originalObject.a);       // it will also print 1 
copyObject1.a = 4; 
console.log(copyObject1.a);           //now it will print 4 
console.log(originalObject.a);       // now it will also print 4

var copyObject2 = Object.assign({}, originalObject);

console.log(copyObject2.a);        // it will print 1 
console.log(originalObject.a);      // it will also print 1 
copyObject2.a = 4; 
console.log(copyObject2.a);        // now it will print 4 
console.log(originalObject.a);      // now it will print 1

这段代码显示了一个深拷贝:

var copyObject2 = Object.assign({}, originalObject);

console.log(copyObject2.a);        // it will print 1 
console.log(originalObject.a);      // it will also print 1 
copyObject2.a = 4; 
console.log(copyObject2.a);        // now it will print 4 
console.log(originalObject.a);      // !! now it will print 1 !!

我得到1 1 4 4 4 4 4 4
在深拷贝中,执行 copyObject.a = 8 然后检查。希望你能得到正确的答案。
object.assign({},arr) 不会创建深拷贝,假设我们有以下对象 var source = {"foo":1,"name":"Testing",c:{age:34}} var dCopy = Object.assign({},source) console.log(dCopy.c.age) console.log(Source deep ${source.c.age}) source.c.age = 3 console.log(dCopy.c.age) console.log({ 1})
A
Arun Raaj

浅克隆:定义:“对象的浅拷贝复制‘主’对象,但不复制内部对象。”当自定义对象(例如 Employee)只有原始的字符串类型变量时,您可以使用浅克隆。

Employee e = new Employee(2, "john cena");
Employee e2=e.clone();

您在覆盖的 clone() 方法中返回 super.clone();,您的工作就结束了。

深度克隆:定义:“与浅拷贝不同,深度拷贝是对象的完全独立拷贝。”表示当一个 Employee 对象持有另一个自定义对象时:

Employee e = new Employee(2, "john cena", new Address(12, "West Newbury", "Massachusetts");

然后您必须编写代码来克隆“地址”对象以及覆盖的 clone() 方法。否则,Address 对象将不会克隆,并且当您更改克隆的 Employee 对象中的 Address 值时会导致错误,这也反映了原始对象。


D
Dour High Arch
var source = { firstName="Jane", lastname="Jones" };
var shallow = ShallowCopyOf(source);
var deep = DeepCopyOf(source);
source.lastName = "Smith";
WriteLine(source.lastName); // prints Smith
WriteLine(shallow.lastName); // prints Smith
WriteLine(deep.lastName); // prints Jones

这不是一个很好的例子。浅拷贝多用于对象的快速拷贝,不拷贝数据,但是一旦对象需要修改共享数据,就会对其进行深拷贝。您的示例可能会使初学者感到困惑。
这仅适用于使用指针表示字符串的语言。 DHA 试图说明的一点是,浅拷贝只复制指向相同(单一)原始内容的指针,而深拷贝也克隆指针的引用内容。两种方法都复制表面内容。如果语言将字符串存储为表面文字内容,例如在 WAV 标头中,则此示例将不起作用。请注意,对于大多数不深奥的现实问题,这可能过于挑剔。
C
Community

浅复制 - 原始和浅复制对象内的引用变量具有对公共对象的引用。

深度复制 - 原始对象和深度复制对象内的引用变量引用了不同的对象。

克隆总是做浅拷贝。

public class Language implements Cloneable{
    
    String name;
    public Language(String name){
        this.name=name;
    }
    
    public String getName() {
        return name;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

主要课程如下 -

public static void main(String args[]) throws ClassNotFoundException, CloneNotSupportedException{

      ArrayList<Language> list=new ArrayList<Language>();
      list.add(new Language("C"));
      list.add(new Language("JAVA"));

      ArrayList<Language> shallow=(ArrayList<Language>) list.clone();
      //We used here clone since this always shallow copied.

      System.out.println(list==shallow);
      
      for(int i=0;i<list.size();i++)
      System.out.println(list.get(i)==shallow.get(i));//true
      
      ArrayList<Language> deep=new ArrayList<Language>();
      for(Language language:list){
          deep.add((Language) language.clone());
      }
      System.out.println(list==deep);
      for(int i=0;i<list.size();i++)
          System.out.println(list.get(i)==deep.get(i));//false
      
} 

以上的输出将是-

假 真 真 假 假 假

对原始对象所做的任何更改都将反映在浅对象而不是深对象中。

  list.get(0).name="ViSuaLBaSiC";
  System.out.println(shallow.get(0).getName()+"  "+deep.get(0).getName());

输出 - ViSuaLBaSiC C


P
PeerNet

想象一下,有两个数组称为 arr1 和 arr2。

arr1 = arr2;   //shallow copy
arr1 = arr2.clone(); //deep copy

s
santhosh

简单来说,浅拷贝类似于按引用调用,深拷贝类似于按值调用

在引用调用中,函数的形参和实参都引用相同的内存位置和值。

在按值调用中,函数的形参和实参都指不同的内存位置但具有相同的值。


S
Sushant

浅拷贝构造一个新的复合对象并将其引用插入到原始对象中。

与浅拷贝不同,深拷贝构造新的复合对象并插入原始复合对象的原始对象的副本。

让我们举个例子。

import copy
x =[1,[2]]
y=copy.copy(x)
z= copy.deepcopy(x)
print(y is z)

上面的代码打印 FALSE。

让我们看看如何。

原始复合对象 x=[1,[2]](称为复合对象,因为它在对象内部有对象 (Inception))

https://i.stack.imgur.com/QtsjW.png

正如您在图像中看到的,列表中有一个列表。

然后我们使用 y = copy.copy(x) 创建它的浅表副本。 python在这里所做的是,它将创建一个新的复合对象,但其中的对象指向原始对象。

https://i.stack.imgur.com/QLeDN.png

在图像中,它为外部列表创建了一个新副本。但内部列表与原始列表相同。

现在我们使用 z = copy.deepcopy(x) 创建它的深拷贝。 python在这里所做的是,它将为外部列表和内部列表创建新对象。如下图所示(红色突出显示)。

https://i.stack.imgur.com/4fYHO.png

最后代码打印 False,因为 y 和 z 不是同一个对象。

HTH。


n
notytony
struct sample
{
    char * ptr;
}
void shallowcpy(sample & dest, sample & src)
{
    dest.ptr=src.ptr;
}
void deepcpy(sample & dest, sample & src)
{
    dest.ptr=malloc(strlen(src.ptr)+1);
    memcpy(dest.ptr,src.ptr);
}

V
VSS

要为其他答案添加更多内容,

对象的浅拷贝对基于值类型的属性执行按值复制,对基于引用类型的属性执行按引用复制。

对象的深层复制对基于值类型的属性执行按值复制,以及对层次结构深处(引用类型的)基于引用类型的属性执行按值复制


P
Pang

浅拷贝不会创建新引用,但深拷贝会创建新引用。

这是解释深拷贝和浅拷贝的程序。

public class DeepAndShollowCopy {
    int id;
    String name;
    List<String> testlist = new ArrayList<>();

    /*
    // To performing Shallow Copy 
    // Note: Here we are not creating any references. 
      public DeepAndShollowCopy(int id, String name, List<String>testlist)
       { 

       System.out.println("Shallow Copy for Object initialization");
       this.id = id; 
       this.name = name; 
       this.testlist = testlist; 

       }
    */  

    // To performing Deep Copy 
    // Note: Here we are creating one references( Al arraylist object ). 
    public DeepAndShollowCopy(int id, String name, List<String> testlist) {
        System.out.println("Deep Copy for Object initialization");
        this.id = id;
        this.name = name;
        String item;
        List<String> Al = new ArrayList<>();
        Iterator<String> itr = testlist.iterator();
        while (itr.hasNext()) {
            item = itr.next();
            Al.add(item);
        }
        this.testlist = Al;
    }


    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Java");
        list.add("Oracle");
        list.add("C++");
        DeepAndShollowCopy copy=new DeepAndShollowCopy(10,"Testing", list);
        System.out.println(copy.toString());
    }
    @Override
    public String toString() {
        return "DeepAndShollowCopy [id=" + id + ", name=" + name + ", testlist=" + testlist + "]";
    }
}

S
Santosh

摘自 [博客]:http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

深拷贝涉及使用一个对象的内容来创建同一类的另一个实例。在深拷贝中,两个对象可能包含相同的信息,但目标对象将拥有自己的缓冲区和资源。任何一个对象的破坏都不会影响剩余的对象。重载的赋值运算符将创建对象的深层副本。

浅拷贝涉及将一个对象的内容复制到同一类的另一个实例中,从而创建镜像。由于引用和指针的直接复制,两个对象将共享另一个对象的相同外部包含的内容,这是不可预测的。

解释:

使用复制构造函数,我们只需逐个复制数据值。这种复制方法称为浅拷贝。如果对象是一个简单的类,由内置类型和没有指针组成,这是可以接受的。这个函数将使用值和对象,并且它的行为不会被浅拷贝改变,只有作为成员的指针的地址被复制,而不是地址指向的值。然后对象的数据值会被函数无意中更改。当函数超出范围时,对象的副本及其所有数据都会从堆栈中弹出。

如果对象有任何指针,则需要执行深拷贝。使用对象的深层副本,为自由存储中的对象分配内存并复制指向的元素。深层副本用于从函数返回的对象。


N
Nayas Subramanian

我从以下几行中了解到。

浅拷贝将对象值类型(int、float、bool)字段复制到目标对象中,并且对象的引用类型(字符串、类等)被复制为目标对象中的引用。在这个目标中,引用类型将指向源对象的内存位置。

深拷贝将对象的值和引用类型复制到目标对象的全新副本中。这意味着值类型和引用类型都将被分配一个新的内存位置。


R
Rajaram Shelar

浅拷贝是创建一个新对象,然后将当前对象的非静态字段复制到新对象中。如果一个字段是一个值类型 --> 对该字段进行逐位复制;对于引用类型 --> 引用被复制但被引用的对象不是;因此原始对象及其克隆引用同一个对象。

深拷贝是创建一个新对象,然后将当前对象的非静态字段复制到新对象。如果字段是值类型 --> 将执行该字段的逐位复制。如果字段是引用类型--> 执行引用对象的新副本。要克隆的类必须标记为 [Serializable]。


k
komizo

复制数组:

Array 是一个类,这意味着它是引用类型,因此 array1 = array2 导致两个变量引用同一个数组。

但是看看这个例子:

  static void Main()
    {
        int[] arr1 = new int[] { 1, 2, 3, 4, 5 }; 
        int[] arr2 = new int[] { 6, 7, 8, 9, 0 };

        Console.WriteLine(arr1[2] + " " + arr2[2]);
        arr2 = arr1;
        Console.WriteLine(arr1[2] + " " + arr2[2]); 
        arr2 = (int[])arr1.Clone();
        arr1[2] = 12;
        Console.WriteLine(arr1[2] + " " + arr2[2]);
    }

浅克隆意味着只复制克隆数组所代表的内存。

如果数组包含值类型对象,则复制值;

如果数组包含引用类型,则仅复制引用 - 因此有两个数组的成员引用相同的对象。

要创建深层副本——引用类型重复,您必须遍历数组并手动克隆每个元素。


我不了解其他语言,但在 C#/VB 中,浅复制值类型数组并复制值。这两个数组引用相同的对象。将按钮添加到表单并添加此代码以查看:private void button1_Click(object sender, EventArgs e) { int[] arr1 = new int[]{1,2,3,4,5}; int[] arr2 = new int[]{6,7,8,9,0}; MessageBox.Show(arr1[2] + " " + arr2[2]); arr2 = arr1; MessageBox.Show(arr1[2] + " " + arr2[2]); arr1[2] = 12; MessageBox.Show(arr1[2] + " " + arr2[2]); }
你是对的,我更准确地纠正了我的答案,在数组上使用克隆。您绝对正确,“浅复制值类型数组不会复制值”,但是在数组上使用克隆可以。我试着解释一下,试试看。谢谢
r
royal52

复制构造函数用于用先前创建的同一类的对象来初始化新对象。默认情况下,编译器写了一个浅拷贝。当不涉及动态内存分配时,浅拷贝工作正常,因为当涉及动态内存分配时,两个对象将指向堆中的相同内存位置,因此为了解决这个问题,我们编写了深拷贝,因此两个对象都有自己的属性副本记忆中。要阅读包含完整示例和说明的详细信息,您可以查看文章 C++ constructors


L
Lance Ruo Zhang

为了在浅拷贝和简单地为列表分配一个新的变量名之间增加一点混淆。

“假设我们有:

x = [
    [1,2,3],
    [4,5,6],
    ]

此语句创建 3 个列表:2 个内部列表和一个外部列表。然后以名称 x 提供对外部列表的引用。如果我们这样做

y = x

没有数据被复制。我们在内存中的某个地方仍然有相同的 3 个列表。所有这一切都是使外部列表在名称 y 下可用,除了它以前的名称 x。如果我们这样做

y = list(x)

或者

y = x[:]

这将创建一个与 x 具有相同内容的新列表。列表 x 包含对 2 个内部列表的引用,因此新列表还将包含对相同的 2 个内部列表的引用。只复制一个列表——外部列表。现在内存中有4个列表,两个内部列表,外部列表和外部列表的副本。原始外部列表在名称 x 下可用,而新外部列表在名称 y 下可用。

内部列表没有被复制!此时您可以从 x 或 y 访问和编辑内部列表!

如果您有一个二维(或更高)列表,或任何类型的嵌套数据结构,并且您想要制作所有内容的完整副本,那么您想要使用复制模块中的 deepcopy() 函数。您的解决方案也适用于二维列表,因为迭代外部列表中的项目并制作每个项目的副本,然后为所有内部副本构建一个新的外部列表。”

来源:https://www.reddit.com/r/learnpython/comments/1afldr/why_is_copying_a_list_so_damn_difficult_in_python/