49 list_排序算法和深浅copy

能征服人心的,永远不是聪明和强势,——而是善良;
能感动人心的,永远不是语言和忽悠,——而是真诚;


0 函数帮助文档查看: lst.sort? lst.sort??

1.列表排序

排序算法也有内外之分:

内部排序指的是在内存中进行排序;
外部排序指的是由于数据量较大,无法读入内存而需要在排序过程中访问外部存储的情况;

经典的排序算法如下

Python 列表有内置就地排序的方法 list.sort(),返回None此外还有一个内置的 sorted() 函数将一个可迭代对象(iterable)排序为一个新的有序列表;

1.1 排序基础——list.sorted与sorted差别

1.sorted(iterable, /, *, key=None, reverse=False),得到一个有序的新列表;

>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]

2.list.sort() 方法,此方法为就地排序(并且返回 None 来避免混淆)。通常来说这不如 sorted() 方便——但是当你不需要保留原始列表的时候,这种方式略高效一些。

>>> a = [5, 2, 3, 1, 4]
>>> a.sort()
>>> a[1, 2, 3, 4, 5]

3.另外一个区别是 list.sort() 方法只可以供列表使用,而 sorted() 函数可以接受任意可迭代对象(iterable)。

>>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]
1.2 Key 参数

list.sort() 和 sorted() 都有一个 key 参数,用于指定在作比较之前,调用何种函数(str / int)对列表元素进行处理。

>>> sorted("This is a test string from Andrew".split(), key=str.lower)
['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']

key 参数的值应该是一个函数,该函数接收一个参数,并且返回一个 key 为排序时所用。这种方法(高阶函数)速度很快,因为每个输入项仅调用一次 key 函数;

>>> student_tuples = [
    ('john', 'A', 15),
    ('jane', 'B', 12),
    ('dave', 'B', 10),]
>>> sorted(student_tuples, key=lambda student: student[2])   # sort by age
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

2.浅Copy与深Copy

引用、浅拷贝和深拷贝的区别

lst=[1,2,3,4]
lst1=lst # 同一个对象,指向同一个东西
lst2=lst.copy() # 内容相同,不同的对象,遇到引用类型不会复制对象,只会复制地址
lst3=copy.deepcopy(lst) # 内容相同,但不同的对象,如果有引用类型也会复制出不同的对象

浅拷贝:为了解决函数传递后被修改的问题,就需要拷贝一份副本,将副本传递给函数使用,就算是副本被修改,也不会影响原始数据 。

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

数据拷贝会涉及到Python中对象、可变类型、引用这3个概念,先来看看这几个概念,只有明白了他们才能更好的理解深拷贝与浅拷贝到底是怎么一回事。

2.1** Python对象**都拥有三个属性:身份、类型、值。
In [2]: id(name)  # id:身份的唯一标识
Out[2]: 1698668550104
In [3]: type(name) # type:对象的类型,决定了该对象可以保存什么类型的值
Out[3]: str
In [4]: name  # 对象的值,表示的数据
Out[4]: 'laowang'
可变与不可变对象
可变对象: 列表、字典、集合

所谓可变是指可变对象的值可变,身份是不变的。

不可变对象:数字、字符串、元组

不可变对象就是对象的身份和值都不可变。新创建的对象被关联到原来的变量名,旧对象被丢弃,垃圾回收器会在适当的时机回收这些对象。

引用=id()

引用实际就是内存中的一个数字地址编号,在使用对象时,只要知道这个对象的地址,就可以操作这个对象,但是因为这个数字地址不方便在开发时使用和记忆,所以使用变量名的形式来代替对象的数字地址。 在 Python 中,变量就是地址的一种表示形式,并不开辟开辟存储空间

不可变对象的拷贝

不可变对象只在修改的时候才会在内存中开辟新的空间, 而拷贝实际上是让多个对象同时指向一个引用,和对象的赋值没区别。

In [11]: import copy
In [12]: a = 10
In [13]: b = copy.copy(a)
In [14]: id(a)
Out[14]: 1730306496
In [15]: id(b)
Out[15]: 1730306496
可变对象的拷贝

可变对象的拷贝,会在内存中开辟一个新的空间来保存拷贝的数据。当再改变之前的对象时,对拷贝之后的对象没有任何影响。

In [25]: l1 = [1, 2, 3]
In [26]: l2 = copy.copy(l1)
In [27]: id(l1)
Out[27]: 1916631742088
In [28]: id(l2)
Out[28]: 1916636282952
In [29]: l1[0] = 11
In [30]: id(l1)
Out[30]: 1916631742088
In [31]: id(l2)
Out[31]: 1916636282952

同样的,通过一个实例来感受一下:不难看出,a与b指向相同的引用,不可变对象的拷贝就是对象赋值。

复杂对象在拷贝时,并没有解决数据在传递之后,数据改变的问题。 出现这种原因,是copy() 函数在拷贝对象时,只是将指定对象中的所有引用拷贝了一份,如果这些引用当中包含了一个可变对象的话,那么数据还是会被改变。 这种拷贝方式,称为浅拷贝。

深拷贝——解决原始数据改变的问题

区别于浅拷贝只拷贝顶层引用,深拷贝会逐层进行拷贝,直到拷贝的所有引用都是不可变引用为止。

l1 = [3, 4, a]
In [47]: l2 = copy.deepcopy(li)
In [48]: id(l1)
Out[48]: 1916632194312
In [49]: id(l2)
Out[49]: 1916634281416
In [50]: a[0] = 11
In [51]: id(l1)
Out[51]: 1916632194312
In [52]: id(l2)
Out[52]: 1916634281416
In [54]: l1
Out[54]: [3, 4, [11, 2]]
In [55]: l2
Out[55]: [1, 2, 3]
查漏补缺

1.为什么Python默认的拷贝方式是浅拷贝?
时间角度:浅拷贝花费时间更少
空间角度:浅拷贝花费内存更少
效率角度:浅拷贝只拷贝顶层数据,一般情况下比深拷贝效率高。

本文知识点总结:

1.不可变对象在赋值时会开辟新空间
2.可变对象在赋值时,修改一个的值,另一个也会发生改变
3.深、浅拷贝对不可变对象拷贝时,不开辟新空间,相当于赋值操作
4.浅拷贝在拷贝时,只拷贝第一层中的引用,如果元素是可变对象,并且被修改,那么拷贝的对象也会发生变化
5.深拷贝在拷贝时,会逐层进行拷贝,直到所有的引用都是不可变对象为止。
6.Python 中有多种方式实现浅拷贝,copy模块的copy 函数 ,对象的 copy 函数 ,工厂方法,切片等。
7.大多数情况下,编写程序时,都是使用浅拷贝,除非有特定的需求
8.浅拷贝的优点:拷贝速度快,占用空间少,拷贝效率高

深度解读Python深拷贝与浅拷贝问题

你可能感兴趣的:(49 list_排序算法和深浅copy)