本文共 7614 字,大约阅读时间需要 25 分钟。
目录
① 能够表示多个数据的类型称为组合数据类型
② 组合数据类型能够将多个同类型或不同类型的数据组织起来,通过单一的表示使数据操作更有序、更容易
③ 根据数据之间的关系,组合数据类型可以分为3类:
a. 序列类型
序列类型是一个元素向量,元素之间存在先后关系,通过序号访问,元素之间不排他
tips:元素之间不排他是因为序列类型中可以存在值相同但位置不同的元素
b. 集合类型
集合类型是一个元素集合,元素之间无序,相同元素在集合中唯一存在
c. 映射类型
映射类型是"键-值"数据项的组合,每个元素是一个键值对
在Python中,每一类组合数据类型都对应一个或多个具体的数据类型,
① 集合类型与数学中的集合概念一致,即包括0个或多个数据项的无序组合
② 集合元素之间无序,每个元素唯一,不存在相同元素
③ 由于集合是无序组合,所以没有索引和位置的概念,不能分片(not subscriptable)
④ 集合元素不可更改,所以元素类型只能是固定数据类型,例如整数、浮点数、字符串、元组等
tips:列表、字典和集合类型本身都是可变数据类型,所以不能作为集合元素
说明1:为什么集合类型的元素必须是不可变数据类型
因为集合要求元素唯一,不存在相同元素,如果元素可变,则可能被修改为相同元素,这就与定义相违背了
不可变数据类型包括整数、浮点数、复数、字符串、元组
说明2:集合本身是可变数据类型
集合类型要求元素是不可变类型,但是集合类型本身是可变数据类型,因为可以向集合中增加 / 删除元素
说明3:如何界定可变 / 不可变数据类型
Python中界定可变 / 不可变数据类型是根据该类型是否能够进行哈希运算,能够进行哈希运算的类型都可以作为集合元素
e.g. 列表不能进行哈希运算,不是不可变数据类型,所以不能作为集合元素
实践一下,集合本身是不能进行哈希运算的,是可变数据类型
说明4:哈希运算
哈希运算可以将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。哈希值是对数据的一种有损且紧凑的表示形式,可以认为哈希值是数据在另一个数据维度的体现
Python提供内置函数hash,用于产生哈希值
说明5:理解可变 / 不可变的2个维度
① 组合数据类型本身是否是可变的
② 组合数据类型的成员是否是可变的
| 类型本身是否可变 | 类型成员是否可变 |
集合 | 可变 | 不可变 |
元组 | 不可变 | 可变(后文有详细讨论) |
列表 | 可变 | 可变 |
① 集合用大括号{}表示,元素间用逗号分隔
② 建立集合类型使用{}或set函数
③ 建立空集合类型,必须使用set函数
说明1:为什么建立空集合类型必须使用set函数
因为Python中字典类型也使用大括号表示,而{}表示空字典类型
说明2:建立集合类型时,会去除重复元素,且集合的实际存储顺序与定义顺序可以不一致
tips:集合元素的顺序就是没有顺序~
说明2:使用set函数生成集合时,参数可以是任何组合数据类型(注意必须是组合数据类型),返回结果是一个无重复且排序任意的集合
准确说是要求set函数的参数可迭代(iterable)
说明:
S - T也称为相对补集
S ^ T也成为对称差集
Python中共有6个集合操作符,
说明:并差交补还有结合赋值运算的增强操作符
说明:S.pop方法是随机返回集合S中的一个元素,而且会从集合中删除该元素,也就是更新集合S
集合类型与其他类型最大的不同在于他不包含重复元素,因此当需要对一维数据进行去重或进行数据重复处理时,一般通过集合完成
① 序列是具有先后关系的一组元素
② 序列是一维元素向量,元素类型可以不同
③ 元素由序号索引,通过下标可以访问序列的特定元素,其中又分为正向与返向两种
④ 序列是一个基类类型,在Python中派生出字符串类型、元组类型、列表类型
说明1:序列与其派生类型的关系
① 序列类型的操作在字符串类型、元组类型、列表类型中均适用,即基类操作在派生类中均适用
② 各个派生类又有各自的特性
上述2点体现了面向对象的思想
说明2:字符串是字符的有序组合,属于序列类型,由于字符串类型十分常用且单一字符只表达一个含义,因此也被看作是基本数据类型
说明:序列元素类型(索引操作类型)
元组、列表的元素类型就是该元素的类型,字符串的元素类型实际上还是字符串,也就是说在Python中没有字符类型,即使只有一个字符也是字符串类型
说明:使用min & max函数时,要求序列的元素是可比较的,所以元素类型必须相同。如果元素不可比较,该函数将报错
① 元组是序列类型的一种扩展
② 元组一旦创建就不能被修改
③ 使用小括号()或tuple函数创建,元素间用逗号分隔
④ 在不混淆语义的情况下,小括号不是必须的(个人建议能用就用上)
说明1:使用tuple函数建议元组时,传递的参数也必须是组合类型(iterable)
说明2:修改元组成员会报错
如果元组的成员类型是可变数据类型呢(e.g. 列表),这个问题其实比较复杂,当元组中只有一个列表成员时,类型就是列表而不是元组;如果还有其他成员,则仍为元组类型
但是此时已经不能进行哈希运算,也就不再是不可变类型,而是一个可变类型
① 元组继承了序列类型的全部通用操作
② 元组因为创建后不能修改,因此没有特殊操作
元组类型用于表达固定数据项、函数可变参数、函数多返回值、多变量同步赋值、循环遍历
import mathfor x, y in ((1, 0), (2, 5), (3, 6)): print(math.hypot(x, y))
① 列表是序列类型的一种扩展
② 列表创建后可以修改
tips:列表的长度和内容都是可变的,可自由对列表中的数据项进行增加、删除或替换;列表没有长度限制,元素类型可以不同,使用非常灵活
③ 使用方括号[]或list函数创建,元素间用逗号分隔
tips:使用list函数建立列表时,传递的参数也必须是组合类型(iterable)
说明1:列表必须通过显式的数据赋值才能生成,简单将一个列表赋值给另一个列表不会生成新的列表对象,也就是说只有使用方括号或者list函数才是生成列表,否则是引用
这里为什么要在修改lt列表之后再打印id值进行验证呢 ? 这是为了说明Python中写时复制机制(更正:list类型在此处不是反应了写时复制机制,而是list就是可变数据类型,后文将详细讨论)
① 字符串赋值实际上也未生成新的字符串,只是字符串元素不能修改(does not support item assignment)
② 整数类型在赋值时也未生成新的整数对象,但是如果进行修改,将会实际生成对象
更新:这点其实是因为整数类型是不可变数据类型,下文有详细讨论
说明2:列表和数组
列表和数组有2个显著不同,
① 数组需要预先分配大小,列表则不需要
② 数组要求元素类型一致,列表则不需要
说明1:正确理解切片的下标
[i:j:k]的切片下标同时满足下面2个条件,
① 有效范围为[i, j - 1]
② 被选中的下标为i + nk (n = 0, 1, 2...)
说明2:关于切片替换的说明
切片替换的操作需要分两种情况讨论,
① 切片中不含步长
此时不要求两个列表长度一样,遵循多增少减的原则处理
② 切片中包含步长
此时要求两个列表长度一样,否则报错
说明3:ls.insert(i, x)操作是将元素x插入列表下标为i的位置,列表中从i开始的元素依次后移
① 元组用于元素不改变的应用场景,更多用于固定搭配场景
② 列表更加灵活,是最常用的序列类型
③ 序列类型最主要的作用是表示一组有序数据,进而操作他们
说明:如果不希望数据被程序修改,可以将其转换为元组类型
基本统计值计算实例是求解一组不定长数据的基本统计值,比如平均值、方差、中位数等
def getNum(): nums = [] iNumStr = input("请输入数字(直接输入回车退出):") while iNumStr != "": nums.append(eval(iNumStr)) iNumStr = input("请输入数字(直接输入回车退出):") return nums
说明:getNum函数首先建立空列表numbs,然后将用户直接输入回车之前的数字均添加到列表中,然后返回该列表、
需要注意的是,getNum函数的调用者需要接收该函数的返回值,此时涉及一次列表的引用
此处说明这点,是因为在C语言中不能返回指向局部变量的指针,因为函数返回时函数栈会被撤销,局部变量将被销毁
这里返回的nums列表是一个局部变量,但是仍然可以以引用的方式返回,这是Python与C/C++的不同(C++中也不允许返回局部变量的引用)
def mean(numbers): s = 0.0 for num in numbers: s += num return s / len(numbers)
def dev(numbers, mean): sdev = 0.0 for num in numbers: sdev += (num - mean) ** 2 return sqrt(sdev / (len(numbers) - 1))
说明:调用dev函数时,传入的numbers为数据列表,mean为均值
def median(numbers): numbers = sorted(numbers) size = len(numbers) if (size % 2) == 0: med = (numbers[size // 2 -1] + numbers[size // 2]) / 2 else: med = numbers[size // 2] return med
说明:sorted函数会返回numbers列表排序后的副本,并不会改变原先列表的值
n = getNum()m = mean(n)print("平均值:{}, 方差:{:.3}, 中位数:{}".format(m, dev(n, m), median(n)))
① 在编程术语中,根据一个信息查找另一个信息的方式构成了"键值对",他表示索引用的"键"和对应的"值"构成的成对关系
② 通过任意键信息查找一组数据中值信息的过程称作映射
③ Python中通过字典实现映射,字典是包含0个或多个键值对的集合,没有长度限制,可以根据键索引值的内容
可以通过大括号{}或dict()函数创建字典
键和值通过冒号连接,不同的键值对通过逗号隔开
说明1:空字典使用{}表示,所以生成空集合时只能使用set()函数
说明2:字典是键值对的集合
从Python设计的角度,由于大括号{}可以表示集合,因此字典类型也具有和集合类似的性质,即键值对之间没有顺序且不能重复
说明3:dict()函数的使用
使用dict函数创建字典相对比较复杂,语法如下,
class dict(**kwarg) # **kwarg为关键字class dict(mapping, **kwarg) # mapping为元素容器class dict(iterable, **kwarg) # iterable为可迭代对象
在当前阶段,我们给出使用关键字和可迭代对象创建字典的示例,
在通过可迭代对象调用dict函数时,使用了一个列表,而列表的成员是元组
在字典变量中,通过键获得值
说明:使用[ ]可以索引字典,还可以向字典中增加元素
小结:在Python中,
小括号:元组
中括号:列表
大括号:集合、字典
说明1:d.keys & d.values返回的是dict_keys & dict_values类型,可以用for循环做遍历,但是不能当作列表操作,他们并不是列表类型
说明2:d.item可以返回字典的所有键值对信息,为dict_items类型,
也可以使用for循环遍历,遍历时的循环变量为元组类型,
如果直接使用变量遍历字典,取出的是什么呢 ?
由于键值对中的键相当于索引,因此for循环返回的变量是键的值
说明3:d.pop取出之后会删除键值对
① 映射无处不在,键值对无处不在
② 例如统计数据出现的次数,数据是键,次数是值
不可变数据类型:当该数据类型对应变量的值发生了改变,那么他对应的内存地址也会发生改变
可变数据类型:当该数据类型对应变量的值发生了改变,那么他对应的内存地址不发生改变
① 整型:不可变数据类型
② 字符串:不可变数据类型
③ 集合:可变数据类型
④ 元组:不可变数据类型
元组创建后是不能修改的,所以从定义上就是不可变数据类型
让人产生疑惑的就是元组并不要求其元素是不可变类型,所以当元组的成员是可变类型时,比如上文中以列表作为元组的成员,就会出现可以改变列表成员的值,但是整个元组变得不可哈希运算
⑤ 列表:可变数据类型
⑥ 字典:可变数据类型
参考资料:
① 中文文本需要通过分词获得单个词语,才能进行基于词语的处理(e.g. 词频统计)
② jieba库是优秀的中文分词第三方库,需要额外安装
③ jieba利用一个中文词库,确定汉字之间的关联概率,形成分词结果。除了分词,用户还可以添加自定义的词组
③ jieba库提供三种分词模式:精确模式、全模式、搜索引擎模式
④ 可使用pip命令安装,
pip install jieba
把文本精确地切分开,不存在冗余单词
把文本中所有可能的词语都扫描出来,有冗余
在精确模式基础上,对长词再次切分
说明:向分词词典加入新词
英文文本不需要进行特殊的分词操作,我们默认使用空格进行分词
def getText(): txt = open("hamlet.txt", "r").read() txt = txt.lower() for ch in '!"#$%&()*+,-./:;<=>?@[\\]^_‘{|}~': txt = txt.replace(ch, " ") # 将特殊字符均替换为空格 return txthamletTxt = getText()words = hamletTxt.split() # 默认以空格切分字符串并返回列表counts = {} # 空字典for word in words: counts[word] = counts.get(word, 0) + 1 # 如果键不存在则返回0items = list(counts.items()) # 此处将字典内容构成列表,是为了排序# 使用列表成员的第2个字段,也就是出现次数排序# 默认为升序,此处reverse=True改为降序items.sort(key=lambda x:x[1], reverse=True)for i in range(10): word, count = items[i] # items[i]取出的是一个元组 print("{:<10}{:>5}".format(word, count))
排序结果如下图所示,
说明1:list(counts.items())构成元组列表
如前文所述,counts.items其实也可以遍历,之所以调用list函数构成列表,是为了后面的排序操作
说明2:剔除虚词
从统计结果分析,统计出了很多虚词,可以构建排除列表加以处理
excludes = {"the", "and", "to", "the"} # 可以根据需要添加for word in excludes: del counts[word] # 以排除列表中的词为键,删除字典中的对应项
中文文本词频统计的逻辑与英文词频相同,这是需要使用jieba库对中文文本先实现分词
import jiebatxt = open("threekingdoms.txt", "r", encoding="utf-8").read()words = jieba.lcut(txt) # 进行精确模式分词counts = {}for word in words: if len(word) == 1: continue; else: counts[word] = counts.get(word, 0) + 1items = list(counts.items())items.sort(key=lambda x:x[1], reverse=True)for i in range(10): word, count = items[i] print("{:<10}{:>5}".format(word, count))
从统计结果分析,此处也统计出了许多虚词,也可以使用排除列表处理。同时有些词含义是相同的,比如曹操和丞相,此时可以对这些词进行特殊处理,示例如下,
转载地址:http://nrwx.baihongyu.com/