编程小白的第一本Python入门书学习笔记 Charper6: 数据结构

数据与结构

通俗来讲数据结构即储存大量数据的容器,在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)

列表具有的最显著的特征是:

  1. 列表中的每一个元素都是可变的,这意味着我们可以在列表中添加、删除和修改元素;
  2. 列表中的元素是有序的,也就是说每一个元素都有一个位置,说明可以进行索引,切片等操作;
  3. 列表可以容纳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'}]
列表的增改删查
  1. 列表的插入
# 列表的插入,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']
  1. 删除列表中元素使用的方法是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']
  1. 替换或修改列表中的元素
fruit = ['pinapple', 'pear', 'grape']
fruit[0] = 'Grapefruit' # 将列表中第0个位置的元素替换为 Grapefruit

print(fruit)
['Grapefruit', 'pear', 'grape']

列表的索引与字符串的切片特别相似,同样分正反索引两种方式,只要输入对应位置就会返回在这个位置上的值

sample=[123456789]
index:012345678
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),习惯上称之为键值对.
字典的特征总结如下:

  1. 字典中的数据必须是以键值对的形式出现的;
  2. 逻辑上讲,键是不能重复的,值可以重复;
  3. 字典中的键不可变,即无法修改;而值是可变的,即可以修改的,可以是任何对象
# 字典的书写方式  
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)

  1. 元组可以理解为一个稳固版的列表,因为元组和列表最大的区别就是元组中的元素是不可修改的,即元组是一种不可变结构
  2. 因此列表中的很多方法不能用在元组上,但元组是可以被查看索引的,方式和列表一样
letters = ('a', 'b', 'c', 'd', 'e', 'f', 'g')
letter = letters[0]

print(letter) 
a

集合(Set)

集合更接近数学上集合的概念,

  1. 每一个集合中的元素都是无序的、不重复的任意对象
  2. 我们可以通过集合判断数据的从属关系
  3. 有时还可以通过集合把数据结构中的重复元素减掉

集合不能被切片也不能被索引,除了做集合运算之外,集合元素可以被添加、被删除

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()函数的作用是对词频进行统计

虽然通过上面的代码,结果出来了,但总觉得有一些奇怪,仔细观察得出结论:

  1. 有一些带标点符号的单词被单独统计了次数,如“tried.”,“hitherto,”等;
  2. 有些单词被重复统计了,即不止一次被展示了统计次数;
  3. 由于 Python 对大小写敏感,开头大写的单词被单独统计了

所以现在根据这些点调整统计方法,对单词做一些预处理:

  1. 去掉首尾连在一起的字符串,并且全部转化为小写字母;
  2. 创建一个单词集合,集合的性质保证了不会出现重复的单词,确保不会出现重复统计单词的现象
  3. 创建一个字典,key 为文本里出现的单词, value 为单词在文本中出现的频率
  4. 在字典中,根据词频顺序打印键值对
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])) # 展示结果

代码解释:

  1. strip()函数的作用是去除单词首尾的符号,其中string.punctuation是一个字符集,包含!@ # ¥ % & * 等符号,使用strip(string.punctuation)可以去除这些首尾符号;
  2. word.lower()函数把所有字母全部转换为小写;
  3. sorted()函数对字典进行排序,默认对key进行排序,但这里对value进行排序,所以在这里使用lambda表达式,在key = lambda word: counts_dict[x]中,返回值是counts_dict[x],也就是counts_dict中的value
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值