Python之切片、深拷贝与浅拷贝


这里是一段防爬虫文本,请读者忽略。
本文原创首发于CSDN,作者IDYS
博客首页:https://blog.csdn.net/weixin_41633902/
本文链接:https://blog.csdn.net/weixin_41633902/article/details/107436335
未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!


写在开头的话

  • 请记住:实践是掌握知识的最快方法
  • 如果你只是怀着看看的态度去快速浏览文章,而不去认认真真的把文章里面讲的任何一个知识点去实践一遍,那么你永远也掌握不了它
  • 生命不息,折腾不止!

切片

00. 线性结构

  • 线性结构
    • 可迭代 for ... in
    • len()可以获取长度
    • 通过下标可以访问
    • 可以切片
  • 学过的线性结构
    • 列表、元组、字符串、bytesbytearray

01. 深拷贝与浅拷贝

1.1 可变元素与不可变元素

  • 不可变对象,该对象所指向的内存中的值不能被改变。 当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。
  • 可变对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的出地址,通俗点说就是原地改变
  • 可变元素
    • 列表list、字典dictbytearray、集合set
  • 不可变元素
    • int,float,complex,long,str,unicode,tuple

1.2 不可变类型演示

  • 源码
a = 1
b = 1
c = 0 + a + 0
print(id(a), id(b), id(c))   #地址相同

tuple1 = (1, 2, 3)
tuple2 = (1, 2, 3)
print(id(tuple1), id(tuple2), id((1, 2, 3)))  #地址相同
tuple1 += ()
print(id(tuple1))   #地址没有改变,任然相同

a_str = "hello"
b_str = "hello"
c_str = a_str + ""
print(id(a_str), id(b_str), id(c_str))  #地址相同
c_str += "1"
print(a_str, b_str, c_str)
print(id(a_str), id(b_str), id(c_str))  # c_str 地址改变
  • 运行结果
140736508053152 140736508053152 140736508053152
1548828643264 1548828643264 1548828643264
1548828643264
1548799505968 1548799505968 1548799505968
hello hello hello1
1548799505968 1548799505968 1548829329328

不可变对象,只要内容是相同的,那么它的地址值便一直没有改变


1.3 可变类型演示

  1. 看一个列表的例子,创建两个具有相同元素的列表。虽然它们的值相同但是它们的地址值不同,指向不同的空间
list1 = [1, 3, 4, 5]
list2 = [1, 3, 4, 5]
print(list1 is list2)
print(id([1, 3, 4, 5]), id(list1), id(list2))
#  虽然内容相同, 但是他们的地址值不同
  • 运行结果
False
1725627172992 1725597412544 1725597433792
  1. 对列表进行赋值操作,那么两个列表都指向内存中的相同空间,因为列表是可以改变的,那么只要改变其中一个列表的内容,那么另外一个列表的内容也会跟着改变
list1 = [1, 3, 4, 5]
list3 = list1
print("list3=", list3, "list1=", list1)
print("id(list3)=", id(list3), "id(list1)=", id(list1))
list1.append(6)  #对列表进行追加操作,但是值一直没有改变
print("list3=", list3, "list1=", list1)
print("id(list3)=", id(list3), "id(list1)=", id(list1))
list1[0] = 10   #对列表的元素进行修改,但是其地址值仍然没有改变
print("list3=", list3, "list1=", list1)  #两个列表中的内容都改变了
print("id(list3)=", id(list3), "id(list1)=", id(list1))
  • 运行结果
list3= [1, 3, 4, 5] list1= [1, 3, 4, 5]
id(list3)= 1844923198592 id(list1)= 1844923198592
list3= [1, 3, 4, 5, 6] list1= [1, 3, 4, 5, 6]
id(list3)= 1844923198592 id(list1)= 1844923198592
list3= [10, 3, 4, 5, 6] list1= [10, 3, 4, 5, 6]
id(list3)= 1844923198592 id(list1)= 1844923198592
  1. 如果是拷贝的话,那么便会将内容拷贝过去,传递的并不是引用
list1 = [1, 2, 3, 4, 5]
list2 = list1[:]
print(list2 is list1)
print(id(list1), id(list2))


  • 运行结果
False
1811785116864 1811785138112
  1. 可变类型的+==
list1 = [1, 2, 3, 4]
print(id(list1))
list1 += [5]
print(id(list1))
list1 = list1 + [6]
print(id(list1))
  • 运行结果
2828082474176
2828082474176
2828082476736

可以看到,当时有+=,列表相当于.extendlist1 = list1 + [6]相当拼接后赋值,其id发生改变


1.4 深拷贝与浅拷贝的不同之处

  • 在浅拷贝时,拷贝出来的新对象的地址和原对象是不一样的,但是新对象里面的可变元素(如列表)的地址和原对象里的可变元素的地址是相同的,也就是说浅拷贝它拷贝的是浅层次的数据结构(不可变元素),对象里的可变元素作为深层次的数据结构并没有被拷贝到新地址里面去,而是和原对象里的可变元素指向同一个地址,所以在新对象或原对象里对这个可变元素做修改时,两个对象是同时改变的,但是深拷贝不会这样,这个是浅拷贝相对于深拷贝最根本的区别。

  • 演示

import copy
list1 = [1, 2, 3, 4, [5, 6, 7]]
list2 = list1
list3 = list1.copy()
list4 = copy.deepcopy(list1)

print("list1", list1, "list2", list2, "\n", "list3", list3, "list4", list4)

list1.append(10)
print("")
print("list1", list1, "list2", list2, "\n", "list3", list3, "list4", list4)

print("")
list1[4].append(8)
print("list1", list1, "list2", list2, "\n", "list3", list3, "list4", list4)

print()
  • 运行结果
list1 [1, 2, 3, 4, [5, 6, 7]] list2 [1, 2, 3, 4, [5, 6, 7]] 
 list3 [1, 2, 3, 4, [5, 6, 7]] list4 [1, 2, 3, 4, [5, 6, 7]]

list1 [1, 2, 3, 4, [5, 6, 7], 10] list2 [1, 2, 3, 4, [5, 6, 7], 10] 
 list3 [1, 2, 3, 4, [5, 6, 7]] list4 [1, 2, 3, 4, [5, 6, 7]]

list1 [1, 2, 3, 4, [5, 6, 7, 8], 10] list2 [1, 2, 3, 4, [5, 6, 7, 8], 10] 
 list3 [1, 2, 3, 4, [5, 6, 7, 8]] list4 [1, 2, 3, 4, [5, 6, 7]]
  1. list1list2采用的是赋值操作,两个对象完全指向同一地址,list1list2就是同一地址的两块引用,所以一个对象在修改浅层元素(不可变)或深层元素(可变)时,另一个对象也同时在变。
  2. list3list1采用的是浅拷贝操作,它们的地址值不同,但是它们可变对象的地址空间相同,所以对一个对象的可变元素操作,那么浅拷贝的另外一个可变对象的元素也跟着变化
  3. list4list1采用的是深拷贝操作,他们的地址值不同,同时在拷贝可变元素时会开闭一块新的空间,进行拷贝。所以他们互不相关

  1. 定义两个具有相同元素的列表时,他么的地址值不同,但是相同元素里面的地址值相同
list1 = [1, 2, 3, 4]
list2 = [1, 2, 3, 4]
print(id(list), id(list2))
print(id(list1[0]), id(list2[0]))
  • 运行结果
140736507820912 2929445322432
140736508053152 140736508053152

  • 属于浅拷贝的操作有
  1. 完全切片操作[:]
  2. list()dict()
  3. copy模块的copy()方法
  • 属于深拷贝的操作有
  1. copy模块的deepcopy()

02. 切片

  • 通过索引区间访问线性结构的一段数据

  • sequence[start:stop] 表示返回[start, stop)区间的子序列

  • 支持负索引

  • start0,可以省略

  • stop为末尾,可以省略

  • 超过上界(右边界),就取到末尾

  • 超过下界(左边界),取到开头

  • start一定要在stop的左边

  • [:] 表示从头至尾,全部元素被取出,等效于copy()方法

  • 步长切片

    • [start:stop:step]
    • step为步长,可以正、负整数,默认是1
    • step要和start:stop同向,否则返回空序列

  • 举例
print("www.idayuanshuai.com"[1:10])
print("www.idayuanshuai.com"[1:-1])
print("www.idayuanshuai.com"[-10:-1])
print("www.idayuanshuai.com"[-15:15])
print("www.idayuanshuai.com"[-1:-15:-1])
  • 运行结果
ww.idayua
ww.idayuanshuai.co
nshuai.co
dayuanshua
moc.iauhsnauya

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值