Python自动化之路4:列表的使用与深浅copy

2 篇文章 0 订阅
1 篇文章 0 订阅

列表介绍

Python中我们使用变量来存储数据,int,float等等数据类型,但是如果有一个需求,需要存储一个班级的学生姓名,一个一个的定义变量然后在赋值肯定是不现实的。而且在现如今大数据的时代,动辄几亿的数据量,更加不合理了。

那么这个需求怎么解决更合适呢?这个时候列表的作用就体现出来了。列表相当于C语言中的数组,但是不同于C中的数组的是,Python中的列表支持存储不同类型的变量,C中只支持存储一段相同数据类型的变量。而且列表在数组的基础上增加了很多衍生功能,例如指定下标增加一个数据或者删除数组。

接下来从列表的各功能开始介绍列表的使用

  • append() 增加一个变量到列表最后
  • insert() 指定下标插入一个变量
  • remove() 删除列表中指定的变量
  • clear() 清空列表为空
  • count() 获得列表中指定变量的个数
  • extend() 联合两个列表
  • index() 获得列表中指定下标的变量
  • pop() 出栈指定下标的变量,参数空时默认出栈栈顶变量
  • reverse() 将列表反转
  • sort() 将列表按照ASCII进行排序,默认升序
  • 切片

功能使用小栗子

以下操作基于该列表初始值

list = ["一","二","三","四","五"]
  1. append()

    源码

    list.append("六")
    print(list)
    

    输出

    ['一', '二', '三', '四', '五', '六']
    
  2. insert()

    源码

    list.insert(2,"二点二")
    print(list)
    

    输出

    ['一', '二', '二点二', '三', '四', '五']
    
  3. remove()

    源码

    list.remove("一")
    print(list)
    

    输出

    ['二', '三', '四', '五']
    

    亦或者是通过以下方法也可以删除某个变量

    del list[1]
    
  4. clear()

    源码

    list.clear()
    

    输出

    []
    
  5. count()

    源码

    list.append("五")
    print(list.count("五"))
    

    输出

    2
    
  6. extend()

    源码

    list2 = ["六","七"]
    list.extend(list2)
    print(list,",",list2)
    

    输出

    ['一', '二', '三', '四', '五', '六', '七'] , ['六', '七']
    

    被联合的列表并不受影响

  7. index()

    源码

    print(list.index("一"),",",list.index("二"))
    

    输出

    0,1
    
  8. pop()

    源码

    ```
    var = list.pop(1)
    print(var)
    print(list)
    ```
    
    输出
    
    ```
    二
    ['一', '三', '四', '五']
    ```
    

    可见,列表在内存中体现为栈存储,通过指定下标做pop()操作可以得到将某个变量出栈。

  9. reverse()

    源码

    ```
    list.reverse()
    print(list)
    ```
    

    输出

    ```
    ['五', '四', '三', '二', '一']
    ```
    
  10. sort()

    源码

    list = ["2一","b二","A三","5四","4五"]
    list.sort()
    print(list)
    

    输出

    ['2一', '4五', '5四', 'A三', 'b二']
    
  11. 切片

    何谓切片?即通过指定起始下标与终止下标得到一个从起始下标开始到终止下标前新的列表.

    源码

    newList = list[0:2]
    print(newList)
    

    输出

    ['一', '二']
    

    其实如果起始下标从0开始或者终止下标为最后一个,都可以省略该参数。

    源码

    newList = list[:2]
    print(newList)
    newList = list[2:]
    print(newList)
    newList = list[:]
    print(newList)
    

    输出

    ['一', '二']
    ['三', '四', '五']
    ['一', '二', '三', '四', '五']
    

    列表中最后一个变量的下标也可以表示为-1,以此类推,倒数第二个为-2,第三个为-3。此时如果不知道列表中变量的个数,要查询最后一个变量就方便了。

    源码

    print(list[-1],",",list[-2])
    

    输出

    五,四
    

    每隔两个变量输出列表中从第一个变量到最后一个变量前的列表。

    源码

    print(list[-5:-1:2])
    

    输出

    ['一', '三']
    

重点戏到了,敲黑板,深浅copy的区别

一般的业务场景下,经常需要拷贝一个已有的变量,在C语言中我们经常使用的是赋值操作,但是对于Python中,如何才能正确的拷贝一个变量呢?这就需要先了解一下Python的拷贝机制了。

  • 赋值操作

    万物基于C,所以Python中自然也有赋值拷贝操作,赋值的操作是生成一个新的变量指向了同一块内存,即引用。这个就不罗嗦了,翠花直接上图.

    源码

    list = ["一","二","三","四","五",["liu","qi"]]
    #赋值拷贝
    list1 = list
    def plist(list):
        print("id(list) = ",id(list))
        print("value(list) = ",list)
        print("idOfSon(list) = ",[id(son) for son in list])
    def plist1(list1):
        print("id(list1) = ",id(list1))
        print("value(list1) = ",list1)
        print("idOfSon(list1) = ",[id(son) for son in list1])
    
    plist(list)
    plist1(list1)
    
    list[0] = "1"
    list[5][0] = "六"
    print('''
    list更改后--------------------------
          ''')
    plist(list)
    plist1(list1)
    

    输出

    id(list) =  2397086202056
    value(list) =  ['一', '二', '三', '四', '五', ['liu', 'qi']]
    idOfSon(list) =  [2397085190224, 2397085341776, 2397086127968, 2397086196240, 2397086196320, 2397086199944]
    id(list1) =  2397086202056
    value(list1) =  ['一', '二', '三', '四', '五', ['liu', 'qi']]
    idOfSon(list1) =  [2397085190224, 2397085341776, 2397086127968, 2397086196240, 2397086196320, 2397086199944]
    
    list更改后--------------------------
          
    id(list) =  2397086202056
    value(list) =  ['1', '二', '三', '四', '五', ['六', 'qi']]
    idOfSon(list) =  [2397055904992, 2397085341776, 2397086127968, 2397086196240, 2397086196320, 2397086199944]
    id(list1) =  2397086202056
    value(list1) =  ['1', '二', '三', '四', '五', ['六', 'qi']]
    idOfSon(list1) =  [2397055904992, 2397085341776, 2397086127968, 2397086196240, 2397086196320, 2397086199944]
    

    在源码中,我们更改了list的0元素字符串的值以及5元素列表中的字符串的值。通过查看输出,可以发现下标0字符串值改变后地址改变了,而下标为五的列表中的值改变了,地址却没有改变,我giao,这是为什么?

    其实在Python中,str、int等数据类型被称为不可更改类型,列表、字典等称为可更改类型。不可更改类型在重新赋值后,变量名指向另外一块赋了新值的内存,而可更改类型在重新赋值后,并不会重新开辟一块内存,而是在原内存上进行更改。以上例子中,下标0的元素为str类型即不可更改类型数据,下标5的元素为列表即可更改类型数据,所以出现以上的输出。

  • 浅copy

    源码即在上述赋值拷贝的源码中将赋值操作替换为copy操作,就不再全部拷贝源码了。

    import copy
    #将赋值替换为copy,以下为伪代码
    list1 = list替换为list1 = copy.copy(list)
    

    输出

    id(list) =  1957253331144
    value(list) =  ['一', '二', '三', '四', '五', ['liu', 'qi']]
    idOfSon(list) =  [1957252319312, 1957253257056, 1957252470864, 1957253325328, 1957253325408, 1957253329032]
    id(list1) =  1957254805320
    value(list1) =  ['一', '二', '三', '四', '五', ['liu', 'qi']]
    idOfSon(list1) =  [1957252319312, 1957253257056, 1957252470864, 1957253325328, 1957253325408, 1957253329032]
    
    list更改后--------------------------
          
    id(list) =  1957253331144
    value(list) =  ['1', '二', '三', '四', '五', ['六', 'qi']]
    idOfSon(list) =  [1957222968544, 1957253257056, 1957252470864, 1957253325328, 1957253325408, 1957253329032]
    id(list1) =  1957254805320
    value(list1) =  ['一', '二', '三', '四', '五', ['六', 'qi']]
    idOfSon(list1) =  [1957252319312, 1957253257056, 1957252470864, 1957253325328, 1957253325408, 1957253329032]
    

    查看输出,发现在进行浅copy操作后,list1重新开辟了一段内存,但是list1中的元素还是采用了引用的机制。在list中的值改变后,list中的不可更改类型的str值改变并且重新开辟了一段内存,而list1并没有任何改变,可见,浅copy时,原不可更改类型的值改变,并不影响新不可更改类型的值。

    如果看到这有点晕,可能是上面的可更改类型与不可更改类型没有记下来,在回去看一下他们的定义。在来看list中可更改类型的值改变,就是下标为5的列表中的不可更改类型改变后,list1中的列表也跟随改变了,说明在浅copy时,原可更改类型的值改变,也会影响新可更改类型的值。

  • 深copy,别晕,快完了

    先来一段绕口令吧,我说“一割”,你说“我哩giaogiao”。
    我:“一割”
    你:
    我:“一割”
    你:

    ok,进入正题,老规矩,深copy的源码与浅copy唯一不同的就是将copy.copy()替换为了copy.deepcopy()

    输出

    id(list) =  2107655108872
    value(list) =  ['一', '二', '三', '四', '五', ['liu', 'qi']]
    idOfSon(list) =  [2107654096976, 2107654248528, 2107655034720, 2107655102992, 2107655103072, 2107655108808]
    id(list1) =  2107656582984
    value(list1) =  ['一', '二', '三', '四', '五', ['liu', 'qi']]
    idOfSon(list1) =  [2107654096976, 2107654248528, 2107655034720, 2107655102992, 2107655103072, 2107656583176]
    
    list更改后--------------------------
          
    id(list) =  2107655108872
    value(list) =  ['1', '二', '三', '四', '五', ['六', 'qi']]
    idOfSon(list) =  [2107653188832, 2107654248528, 2107655034720, 2107655102992, 2107655103072, 2107655108808]
    id(list1) =  2107656582984
    value(list1) =  ['一', '二', '三', '四', '五', ['liu', 'qi']]
    idOfSon(list1) =  [2107654096976, 2107654248528, 2107655034720, 2107655102992, 2107655103072, 2107656583176]
    
    

    查看输出,可以发现,无论是不可更改类型亦或者是可更改类型,在原值更改后,对新值一点威胁都没有。

    也就是说,我们现在定义两个变量,原变量 = 小弟,新变量 = 大哥。刚初始化的时候,小弟在拿着一根吸管在喝可乐的,大哥馋的不行啊,也拿了根吸管插进去嘬了起来。小弟发现可乐不好喝了,想要把瓶子里面的可乐换成雪碧,大哥哪肯同意,于是说:“我giao,你给我自己换个瓶子去喝雪碧”。没毛病啊,大哥muscle比较大,于是小弟怕挨打就乖乖的换了个瓶子躲在墙角瑟瑟发抖的嘬起了雪碧。

    这个“贴切”的案例中,深copy时,瓶子可以隐喻为内存空间,瓶子里的饮料隐喻为内存空间中存 的值,当原变量要改变时,无论改变的是可更改类型或者是不可更改类型变量,对于新变量并无影响,会重新申请一块内存给原变量存放。

  • 利用切片赋值,真的完了

    源码

    list1 = list[:]
    

    输出

    id(list) =  1564095658184
    value(list) =  ['一', '二', '三', '四', '五', ['liu', 'qi']]
    idOfSon(list) =  [1564094646352, 1564094797904, 1564095584096, 1564095652368, 1564095652448, 1564095656072]
    id(list1) =  1564097132360
    value(list1) =  ['一', '二', '三', '四', '五', ['liu', 'qi']]
    idOfSon(list1) =  [1564094646352, 1564094797904, 1564095584096, 1564095652368, 1564095652448, 1564095656072]
    
    list更改后--------------------------
          
    id(list) =  1564095658184
    value(list) =  ['1', '二', '三', '四', '五', ['六', 'qi']]
    idOfSon(list) =  [1564066999520, 1564094797904, 1564095584096, 1564095652368, 1564095652448, 1564095656072]
    id(list1) =  1564097132360
    value(list1) =  ['一', '二', '三', '四', '五', ['六', 'qi']]
    idOfSon(list1) =  [1564094646352, 1564094797904, 1564095584096, 1564095652368, 1564095652448, 1564095656072]
    

    查看输出可见,其实切片赋值也是浅copy。

  • 还可以通过工厂函数来进行浅copy,由于不常用就不补充了。

总结以下两类拷贝方法的区别:

  • 浅copy:copy后会给新变量开辟新的内存空间。对于不可更改类型数据来说,如果更改数据,会影响到新变量中对应的拷贝过来的数据(这里的影响指的是影响值与存储内存)。对于可更改类型数据,原变量的数据更改不会影响到新变量的数据。

  • 深copy:copy后也会给新变量开辟新的内存空间。无论是不可更改类型亦或是可更改类型数据,原变量的改变都不会影响新变量。

歌曲推荐:行如风,如君一骑绝尘,空谷传响,至今谁在倾听。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值