滚雪球学 Python 第二轮开启,进阶之路,列表与元组那些事儿

举报
梦想橡皮擦 发表于 2021/05/26 15:24:21 2021/05/26
【摘要】 橡皮擦,一个逗趣的互联网高级网虫 牛年来了,滚雪球学 Python 第二轮开启,为了方便大家学习,先罗列一下第一遍滚雪球的所有博客吧。 滚雪球学 Python 文章 1.这才是 Python 学习的正确起手姿势,滚雪球学 Python2. 无门槛学会数据类型与输入、输出函数,滚雪球学 Python3. 无转折不编程,滚雪球学 Python4. 列表一学完...

橡皮擦,一个逗趣的互联网高级网虫

牛年来了,滚雪球学 Python 第二轮开启,为了方便大家学习,先罗列一下第一遍滚雪球的所有博客吧。

滚雪球学 Python 文章

当然在学习完这个系列之后,还为大家准备了一个番外篇。滚雪球学 Python 自动化篇,有兴趣的你也可以点点关注。

接下来就进入正题吧,滚雪球学 Python 第二轮将在第一轮的基础之后,对基础知识进行扩展,争取本系列的文章能让你收获新知识。

新的系列,让我们一起 Be More Pythonic

一、列表与元组那些事儿

1. 列表和元组为何要总放在一起

列表和元组在基础篇已经好好的研究了基础用法,你应该保留一个基本印象就是列表和元组,就是一个可以放置任意数据类型的有序集合,或者当成一个容器也可以。

它们两个最直接的区别就是,列表长度大小不固定,可变,元组长度大小固定,不可变。

在很多地方,会把这种区别叫做动态静态

这里最常见的一个错误就是给元组赋值或者修改值了,错误提示如下,出现了要知道原因是啥?

TypeError: 'tuple' object does not support item assignment

  
 
  • 1

如何去给元组增加数据呢,我想你应该也比较清楚了,就是新创建一个元组,把新的数据和旧的数据一拼接,搞定。

# 梦想橡皮擦 专用的反爬虫注释
my_old_tuple = (1, 2, "a", "b")
my_new_tuple = ("c", "d")

my_tuple = my_old_tuple+my_new_tuple
print(my_tuple)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

对于基础部分,还有要注意的就是,元组如果只有一个元素,一定要这么写 (1,) ,逗号不要遗漏,遗漏了括号里面是啥数据类型,最后得到的就是那个数据类型的数据了。

1.1 列表和元组的切片

列表和元组都是有序的,有序就能切片,而切片记住是顾头不顾尾的操作,例如下述代码。

my_tuple = my_old_tuple+my_new_tuple
print(my_tuple[1:3])

  
 
  • 1
  • 2

在刚学习切片的时候,一个比较常见的错误如下,该错误产生的原因是,[] 中括号里面的 : 写成其他符号了。

TypeError: tuple indices must be integers or slices, not tuple

  
 
  • 1

1.2 负数索引与二者相互转换

列表与切片二者都支持负数索引取值,但是需要知道负数索引是从 -1 开始的,为啥?自己琢磨。

小声嘀咕:还不是因为 0 只有一个

二者也可以互相转换,转换应用的是内置的函数 listtuple,顺着函数学习下去,列表与元组都有一些可以应用的内置函数,这部分在滚雪球第一遍学习的时候,咱已经都搞定了,很简单的知识点。

1.3 列表与元组的存储方式

运行下述代码查看运行结果,列表与元组元素数目保持一致。

my_list = ["a", "b", "c"]
print(my_list.__sizeof__())

my_tuple = ("a", "b", "c")
print(my_tuple.__sizeof__())

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

输出的结果存在差异,相同元素数据的列表与元组,系统给列表分配的空间要大一些

64
48

  
 
  • 1
  • 2

第一个知识点是 __sizeof__(): 表示的打印系统分配空间的大小。

接下来我们对其进行一下基本的测试,从列表检测系统分配是如何进行空间分配的。

my_list = []
print("初始化大小",my_list.__sizeof__())

my_list.append("a")
print("追加1个元素之后的大小",my_list.__sizeof__())

my_list.append("b")
print("追加2个元素之后的大小",my_list.__sizeof__())

my_list.append("c")
print("追加3个元素之后的大小",my_list.__sizeof__())

my_list.append("d")
print("追加4个元素之后的大小",my_list.__sizeof__())

my_list.append("e")
print("追加5个元素之后的大小",my_list.__sizeof__())

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

运行结果为:

初始化大小 40
追加1个元素之后的大小 72
追加2个元素之后的大小 72
追加3个元素之后的大小 72
追加4个元素之后的大小 72
追加5个元素之后的大小 104

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

增加一个元素之后,大小变成了 72,然后连续增加 4 个元素,系统分配的大小都没有变化,地 5 个元素,又增加了 32 字节空间,这样已经可以得到结论了:
列表会一次性的增加 4 个元素的空间,当空间使用完毕之后,才会继续增加。

上述代码的原理:
列表从本质上看,是一个动态的数组,列表中并不是存储的真实数据,而是每个元素在内存中的地址(引用),因为列表存储是元素的引用这个特性,所以引用占用的内存空间是相同的,也就是 8 个字节,并且这样可以存储不同类型的数据。

在 64 位的操作系统中,地址占用 8 个字节,如果你的电脑是 32 位,那地址占用的是 4 个字节,注意下即可。

1.4 列表和元组的应用场景

简单来说,元组用在固定元素内容的数据上,列表用在可变的数据上,在希望记忆的简单一些,可以直接记成如果只需要 2、3 个元素,就使用 tuple,元素在多就使用 namedtuple,它是一个函数。

使用 namedtuple 需要先进行导入。

from collections import namedtuple
help(namedtuple)

  
 
  • 1
  • 2

函数原型如下:

namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)
# Returns a new subclass of tuple with named fields.

  
 
  • 1
  • 2

先写一段测试代码:

from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print(p.x)
print(p.y)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

前面两个参数需要简单学习一下。

  • typename:字符串类型的参数,这个参数理解起来比较绕,贴一下官方的解释,namedtuple() 会根据这个 typename, 创建一个子类类名返回出去,例如上文的测试代码中的 Point,创建好的类名称就是 Point ,第二个参数就是以后的类属性了。
  • field_names:用于为创建的元组的每个元素命名,可以传入列表或者元组,例如 ['a', 'b'](a,b),也可以传入'a b''a,b' 这种被逗号或空格分割的单字符串。

上文中如果你希望看到类被构建的过程,可以增加参数 verbose,但是这个参数在官网也有相关的说明,有的版本是不支持的,在 Python 3.7 之后就没有该属性了。

Changed in version 3.6: The verbose and rename parameters became keyword-only arguments.
Changed in version 3.6: Added the module parameter.
Changed in version 3.7: Removed the verbose parameter and the _source attribute.
Changed in version 3.7: Added the defaults parameter and the _field_defaults attribute.

  
 
  • 1
  • 2
  • 3
  • 4

初始化空列表是使用 list() 还是使用 []

该内容可以使用下述代码进行一下效率的测试。

import timeit
a = timeit.timeit('a=list()', number=10000000 )
b = timeit.timeit('a=[]', number=10000000 )
print(a)
print(b)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

运行结果:

1.6634819
0.5888171999999998

  
 
  • 1
  • 2

结论是 [] 速度更快,因为 list() 是函数调用,效率肯定要低一些。

有了上述函数,你也可以测试一下相同的元素在列表与元组初始化的时候,哪个效率更好。

import timeit
a = timeit.timeit('a=("a","b","c")', number=10000)
b = timeit.timeit('b=["a","b","c"]', number=10000)
print(a)
print(b)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

运行结果如下:

# 初始化元组
0.0005571000000000048
# 初始化列表
0.002022099999999999

  
 
  • 1
  • 2
  • 3
  • 4

1.5 这篇博客的总结

这篇博客,重点回顾了列表与元组的一些基础内容,并且探究了一下列表与元组在系统分配上的差异,额外为大家扩展了一个 namedtuple 函数,最后你可以扩展了解一下 timeit 模块。

滚学球学 Python 再次开启,已有起点的拔高,为了更高级的目标,我们来啦。

相关阅读

  1. Python 爬虫 100 例教程,超棒的爬虫教程,立即订阅吧
  2. Python 爬虫小课,精彩 9 讲

今天是持续写作的第 91 / 100 天。
如果你想跟博主建立亲密关系,可以关注同名公众号 梦想橡皮擦,近距离接触一个逗趣的互联网高级网虫。
博主 ID:梦想橡皮擦,希望大家点赞评论收藏

文章来源: dream.blog.csdn.net,作者:梦想橡皮擦,版权归原作者所有,如需转载,请联系作者。

原文链接:dream.blog.csdn.net/article/details/114013978

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。