目录
数据与结构
通俗来讲数据结构即储存大量数据的容器,在python
中称之为内置数据结构(Built-in Data Structure),python
中有四种数据结构,分别是:列表、字典、元组,集合,每种数据结构都有自己的特点。
# 列表
list = [val1, val2, val3, val4]
# 字典
dict = {key1:val1, key2:val2}
# 元组
tuple = (val1, val2, val3, val4)
# 集合
set = {val1, val2, val3, val4}
从最容易识别的特征上来看,列表中的元素是用方括号括起来的,字典和集合中的元素是用花括号括起来的,元组中的元素是圆括号。
其中字典中的元素均是有关键字key
和值value
的对应关系组。
列表(list
)
列表具有的最显著的特征是:
- 列表中的每一个元素都是可变的,这意味着我们可以在列表中添加、删除和修改元素;
- 列表中的元素是有序的,也就是说每一个元素都有一个位置,说明可以进行索引,切片等操作;
- 列表可以容纳
python
中的任何对象
# 列表的索引操作
Weekday = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
print(Weekday[0])
Monday
# 列表可以装入python中的所有对象,举例说明:
all_in_list = [
1, #整数
1.0, #浮点数
'a word', #字符串
print(1), #函数
True, #布尔值
[1,2], #列表中套列表
(1,2), #元组
{'key':'value'} #字典
]
print(all_in_list)
1
[1, 1.0, 'a word', None, True, [1, 2], (1, 2), {'key': 'value'}]
列表的增改删查
- 列表的插入
# 列表的插入,insert()方法
fruit = ['pineapple', 'pear']
fruit.insert(1,'grape') # 指定位置和要插入的元素
print(fruit)
['pineapple', 'grape', 'pear']
在使用
insert()
方法时,必须指定在列表中要插入的新元素的位置,插入元素的实际位置是在指定位置元素之前的位置,
如果指定插入的位置在列表中不存在,即超出指定列表长度,那么被插入元素被放到列表的最后
使用下述这种方法也可以达到“插入”的效果
fruit[0:0] = ['Orange'] # 插入位置是第0个位置,[1:1]插入位置是第1个位置,类似于切片操作的区间
print(fruit)
['Orange', 'grape']
- 删除列表中元素使用的方法是
remove()
fruit = ['pinapple', 'pear', 'grape']
fruit.remove('grape')
print(fruit)
['pinapple', 'pear']
删除还有一种操作,是使用del
关键字进行声明
fruit = ['Grapefruit', 'pear', 'grape']
del fruit[0:2] # 删除第0个和第1个元素,因为该区间是左闭右开的
print(fruit)
['grape']
- 替换或修改列表中的元素
fruit = ['pinapple', 'pear', 'grape']
fruit[0] = 'Grapefruit' # 将列表中第0个位置的元素替换为 Grapefruit
print(fruit)
['Grapefruit', 'pear', 'grape']
列表的索引与字符串的切片特别相似,同样分正反索引两种方式,只要输入对应位置就会返回在这个位置上的值
sample= | [ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ] |
---|---|---|---|---|---|---|---|---|---|---|---|
index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ||
reverse index: | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 |
# 小例子:元素周期表
periidoic_table = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'o', 'F', 'Ne']
print(periidoic_table[0])
print(periidoic_table[-2])
print(periidoic_table[0:3])
print(periidoic_table[-10:-7])
print(periidoic_table[-10:])
print(periidoic_table[:9])
H
F
['H', 'He', 'Li']
['H', 'He', 'Li']
['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'o', 'F', 'Ne']
['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'o', 'F']
# 查看某个值在列表中的具体位置,不能直接使用下述这种操作,需使用列表中的方法 index
print(periidoic_table['H'])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_23276/3106788200.py in <module>
1 # 查看某个值在列表中的具体位置,不能直接使用下述这种操作,需使用列表中的方法 index
----> 2 print(periidoic_table['H'])
TypeError: list indices must be integers or slices, not str
# 查看指定值在列表中的位置
print(periidoic_table.index('H'))
0
字典(Dictionary
)
字典这种数据结构的特征和现实中的字典一样,内容与名称你对应。 在python
中对应为键(key
) - 值(value
),习惯上称之为键值对.
字典的特征总结如下:
- 字典中的数据必须是以键值对的形式出现的;
- 逻辑上讲,键是不能重复的,值可以重复;
- 字典中的键不可变,即无法修改;而值是可变的,即可以修改的,可以是任何对象
# 字典的书写方式
NASDAQ_code = {
'BIDU': 'Baidu',
'SINA': 'Sina',
'YOKU': 'Youku'
}
print(NASDAQ_code)
{'BIDU': 'Baidu', 'SINA': 'Sina', 'YOKU': 'Youku'}
# 字典中键和值不能脱离对方而单独存在,会报语法错误
NASDAQ_code = {'BIDU': }
File "C:\Users\R\AppData\Local\Temp/ipykernel_23276/3866311085.py", line 2
NASDAQ_code = {'BIDU': }
^
SyntaxError: invalid syntax
# 字典中的键必须是不可变的,所以下述例子会报错,因为列表是可变元素
key_test = {[]: 'a Test'}
print(key_test)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_23276/194953050.py in <module>
1 # 字典中的键必须是不可变的,所以下述例子会报错,因为列表是可变元素
----> 2 key_test = {[]: 'a Test'}
3 print(key_test)
TypeError: unhashable type: 'list'
- 再次强调: 字典中键(
key
)和值(value
)是一一对应的,键(key
)是不可变的
# 字典中的键值不会重复,相同的键值只出现一次
a = {'key': 123, 'key':123}
print(a)
{'key': 123}
# 字典中的键值不会重复,相同的键值只出现一次
a = {'key': 123456, 'key':12345}
print(a) # 且经过测试发现,出现的始终是最后一个
{'key': 12345}
字典中的增改删查
NASDAQ_code = {
'BIDU': 'Baidu',
'SINA': 'Sina'
}
# 字典中没有可以往字典里添加单一元素的“方法”,但是可以通过下述操作进行添加
NASDAQ_code['YOKU'] = 'Youku' # 会添加到最后
print(NASDAQ_code)
{'BIDU': 'Baidu', 'SINA': 'Sina', 'YOKU': 'Youku'}
列表中添加多个元素的方法为extend()
, 字典中对应添加多个元素的方法为update()
NASDAQ_code.update({'FB': 'Facebook', 'TSLA': 'Tesla'})
print(NASDAQ_code)
{'BIDU': 'Baidu', 'SINA': 'Sina', 'YOKU': 'Youku', 'FB': 'Facebook', 'TSLA': 'Tesla'}
# 字典中删除元素使用 del 方法
del NASDAQ_code['FB']
print(NASDAQ_code)
{'BIDU': 'Baidu', 'SINA': 'Sina', 'YOKU': 'Youku', 'TSLA': 'Tesla'}
注意:虽然字典中使用的是花括号,但在索引时使用的是和列表一样的方括号,在方括号中放入的一定是–字典中的键(key
)
也就是说字典需要通过键值来进行索引,这和常规字典中的用法也是一致的
print(NASDAQ_code['TSLA'])
Tesla
字典不能进行切片操作
# 字典不能进行切片操作
chart[1:4] # 这种写法在字典中是错误的
元组(Tuple
)
- 元组可以理解为一个稳固版的列表,因为元组和列表最大的区别就是元组中的元素是不可修改的,即元组是一种不可变结构
- 因此列表中的很多方法不能用在元组上,但元组是可以被查看索引的,方式和列表一样
letters = ('a', 'b', 'c', 'd', 'e', 'f', 'g')
letter = letters[0]
print(letter)
a
集合(Set
)
集合更接近数学上集合的概念,
- 每一个集合中的元素都是无序的、不重复的任意对象,
- 我们可以通过集合判断数据的从属关系,
- 有时还可以通过集合把数据结构中的重复元素减掉
集合不能被切片也不能被索引,除了做集合运算之外,集合元素可以被添加、被删除
a_set = {1, 2, 3, 4}
a_set.add(5) # 添加操作
print(a_set)
{1, 2, 3, 4, 5}
a_set.discard(5) # 删除操作
print(a_set)
{1, 2, 3, 4}
数据结构中的一些技巧
多重循环
在实际操作中往往会遇到很多问题,比如:在整理表格或文件的时候按照字母或者日期进行排序,在Python
中可以这样做:
num_list = [6, 2, 7, 1, 3, 5]
print(sorted(num_list))
[1, 2, 3, 5, 6, 7]
sorted
函数会按照长短、大小、英文字母的顺序给列表中的每个元素进行排序
sorted
函数并不会改变列表本身,可以理解为先将列表进行复制,然后再按顺序进行整理
使用默认参数 reverse
(默认为False
) 后,可以将列表按照逆序整理
# 按逆序整理
print(sorted(num_list,reverse=True))
[7, 6, 5, 3, 2, 1]
整理列表过程中如果同时需要两个列表需要处理时,可以用的zip
函数
- zip函数的原型为:
zip([iterable, …])
- 参数
iterable
为可迭代的对象,并且可以有多个参数。该函数返回一个以元组为元素的列表,其中第i
个元组包含每个参数序列的第i
个元素。 - 返回的列表长度被截断为最短的参数序列的长度;只有一个序列参数时,它返回一个1元组的列表;没有参数时,它返回一个空的列表。
# 伪代码
for a,b in zip(num,str)
# 面对多个文件时 zip 函数的使用
for a, b in zip(num, str):
print(b, 'is', a)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_23276/324545502.py in <module>
1 # 面对多个文件时 zip 函数的使用
----> 2 for a, b in zip(num, str):
3 print(b, 'is', a)
NameError: name 'num' is not defined
# zip 函数的使用
import numpy as np
a=[1,2,3,4,5]
b=(1,2,3,4,5)
c=np.arange(5)
d="zhang"
zz=zip(a,b,c,d)
print(list(zz))
[(1, 1, 0, 'z'), (2, 2, 1, 'h'), (3, 3, 2, 'a'), (4, 4, 3, 'n'), (5, 5, 4, 'g')]
列表推导式
列表推导式也叫列表解析式,现将10个元素装入列表中
# 普通写法
a = []
for i in range(1, 11):
a.append(i)
print(a)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 用列表推导式或列表解析式的方式来写
b = [i for i in range(1, 11)]
print(b)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
可以看出列表推导式不仅非常方便,而且在执行效率上要明显占优
可以通过下面这段程序对比两者在执行效率上的差异
import time
a = []
t0 = time.clock()
for i in range(1,20000):
a.append(i)
print(time.clock() - t0, "seconds process time")
t0 = time.clock()
b = [i for i in range(1,20000)]
print(time.clock() - t0, "seconds process time")
0.0024082999998427113 seconds process time
0.0006596000002900837 seconds process time
如何理解列表推导式
list = [item for item in iterable]
?答:可以将列表推导式简单的看作两部分,第一个
item
后面的部分可以看作一个标准的for
循环表达式,第一个item
可以看作是我们想要放在列表中的元素,在上面这个例子中,想要放入列表中的元素是后面循环的元素本身。
为了进一步理解列表推导式,看下面几个例子
a = [i**2 for i in range(1,10)]
c = [j+1 for j in range (1,10)]
k = [n for n in range(1,10) if n % 2 == 0]
z = [letter.lower() for letter in 'ABCDEFGHIGKLMN']
print(a)
print(c)
print(k)
print(z)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
[2, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 6, 8]
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'g', 'k', 'l', 'm', 'n']
字典推导式略有不同,主要是因为创建字典必须满足键–值的两个条件才可以
d = {i: i+1 for i in range(4)}
g = {i: j for i,j in zip(range(1,6), 'abcde')}
k = {i: j.upper() for i,j in zip(range(1,6), 'abcde')} # upper()是将所有小写字母转换为大写
print(d)
print(g)
print(k)
{0: 1, 1: 2, 2: 3, 3: 4}
{1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}
{1: 'A', 2: 'B', 3: 'C', 4: 'D', 5: 'E'}
循环列表时获取元素索引
因为列表是有序的,所以使用Python
中独有的函数enumerate()
来索引每个元素的具体位置
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
for num, letter in enumerate(letters):
print(letter, 'is', num+1)
a is 1
b is 2
c is 3
d is 4
e is 5
f is 6
g is 7
综合项目
为深入理解列表的使用方法,这里做一个瓦尔登湖词频统计的小例子(为了避免编码的问题,将文本用编译器打开再重新保存一次)
- 预备知识
# 采用 split 方法将字符串中的每个单词分开,得到独立的单词
lyric = 'The night begin to shine, the night begin to shine'
words = lyric.split() # 默认的分隔符为空格
print(words)
['The', 'night', 'begin', 'to', 'shine,', 'the', 'night', 'begin', 'to', 'shine']
下面开始做词频统计
path = './Walden.txt'
with open(path,'r',encoding= 'utf-8') as text: # 要指定解码格式,否则在读文本文件的时候会造成解码错误
words = text.read().split() # 进行分词
print(words) # 打印进行分词后的文本
for word in words:
print('{}-{} times'.format(word, words.count(word))) # 打印结果,count()函数的作用是对词频进行统计
虽然通过上面的代码,结果出来了,但总觉得有一些奇怪,仔细观察得出结论:
- 有一些带标点符号的单词被单独统计了次数,如“tried.”,“hitherto,”等;
- 有些单词被重复统计了,即不止一次被展示了统计次数;
- 由于 Python 对大小写敏感,开头大写的单词被单独统计了
所以现在根据这些点调整统计方法,对单词做一些预处理:
- 去掉首尾连在一起的字符串,并且全部转化为小写字母;
- 创建一个单词集合,集合的性质保证了不会出现重复的单词,确保不会出现重复统计单词的现象
- 创建一个字典,key 为文本里出现的单词, value 为单词在文本中出现的频率
- 在字典中,根据词频顺序打印键值对
import string # 引入模块 string,string.punctuation中包含的是所有标点符号
path = './Walden.txt' # 读取文件
with open(path,'r',encoding= 'utf-8') as text: # 需要指定解码格式
words = [raw_word.strip(string.punctuation).lower() for raw_word in text.read().split()]
# 去掉了标点符号,并把首字母大写的单词转化为小写
words_set = set(words) # 将列表用 set 函数转换为集合,自动去掉了其中所有重复的元素
counts_dict = {keyword: words.count(keyword) for keyword in words_set} # 创建一个以单词为键,出现频率为值的字典
for word in sorted(counts_dict, key=lambda x: counts_dict[x], reverse=True): # 默认对键进行排序,这里采用 lambda 表达式按照字典的值进行排序
print('{} -- {} times'.format(word, counts_dict[word])) # 展示结果
代码解释:
strip()
函数的作用是去除单词首尾的符号,其中string.punctuation
是一个字符集,包含!@ # ¥ % & * 等符号,使用strip(string.punctuation)
可以去除这些首尾符号;word.lower()
函数把所有字母全部转换为小写;sorted()
函数对字典进行排序,默认对key
进行排序,但这里对value
进行排序,所以在这里使用lambda
表达式,在key = lambda word: counts_dict[x]
中,返回值是counts_dict[x]
,也就是counts_dict
中的value