简单的来说,数据结构(data structure)是计算机中存储、组织数据的方式。比如我们之前使用过的列表就是一种数据结构,在这里我们还会深入学习它。之前也有简单的介绍

列表

  1. >>> a = [23, 45, 1, -3434, 43624356, 234]
  2. >>> a.append(45)
  3. >>> a
  4. [23, 45, 1, -3434, 43624356, 234, 45]

首先我们建立了一个列表 a。然后调用列表的方法 a.append(45) 添加元素 45 到列表末尾。你可以看到元素 45 已经添加到列表的末端了。有些时候我们需要将数据插入到列表的任何位置,这时我们可以使用列表的 insert() 方法。

  1. >>> a.insert(0, 1) # 在列表索引 0 位置添加元素 1
  2. >>> a
  3. [1, 23, 45, 1, -3434, 43624356, 234, 45]
  4. >>> a.insert(0, 111) # 在列表索引 0 位置添加元素 111
  5. >>> a
  6. [111, 1, 23, 45, 1, -3434, 43624356, 234, 45]

具体来说是,插入位置后元素后移,这样保证了插入后新添加元素就是索引的位置。

列表方法 count(s) 会返回列表元素中 s 的数量。我们来检查一下 45 这个元素在列表中出现了多少次。

  1. >>> a.count(45)
  2. 2

如果你想要在列表中移除任意指定值,你需要使用 remove() 方法。

  1. >>> a.remove(234)
  2. >>> a
  3. [111, 1, 23, 45, 1, -3434, 43624356, 45]

现在我们反转整个列表。

  1. >>> a.reverse()
  2. >>> a
  3. [45, 43624356, -3434, 1, 45, 23, 1, 111]

怎样将一个列表的所有元素添加到另一个列表的末尾呢,可以使用列表的 extend() 方法。(这还有一种简单的方法,直接用“+”连接)

  1. >>> b = [45, 56, 90]
  2. >>> a.extend(b) # 添加 b 的元素而不是 b 本身
  3. >>> a
  4. [45, 43624356, -3434, 1, 45, 23, 1, 111, 45, 56, 90]

给列表排序,我们使用列表的 sort() 方法,排序的前提是列表的元素是可比较的。

  1. >>> a.sort()
  2. >>> a
  3. [-3434, 1, 1, 23, 45, 45, 45, 56, 90, 111, 43624356]

你也能使用 del 关键字删除指定位置的列表元素。

  1. >>> del a[-1]
  2. >>> a
  3. [-3434, 1, 1, 23, 45, 45, 45, 56, 90, 111]

将列表用作栈和队列

是我们通常所说的一种 LIFO (Last In First Out 后进先出)数据结构。它的意思是最后进入的数据第一个出来。一个最简单的例子往一端封闭的管道放入一些弹珠然后取出来,如果你想把弹珠取出来,你必须从你最后放入弹珠的位置挨个拿出来。用代码实现此原理:

  1. >>> a = [1, 2, 3, 4, 5, 6]
  2. >>> a
  3. [1, 2, 3, 4, 5, 6]
  4. >>> a.pop()
  5. 6
  6. >>> a.pop()
  7. 5
  8. >>> a.pop()
  9. 4
  10. >>> a.pop()
  11. 3
  12. >>> a
  13. [1, 2]
  14. >>> a.append(34)
  15. >>> a

上面的代码中我们使用了一个新方法 pop()。传入一个参数 i 即 pop(i) 会将第 i 个元素弹出。请注意,没有push()函数,因为append()函数就能实现了。

在我们的日常生活中会经常遇到队列,比如售票窗口、图书馆、超市的结账出口。队列是一种在末尾追加数据以及在开始弹出数据的数据结构。与栈不同,它是 FIFO (First In First Out 先进先出)的数据结构。

  1. >>> a = [1, 2, 3, 4, 5]
  2. >>> a.append(1)
  3. >>> a
  4. [1, 2, 3, 4, 5, 1]
  5. >>> a.pop(0)
  6. 1
  7. >>> a.pop(0)
  8. 2
  9. >>> a
  10. [3, 4, 5, 1]

我们使用 a.pop(0) 弹出列表中第一个元素。可见栈和队列在代码实现上没有多大区别,只是出队一个从队首,一个从队尾。

列表推导式

列表推导式为从序列中创建列表提供了一个简单的方法。

如果没有列表推导式,一般都是这样创建列表的:通过将一些操作应用于序列的每个成员并通过返回的元素创建列表,或者通过满足特定条件的元素创建子序列。

  1. >>> squares = []
  2. >>> for x in range(10):
  3. ... squares.append(x**2)
  4. ...
  5. >>> squares
  6. [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

注意这个 for 循环中的被创建(或被重写)的名为 x 的变量在循环完毕后依然存在。

使用如下方法,我们可以计算 squares 的值而不会产生任何的副作用:

  1. squares = list(map(lambda x: x**2, range(10)))

等价于下面的列表推导式。

  1. squares = [x**2 for x in range(10)]

上面这个方法更加简明且易读。

列表推导式由包含一个表达式的中括号组成,表达式后面跟随一个 for 子句,之后可以有零或多个 for 或 if 子句。结果是一个列表,由表达式依据其后面的 for 和 if 子句上下文计算而来的结果构成。

例如,如下的列表推导式结合两个列表的元素,且元素之间不相等:

  1. >>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
  2. [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

等同于:

  1. >>> combs = []
  2. >>> for x in [1,2,3]:
  3. ... for y in [3,1,4]:
  4. ... if x != y:
  5. ... combs.append((x, y))
  6. ...
  7. >>> combs
  8. [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

值得注意的是在上面两个方法中的 for 和 if 语句的顺序。

列表推导式也可以嵌套。

  1. >>> a=[1,2,3]
  2. >>> z = [x + 1 for x in [x ** 2 for x in a]]
  3. >>> z
  4. [2, 5, 10]

元组

元组是由数个逗号分割的值组成,一般用小括号括起来,也可以省略。

  1. >>> a = 'Fedora', 'ShiYanLou', 'Kubuntu', 'Pardus'
  2. >>> a
  3. ('Fedora', 'ShiYanLou', 'Kubuntu', 'Pardus')
  4. >>> a[1]
  5. 'ShiYanLou'
  6. >>> for x in a:
  7. ... print(x, end=' ')
  8. ...
  9. Fedora ShiYanLou Kubuntu Pardus

你可以对任何一个元组执行拆封操作并赋值给多个变量,就像下面这样:

  1. >>> divmod(15,2)
  2. (7, 1)
  3. >>> x, y = divmod(15,2)
  4. >>> x
  5. 7
  6. >>> y
  7. 1

元组是不可变类型,这意味着你不能在元组内删除或添加或编辑任何值。如果你尝试这些操作,将会出错:

  1. >>> a = (1, 2, 3, 4)
  2. >>> del a[0]
  3. Traceback (most recent call last):
  4. File "<stdin>", line 1, in <module>
  5. TypeError: 'tuple' object doesn't support item deletion
  6. >>> a.append(2)
  7. Traceback (most recent call last):
  8. File "<stdin>", line 1, in <module>
  9. AttributeError: 'tuple' object has no attribute 'append'

要创建只含有一个元素的元组,在值后面跟一个逗号。

  1. >>> a = (123)
  2. >>> a
  3. 123
  4. >>> type(a)
  5. <class 'int'>
  6. >>> a = (123, )
  7. >>> b = 321,
  8. >>> a
  9. (123,)
  10. >>> b
  11. (321,)
  12. >>> type(a)
  13. <class 'tuple'>
  14. >>> type(b)
  15. <class 'tuple'>

通过内建函数 type() 你可以知道任意变量的数据类型。还记得我们使用 len() 函数来查询任意序列类型数据的长度吗?

  1. >>> type(len)
  2. <class 'builtin_function_or_method'>
  3. >>> type(print)
  4. <class 'builtin_function_or_method'>

集合

集合是一个无序不重复元素的集。基本功能包括关系测试和消除重复元素。集合对象还支持 union(联合),intersection(交),difference(差)和 symmetric difference(对称差集)等数学运算。

大括号或 set() 函数可以用来创建集合。注意:想要创建空集合,你必须使用 set() 而不是 {}。后者用于创建空字典,我们在下一节中介绍的一种数据结构。

下面是集合的常见操作:

  1. >>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
  2. >>> print(basket) # 你可以看到重复的元素被去除
  3. {'orange', 'banana', 'pear', 'apple'}
  4. >>> 'orange' in basket
  5. True
  6. >>> 'crabgrass' in basket
  7. False
  8.  
  9. >>> # 演示对两个单词中的字母进行集合操作
  10. ...
  11. >>> a = set('abracadabra')
  12. >>> b = set('alacazam')
  13. >>> a # a 去重后的字母
  14. {'a', 'r', 'b', 'c', 'd'}
  15. >>> a - b # a 有而 b 没有的字母
  16. {'r', 'd', 'b'}
  17. >>> a | b # 存在于 a 或 b 的字母
  18. {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
  19. >>> a & b # a 和 b 都有的字母
  20. {'a', 'c'}
  21. >>> a ^ b # 存在于 a 或 b 但不同时存在的字母
  22. {'r', 'd', 'b', 'm', 'z', 'l'}

从集合中添加或弹出元素:

  1. >>> a = {'a','e','h','g'}
  2. >>> a.pop()
  3. 'h'
  4. >>> a.add('c')
  5. >>> a
  6. {'c', 'e', 'g', 'a'}

字典

字典是是无序的键值对(key:value)集合,同一个字典内的键必须是互不相同的。一对大括号 {} 创建一个空字典。初始化字典时,在大括号内放置一组逗号分隔的键:值对,这也是字典输出的方式。我们使用键来检索存储在字典中的数据。

  1. >>> data = {'kushal':'Fedora', 'kart_':'Debian', 'Jace':'Mac'}
  2. >>> data
  3. {'kushal': 'Fedora', 'Jace': 'Mac', 'kart_': 'Debian'}
  4. >>> data['kart_']
  5. 'Debian'

创建新的键值对很简单:

  1. >>> data['parthan'] = 'Ubuntu'
  2. >>> data
  3. {'kushal': 'Fedora', 'Jace': 'Mac', 'kart_': 'Debian', 'parthan': 'Ubuntu'}

使用 del 关键字删除任意指定的键值对:

  1. >>> del data['kushal']
  2. >>> data
  3. {'Jace': 'Mac', 'kart_': 'Debian', 'parthan': 'Ubuntu'

使用 in 关键字查询指定的键是否存在于字典中:

  1. >>> 'ShiYanLou' in data
  2. False

dict() 可以从包含键值对的元组中创建字典:

  1. >>> dict((('Indian','Delhi'),('Bangladesh','Dhaka')))
  2. {'Indian': 'Delhi', 'Bangladesh': 'Dhaka'}

如果你想要遍历一个字典,使用字典的 items() 方法:

  1. >>> data
  2. {'Kushal': 'Fedora', 'Jace': 'Mac', 'kart_': 'Debian', 'parthan': 'Ubuntu'}
  3. >>> for x, y in data.items():
  4. ... print("{} uses {}".format(x, y))
  5. ...
  6. Kushal uses Fedora
  7. Jace uses Mac
  8. kart_ uses Debian
  9. parthan uses Ubuntu

许多时候我们需要往字典中的元素添加数据,我们首先要判断这个元素是否存在,不存在则创建一个默认值。如果在循环里执行这个操作,每次迭代都需要判断一次,降低程序性能。

我们可以使用 dict.setdefault(key, default) 更有效率的完成这个事情:

  1. >>> data = {}
  2. >>> data.setdefault('names', []).append('Ruby')
  3. >>> data
  4. {'names': ['Ruby']}
  5. >>> data.setdefault('names', []).append('Python')
  6. >>> data
  7. {'names': ['Ruby', 'Python']}
  8. >>> data.setdefault('names', []).append('C')
  9. >>> data
  10. {'names': ['Ruby', 'Python', 'C']}

试图索引一个不存在的键将会抛出一个 keyError 错误。我们可以使用 dict.get(key, default) 来索引键,如果键不存在,那么返回指定的 default 值:

  1. >>> data['foo']
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. KeyError: 'foo'
  5. >>> data.get('foo', 0)
  6. 0

如果你想要在遍历列表(或任何序列类型)的同时获得元素索引值,你可以使用 enumerate()

  1. >>> for i, j in enumerate(['a', 'b', 'c']):
  2. ... print(i, j)
  3. ...
  4. 0 a
  5. 1 b
  6. 2 c

你也许需要同时遍历两个序列类型,你可以使用 zip() 函数。请注意,与写成二重循环是不同的。

  1. >>> a = ['Pradeepto', 'Kushal']
  2. >>> b = ['OpenSUSE', 'Fedora']
  3. >>> for x, y in zip(a, b):
  4. ... print("{} uses {}".format(x, y))
  5. ...
  6. Pradeepto uses OpenSUSE
  7. Kushal uses Fedora

综合示例

这个例子里我们计算两个矩阵的 Hadamard 乘积。要求输入矩阵的行/列数(在这里假设我们使用的是 n × n 的矩阵)。

  1. #!/usr/bin/env python3
  2. n = int(input("Enter the value of n: "))
  3. print("Enter values for the Matrix A")
  4. a = []
  5. for i in range(n):
  6. a.append([int(x) for x in input().split()])
  7. print("Enter values for the Matrix B")
  8. b = []
  9. for i in range(n):
  10. b.append([int(x) for x in input().split()])
  11. c = []
  12. for i in range(n):
  13. c.append([a[i][j] * b[i][j] for j in range(n)])
  14. print("After matrix multiplication")
  15. print("-" * 7 * n)
  16. for x in c:
  17. for y in x:
  18. print(str(y).rjust(5), end=' ')
  19. print()
  20. print("-" * 7 * n)

这里我们使用了几次列表推导式。[int(x) for x in input().split()] 首先通过 input() 获得用户输入的字符串,再使用 split()分割字符串得到一系列的数字字符串,然后用 int() 从每个数字字符串创建对应的整数值。我们也使用了 [a[i][j] * b[i][j] for j in range(n)] 来得到矩阵乘积的每一行数据。

一般来说,目前我们见到的数据结构已经够用了,不过 Python 中还有一些其它有用的数据结构,可以在这里了解:https://docs.python.org/3/library/datatypes.html

参考链接:https://www.shiyanlou.com/courses/596

Python3简明教程(六)—— 数据结构的更多相关文章

  1. Python3 简明教程学习(上)

    一.开始 Python 之旅交互模式 1.Ctrl + D 输入一个 EOF 字符来退出解释器,也可以键入 exit() 来退出 2.#!/usr/bin/env python3 中#!称为 Sheb ...

  2. Python3 简明教程

    Python是由吉多·范罗苏姆(Guido Van Rossum)在90年代早期设计.它是如今最常用的编程 语言之一.它的语法简洁且优美,几乎就是可执行的伪代码. 注意:这篇教程是特别为Python3 ...

  3. Python3简明教程(十四)—— Collections模块

    collections 是 Python 内建的一个集合模块,提供了许多有用的集合类. 在这个实验我们会学习 Collections 模块.这个模块实现了一些很好的数据结构,它们能帮助你解决各种实际问 ...

  4. Python3简明教程(九)—— 文件处理

    文件是保存在计算机存储设备上的一些信息或数据.你已经知道了一些不同的文件类型,比如你的音乐文件,视频文件,文本文件.Linux 有一个思想是“一切皆文件”,这在实验最后的 lscpu 的实现中得到了体 ...

  5. Python3简明教程(五)—— 流程控制之循环

    有些时候我们需要多次执行相同的任务,我们使用一个计数器来检查代码需要执行的次数.这个技术被称为循环. while循环 while语句的语法如下: while condition: statement1 ...

  6. Python3简明教程(十二)—— 模块

    在这节我们将要学习 Python 模块相关知识.包括模块的概念和导入方法,包的概念和使用,第三方模块的介绍,命令行参数的使用等. 模块 到目前为止,我们在 Python 解释器中写的所有代码都在我们退 ...

  7. Python3简明教程(十一)—— 类

    本节中将通过定义一些简单的 Python 类,来学习 Python 面向对象编程的基本概念. 定义类 在写你的第一个类之前,你应该知道它的语法.我们以下面这种方式定义类: class nameofth ...

  8. Python3简明教程(十)—— 异常

    在本节我们学习 Python 的异常以及如何在你的代码中处理它们. 异常 在程序执行过程中发生的任何错误都是异常.每个异常显示一些相关的错误信息,比如你在 Python3 中使用 Python2 独有 ...

  9. Python3简明教程(七)—— 字符串

    字符串是 Python 中最常用的数据类型.本节实验将会学习如何对 Python3 的字符串进行处理操作. 字符串的三种表示 可以通过几种不同的方式表示字符串.如单引号('...')或双引号(&quo ...

随机推荐

  1. nodejs supvisor模块

    在测试nodejs程序的时候,每次都需要在控制台编译,非常的麻烦.supervisor是一款无需重复手动编译,自动后台监听文件变化来自动编译,并且不需要在项目内require,使用非常的方便. 使用方 ...

  2. DebugView 使用

    最近遇到带加密狗的工程项目,无法使用控制台调试,尝试使用DebugView进行辅助调试. DebugView是一个系统调试信息输出的捕获工具,可以捕获程序中由TRACE(debug版本)和Output ...

  3. ubuntu设置里面怎么少了好多设置了比如桌面背景

    哈哈  我也是醉了  这个虚拟机真的不好对付 解决办法:sudo apt-get install unity-control-center          ok!

  4. Android 应用程序窗体显示状态操作(requestWindowFeature()的应用)(转载)

    转自:http://www.cnblogs.com/salam/archive/2010/11/30/1892143.html 我们在开发程序是经常会需要软件全屏显示.自定义标题(使用按钮等控件)和其 ...

  5. Codeforces 711B 【模拟】

    比赛的时候绝壁打麻烦了... 考虑的好麻烦...wa7...还要判断出来的是不是positive的... 好吧..认了.. #include<cstdio> #include <ma ...

  6. poj1511【最短路spfa】

    题意: 计算从源点1到各点的最短路之和+各点到源点1的最短路之和: 思路: 源点这个好做啊,可是各点到源点,转个弯就是反向建边然后求一下源点到各点的最短路,就是各点到源点的最短路,在两幅图里搞: 贴一 ...

  7. MySQL的分支

    1.MariaDB MariaDB数据库管理系统是 MySQL 的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MyS ...

  8. P2579 [ZJOI2005]沼泽鳄鱼

    传送门 话说邻接矩阵居然还能快速幂的么-- 把原图的邻接矩阵\(G\)打出来,那么\(G[u][v]\)表示一秒后\(u\)到\(v\)的方案数,\(G^2[u][v]\)表示\(2\)秒后的方案数- ...

  9. IT兄弟连 JavaWeb教程 Servlet中定义的变量的作用域类型

    在Java语言中,局部变量和实力变量有着不同的作用于,它们的区别如下: 局部变量在一个方法中定义,每当一个线程执行局部变量所在的方法时,在线程的堆栈中就会创建这个局部变量,当线程执行完该方法,局部变量 ...

  10. 9-26模拟赛 By cellur925

    1.计数 (count.cpp/c/pas)时间限制:1s内存限制:256MB[问题描述]给出 m 个数 a[1],a[2],…,a[m]求 1~n 中有多少数不是 a[1],a[2],…,a[m]的 ...