【Python学习003】高效数据结构-列表

news2025/1/17 1:03:38

【Python学习003】高效数据结构-列表

我们的公众号是【朝阳三只大明白】,满满全是干货,分享近期的学习知识以及个人总结(包括读研和IT),希望大家一起努力,一起加油!求关注!!

简介

【列表 (List) 】是 Python 中最基本的数据类型之一,列表中的每个元素均会分配一个数字,用以记录位置,我们称之为 索引 ,索引值从 0 开始,依次往后计数。

列表使用中括号【[]】包裹,元素之间使用逗号【,】 分隔,其元素可以是数字、字符串、列表等其他任何数据类型。

列表同样支持索引、更新、删除、嵌套、拼接、成员检查、截取、追加、扩展、排序等相关操作。

列表的每一个元素实质上存储的是一个指针,而不是数据本身。以下面一段做示例:

# 创建一个列表
x = [1, 2, 'a', [1, 2], 3, 4, 'b']
N = len(x)
for i in range(N):
    print(id(x[i]))
print(id(x[3][0]))
print(id(x[3][1])) 
----------------- 第一次运行 ---------------------
140706575156912
140706575156944
2389690352240
# 2389798127680
140706575156976
140706575157008
2389690295408
140706575156912
140706575156944
----------------- 第二次运行 ---------------------
140706575156912
140706575156944
2389690352240
# 2389798169600
140706575156976
140706575157008
2389690295408
140706575156912
140706575156944

由上面的示例可以看出针对数字、常见字符串,在Python启动的时候就已经分配好内存了,示例中的x,它在内存中的分布如下:

内存分布

列表常见操作

常见列表操作

添加元素

append(self, __object: _T) -> None

append方法在列表末尾添加一个元素,相当于a[len(a):] = [x],示例如下:

nums = [1, 2, 3]
nums.append(4)
nums -> [1, 2, 3, 4]

扩展列表

extend(self, __iterable: Iterable[_T]) -> None

extend方法用可迭代对象的元素扩展列表。调用该方法相当于a[len(a):] = iterable,具体示例如下:

nums = [1, 2, 3]
nums_extend = [4, 5, 6]
nums.extend(nums_extend)
nums -> [1, 2, 3, 4, 5, 6]

插入元素

insert(self, __index: SupportsIndex, __object: _T) -> None

insert方法用来在指定位置插入元素。第一个参数是插入元素的索引,因此,a.insert(0, x) 在列表开头插入元素, a.insert(len(a), x) 等同于 a.append(x) ,具体示例如下:

nums = [1, 2, 3]
nums.insert(0, 5)
nums -> [5, 1, 2, 3]

按元素值删除

remove(self, __value: _T) -> None

remove方法用来从从列表中删除第一个值为 x 的元素。未找到指定元素时,触发 ValueError 异常,示例如下:

x = [1, 2, 3, 1]
x.remove(1)
x -> [2, 3, 1]

调用该方法会从左向右寻找相等的元素,底层是调用传入对象的__eq__方法对列表中的所有对象进行比较,如果该方法返回True,则会删除该元素,示例如下:

class TestClass:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def __eq__(self, other):
        print(f"{self}调用eq方法")
        return self.name == other.name and self.age == other.age

    def __str__(self):
        return f"name:{self.name}, age:{self.age} "\
    
a1 = TestClass("a", 1)
a2 = TestClass("b", 2)
x = [a1, a2]
a3 = TestClass("b", 2)
x.remove(a3)
x
------------------------------
name:a, age:1 调用eq方法
name:b, age:2 调用eq方法
[<__main__.TestClass at 0x2dce1df8b80>]

当使用remove方法删除元素的时候需要注意当一个列表包含多个类型的元素的时候很容易出错,传入对象的__eq__方法写的不好会产生很多问题,具体示例如下:

a1 = TestClass("a", 1)
a2 = TestClass("b", 2)
x = [1, a1, a2]
a3 = TestClass("b", 2)
x.remove(a3)
x -> AttributeError: 'int' object has no attribute 'name'

按位置进行删除

pop(self, __index: SupportsIndex = ...) -> _T

调用pop方法删除列表中指定位置的元素,并返回被删除的元素。未指定位置时,a.pop() 删除并返回列表的最后一个元素,具体示例如下:

x = [1, 2, 3]
x.pop()
x -> [1, 2]
x = [1, 2, 3]
x.pop(0)
x -> [2, 3]

清除列表元素

clear(self) -> None

调用clear方法会删除列表里的所有元素,相当于 del a[:] ,具体示例如下:

x = [1, 2, 3]
x.clear()
x -> []

按索引返回元素

index(self, __value: _T, __start: SupportsIndex = ..., __stop: SupportsIndex = ...) -> int

调用index方法返回列表中第一个值为 x 的元素的零基索引。未找到指定元素时,触发 ValueError 异常。

可选参数 startend 是切片符号,用于将搜索限制为列表的特定子序列。返回的索引是相对于整个序列的开始计算的,而不是基于start 参数,具体示例如下:

x = [1, 2, 3]
x.index(2) -> 1

统计元素出现次数

count(self, __value: _T) -> int

调用count方法会返回列表中元素 x 出现的次数,具体示例如下:

x = [1, 2, 3, 1]
x.count(1) -> 2

元素原地排序

sort(self, *, key: Callable[[_T], SupportsRichComparison], reverse: bool = ...) -> None

调用sort方法就地排序列表中的元素,具体示例如下:

x = [1, 2, 3, 1]
x.sort(reverse=True)
x -> [3, 2, 1, 1]

翻转列表元素

reverse(self) -> None

调用reverse方法会翻转列表中的元素,具体示例如下:

x = [1, 2, 3]
x.reverse()
x -> [3, 2, 1]

浅拷贝

copy(self) -> list[_T]

调用copy方法返回列表的浅拷贝,相当于 a[:] ,关于浅拷贝和深拷贝,详情见【Python】python深拷贝和浅拷贝(一)。浅拷贝示例如下:

x = [1, [2, 3], 4]
y = x.copy()
y -> [1, [2, 3], 4]
y[1].append(5)
x -> [1, [2, 3, 5], 4]

符号操作

符号说明
+列表拼接
*重复元素
in / not in成员判断
[]索引取值
x[start_index: end_index]列表切片

具体示例如下:

# 列表拼接
x1 = [1, 2, 3]
x2 = [4, 5, 6]
x1 + x2 -> [1, 2, 3, 4, 5, 6]

# 重复元素,注意重复元素类似于浅拷贝之后再拼接
x = [1, 2, 3]
x * 3 -> [1, 2, 3, 1, 2, 3, 1, 2, 3]

x = [1, [2, 3], 4]
y = x * 3
y[1].append('other')
y -> [1, [2, 3, 'other'], 4, 1, [2, 3, 'other'], 4, 1, [2, 3, 'other'], 4]

# 成员判断
x = [1, 2, 3]
1 in x -> True
4 not in x -> True

# 列表切片,注意左闭右开
x = [1, 2, 3, 4, 5, 6]
x[1:4] -> [2, 3, 4]

列表的高阶实现

列表实现队列

队列的特点是先进先出,使用列表可以调用append方法添加元素,调用pop方法取出元素,示例如下:

x = []
x.append(1)
x.append(2)
x.append(3)
print(x.pop(0)) -> 1
print(x.pop(0)) -> 2

列表实现栈

栈的特点是后进先出,同样也可调用append方法和pop方法实现栈, 具体示例如下:

x = []
x.append(1)
x.append(2)
x.append(3)
print(x.pop()) -> 3
print(x.pop()) -> 2

但是列表作为队列的效率很低。因为在列表末尾添加和删除元素非常快,但在列表开头插入或移除元素却很慢(因为所有其他元素都必须移动一位)。最好使用 【collections.deque】来实现队列,deque可以快速从两端添加或删除元素。

del语句

del 语句按索引,而不是值从列表中移除元素。与返回删除值的 pop() 方法不同, del 语句也可以从列表中移除切片,或清空整个列表,具体示例如下:

x = [1, 2, 3, 4, 5, 6]
del x[0]
x -> [2, 3, 4, 5, 6]

# 删除切片的时候注意左闭右开问题
x = [1, 2, 3, 4, 5, 6]
del x[1:3]
x -> [1, 4, 5, 6]

高效列表

使用列表推导式

列表推导式是一种生成列表的方式,相比与传统的列表生成在底层进行了优化,具有了更高的性能,列表推导式的具体优势如下:

  1. 拥有更高的性能,先分配的内存再添加数据;

  2. 使用列表推导式语义上更加直观;

  3. 适用于类型转换、数据过滤;

传统方法

# 1亿数据
data = range(100000000)
%%timeit tmp = 0
some_list = list()
for i in data:
    some_list.append(i)
    
4.75 s ± 59.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

使用列表推导式

# 1亿数据
data = range(100000000)
%%timeit tmp = 0
some_list = [i for i in data] 

3.26 s ± 29.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

从上面的例子可以看出列表推导式节约了30%的运行时间,提升幅度已经很大了,随着数据量的提升,列表推导式的性能提升还会更显著。

嵌套列表推导式

x = [[i for i in range(3)] for j in range(3)]
x -> [[0, 1, 2], [0, 1, 2], [0, 1, 2]]

列表推导式除了可以更快地构建列表,还可以执行一些简单的过滤,示例如下:

# 传统方法
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
odd_numbers = filter(lambda x: x%2 != 0, nums)
list(odd_numbers) ->[1, 3, 5, 7, 9]

# Pythonic方法
odd_numbers = [i for i in nums if i % 2 != 0]
odd_numbers -> [1, 3, 5, 7, 9]

使用all或any

  1. 当对象为空列表的时候,all方法的判断结果仍是True。

  2. 当对象为空列表的时候,any方法的判断结果是False。

all方法在数据量很大的情况下有一定的性能提升,对比示例如下:

def check(l: List[int]) -> bool:
    for i in l:
        if not i:
            return False

    return True

# 准备数据
data = range(100000000)

%%timeit tmp = 0
check(data) -> 67.3 ns ± 0.628 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

%%timeit tmp = 0
all(data) -> 43 ns ± 0.998 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each) 	

总结

  1. 列表作为 Python 最基本的数据类型之一,在工作中十分常用,一般与其他数据类型搭配使用,用于构建数据结构;
  2. 定义列表可直接使用 [], 也可选择 list() 方法,如果从一个可迭代对象创建列表尽量使用列表推导式
  3. 列表可以实现队列,但是性能欠佳,最好使用【deque】来实现;
  4. 判断列表元素的时候可以考虑调用any或者all,而不是自己的实现;
  5. 文中难免会出现一些描述不当之处(尽管我已反复检查多次),欢迎在留言区指正,列表相关的知识点也可进行分享。

往期回顾

  1. 【Python学习002】函数参数
  2. 【Python学习001】内置类型
  3. 【Python】python深拷贝和浅拷贝(二)
  4. 【Python】python深拷贝和浅拷贝(一)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/178288.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浏览器使用本地硬盘上的JS文档

网页通过<script>标记可以引入在线的js文档&#xff0c;只有把网页保存在本地硬盘才能使用相对路径引入本地js文档&#xff0c;普通浏览器难以实现在线页面上引入本地js文档。怎么解决这个问题呢&#xff1f;首先准备好需要引入的JS文档&#xff0c;可以是成熟的JS库&…

【docker概念和实践 4】(3)本地镜像提交到阿里云

一、说明 本篇讲述如何将自己制作的容器转化成镜像&#xff0c;再将镜像推送到远端阿里云上。 二、制造一个本地容器 2.1 从远端仓库拉取ubuntu镜像 使用镜像拉取语句&#xff1a; docker pull ubuntu:20.04 docker run -it ubuntu&#xff1a;20.04 bash 通过以上两句&…

LeetCode[200]岛屿数量

难度&#xff1a;中等题目&#xff1a;给你一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外&am…

5-4中央处理器-控制器的功能和工作原理

文章目录一.控制器的结构和功能二.硬布线控制器1.硬布线控制单元图2.微操作命令分析3.CPU的控制方式&#xff08;1&#xff09;同步控制方式&#xff08;2&#xff09;异步控制方式&#xff08;3&#xff09;联合控制方式4.硬布线控制单元的设计步骤&#xff08;1&#xff09;分…

qsort函数用法 + 模拟实现qsort函数

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前是C语言学习者 ✈️专栏&#xff1a;【C/C】算法 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞…

论文笔记目录(ver2.0)

1 时间序列 1.1 时间序列预测 论文名称来源主要内容论文笔记&#xff1a;DCRNN &#xff08;Diffusion Convolutional Recurrent Neural Network: Data-Driven Traffic Forecasting&#xff09;_UQI-LIUWJ的博客-CSDN博客iclr 2017使用双向扩散卷积GRU&#xff0c;建模空间和…

目标跟踪心得篇五:MOT数据集标注、TrackEval的使用、DarkLabel不能自动跟踪解决方案及如何在MMTracking上输出跟踪评测

跟踪方向的标注成本非常很大的 ,那么我们如何尽可能一次性弄好呢? 所选标注工具:DarkLabel DarkLabel是一个轻量的视频标注软件,尤其做MOT任务非常友好,其标注可以通过脚本转化为标准的目标检测数据集格式、ReID数据集格式和MOT数据集格式。 使用之前: darklabel.yml:保…

传参的理解

前言 当我们调用函数的时候&#xff0c;参数是怎么传递给被调用方的&#xff0c;有想过这个问题吗&#xff1f;传递不同大小的参数对调用方式有影响吗&#xff1f;本文将带你探究这些问题&#xff0c;阅读本文需要对函数栈帧有一定的理解&#xff0c;并了解基本的汇编指令。 …

傅一平:2022年我的私人书单

2022年过去了&#xff0c;推荐我的TOP 10 书单&#xff0c;同时附上我的一句话评语和豆瓣的评分&#xff0c;这些书代表了我学习的方向&#xff0c;包括学习方法、思考方法、数据治理、数字化转型、系统架构、职场管理、个人修养、生活态度等。TOP 1 学习究竟是什么一句话评语…

【Ajax】HTTP超文本传输协议

一、HTTP协议简介什么是通信通信&#xff0c;就是信息的传递和交换。通信三要素&#xff1a;通信的主体通信的内容通信的方式1.1 现实生活中的通信案例&#xff1a;张三要把自己考清北大学的好消息写信告诉自己的好朋友李四。其中&#xff1a;通信的主体是张三和李四&#xff1…

Linux网络:应用层之HTTP协议

文章目录一、应用层1.协议2.网络版计算器二、HTTP 协议1. URL2. HTTP 协议格式3.查看 HTTP 请求4.发送 HTTP 响应5. HTTP 的方法6. HTTP 的状态码7. HTTP 的版本8. HTTP 常见 Header9. Cookie 与 session三、HTTP 与 HTTPS一、应用层 我们程序员写的一个个解决实际问题&#x…

jvm 堆 栈中存什么?

数据类型 Java虚拟机中&#xff0c;数据类型可以分为两类&#xff1a;基本类型和引用类型。基本类型的变量保存原始值&#xff0c;即&#xff1a;他代表的 值就是数值本身&#xff1b;而引用类型的变量保存引用值。“引用值”代表了某个对象的引用&#xff0c;而不是对象本身&…

矩阵理论复习(六)

Q代表有理数&#xff0c;即整数和小数部分有限的分数和小数部分无限循环的分数。无限不循环的小数就是无理数。所有无理数和有理数加起来就是实数集R。与实数对应的就是虚数。 数域的定义 线性空间的定义 线性空间的基和维数 子空间的定义 子空间的判别方法 最常见的…

【唐诗学习】二、初唐诗词领路人

二、初唐诗词领路人 唐朝之前的主流诗人都是在宫廷混口饭吃&#xff0c;他们整天围着皇帝转&#xff0c;写的大多是宫廷奢靡的生活&#xff0c;还会拍皇帝马屁。主流诗人受前朝影响很大&#xff0c;就这么发展到了初唐。照这个剧情发展下去&#xff0c;诗歌迟早要完蛋。 可有些…

狂神聊Git~

版本控制&#xff1a; 版本控制的概念: 它是一种在开发的过程中用于管理我们对文件&#xff0c;目录或工程等内容的修改历史&#xff0c;方便我们查看历史记录&#xff0c;备份以便恢复以前的版本的软件工程技术 版本控制的作用: 用于管理多人协同开发项目的技术 实现跨区…

Tomcat进程占用CPU过高怎么办?

在性能优化这个主题里&#xff0c;前面我们聊过了Tomcat的内存问题和网络相关的问题&#xff0c;接下来我们看一下CPU的问题&#xff0c;CPU资源经常会成为系统性能的一个瓶颈&#xff0c;这其中的原因是多方面的&#xff0c;可能是内存泄漏导致频繁GC&#xff0c;进而引起CPU使…

Linux命令--查看发行版本/内核版本的方法

原文网址&#xff1a;Linux命令--查看发行版本/内核版本的方法_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Linux查看发行版本和内核版本的方法。 查看发行版本 cat /etc/lsb_release 说明 这个命令适用于大部分linux发行版本&#xff08;除了redhat和centos等&#xff09; …

C 语言零基础入门教程(九)

C 函数 函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数&#xff0c;即主函数 main() &#xff0c;所有简单的程序都可以定义其他额外的函数。 您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的&#xff0c;但在逻辑上&#xff0c;…

用Zybo调试CY7C68013A核心板的Slave FIFO模式

用Zybo调试CY7C68013A核心板简介CY7C68013A核心板CY7C68013程序设计硬件连接主要代码Zybo程序设计心得简介 最近在调试CY7C68013A核心板的Slave FIFO模式时&#xff0c;因为电路板的丝印bug&#xff0c;绕了一大圈。最终不但调试成功&#xff0c;也发现了用Zybo调试其它电路板…

C语言对数组元素进行排序

在实际开发中&#xff0c;有很多场景需要我们将数组元素按照从大到小&#xff08;或者从小到大&#xff09;的顺序排列&#xff0c;这样在查阅数据时会更加直观&#xff0c;例如&#xff1a;一个保存了班级学号的数组&#xff0c;排序后更容易分区好学生和坏学生&#xff1b;一…