Python序列类型

序列(Sequence)是有顺序的数据列,Python 有三种基本序列类型:list, tuple 和 range 对象,序列(Sequence)是有顺序的数据列,二进制数据(bytes) 和 文本字符串(str)也是序列类型,它们是特殊序列类型,会有一些特殊的性质和操作。

在实际的使用中,我们并不直接使用序列(Sequence)类型,而是具体使用list、tuple和range对象等等,本文主要是做一个归纳、概括性的说明

序列类型

Python 的内置序列类型有:

类型

创建方法

可变性

特别方法

列表

list()

可变

sort()

元组

tuple()

不可变

等差数列

range()

不可变

属性方法

字符串

str()

不可变

字符的方法

字节串

bytes()

不可变

字节数组

bytearray()

可变

内存视图

memoryview()

不可变

要注意的是,集合、字典不是序列类型,虽然字典在最新的 Python 版本中具备了元素顺序特性,但这不是一种「保证」。

可以通过 

isinstance(obj, collections.Sequence)

来判定对象是不是一个序列类型。

扁平序列

扁平序列有两个特点,第一,内部存储的都是值而不是引用(或者说是内存地址);第二,内部存储的都是同一种数据类型,而且只能存储数值、字节、字符这样的基础数据类型。常见的扁平序列如字符串str、字节bytes、数组array.array、字节数组bytearray和内存视图memoryview等。

我们举一个例子,例如s1 = 'abc',这是一个创建字符序列str的命令。这条命令运行时,Python解释器会先在内存中开辟一块连续的内存空间来存储a、b和c三个字符,创建好对象后会将这块内存的首地址抛给外界,由变量s1来接收,变量s1是另一块内存,这块内存中就存储了字符串序列'abc'的内存首地址。之后如果需要使用'abc'这个对象,都是通过变量s1。当解释器读到变量s1时,发现其存储的是一个内存地址,就会直接读取这个内存地址对应数据,完成对象的访问。

 

 

容器序列

与扁平序列相对应,容器序列中存储的不是值而是对象的引用,正因为如此,容器中可以容纳任何数据类型。常见的容器序列包括列表list、元祖tuple等。

如下例子:list1 = [1,'abc',[10,20,’age']],这是一个列表。

 

可以看到,对于列表本身来说,其在内存中是连续的,但是列表的内存中存储的并不是值本身,而是对象(列表中元素)的引用。至于对象(列表中的元素)本身,则存储在别的内存块中,这些内存可能是连续的,也可能是不连续的,大概率是不连续的。而且,列表中不仅仅可以存储基本数据类型intstr,还可以存储列表list

可变序列

可变序列是指可以在原内存地址上对数据进行修改的序列。这类序列包括列表list、字节数组bytearray、数组array.array,内存视图memoryview等。

列表list提供了append方法,可以在原列表内存地址上对列表进行修改:

>>> b = [1,2,3]
>>> id(b)
4352472384
>>> b.append(4)
>>> id(b)   #追加了元素,但是b的地址并没有变化
4352472384
>>> b
[1, 2, 3, 4]

这里给列表增加了一个元素,但是列表的内存地址并没有发生变化。

不可变序列

不可变序列指的是不可以在原内存地址上对序列进行修改。这类序列包括字符序列str、元祖tuple、和字节序列bytes

例如,对于字符序列str,一旦创建就无法在原内存地址上对数据进行修改,强行修改则是创建新的对象:

>>> s = 'abc'
>>> id(s)
4346120656
>>> s = '123'
>>> id(s)   #s的内容和地址都变化了
4349997680
>>> s
'123'
>>> s += '4'
>>> id(s)  #即使是追加,s的内容和地址也都变化了
4349376816
>>> s
'1234'

可能有人对元祖不可修改无法理解,对于容器序列来说,不可变指的是容器中每一个元素的引用不可变,而不是每一个元素的值不可变。如下面的例子:

>>> a = ('1','2',['3','4'])
# 记录元祖的内存地址
>>> id(a)
1665412226688
# 记录元祖中第一个元素的内存地址
>>> id(a[0])
140710344787616
# 修改元祖中的第一个元素,可以看到报错了,提示元祖对象不支持赋值。因为第一个元素是一个不可变对象,强行修改会创建新的对象,产生新的引用,而元祖不支持修改内部元素的引用,所以报错。
>>> a[0]='5'
Traceback (most recent call last):
  File "<pyshell#14>", line 1, in <module>
    a[0]='5'
TypeError: 'tuple' object does not support item assignment
# 记录元祖中第三个元素列表的内存地址
>>> id(a[2])
1665412212544
# 我们的元祖中第三个元素是一个list,这是一个可变的序列,使用append方法会在原内存地址上进行修改,这样保证元素中第三个元素的引用并不会发生变化,所以修改成功。
>>> a[2].append('9')
# 可以看到第三个元素列表修改成功
>>> a
(1, 2, [3, 4, 9])
# 但是第三个元素的内存地址并未发生变化
>>> id(a[2])
1665412212544
# 元祖第三个元素修改后,元祖本身的内存地址并未发生变化。
>>> id(a)
1665412226688

这个例子中,我们先创建了一个元祖(1,2,[3,4]),元祖中有三个元素1、2、[3,4],前两个都是不可变的字符序列str,通过前面的例子我们已经知道,如果对字符序列强行修改,不会改变原来的字符,而是创建新的对象。新的对象就意味着产生一个新的内存地址,这会导致元祖第一个元素的引用发生变化。这是元祖不能够接受的,所以出现报错。

然而,我们发现对创建的元祖的第三个元素进行修改,却修改成功了,原因是第三个元素是一个列表list,是一个可变序列,对其调用append方法是在原地址上对数据进行的修改,而并不会改变本身的内存地址,因此元祖第三个元素的引用不会发生变化,故而修改成功。

因此我们说对于容器序列,不可变意味着元素的引用不可变,相反,可变则意味着引用可以发生变化:

>>> a = [1,2,[3,4]]
>>> id(a)
2338191916032
>>> id(a[2])
2338200822784
>>> a[2]=5
>>> a
[1, 2, 5]
>>> id(a[2])
140710407702304
>>> id(a)
2338191916032

 看见列表是可以修改元素的引用的,因为它是可变类型。

序列类型的协议

以上我们对Python中的序列类型进行了分类,接下来我们学习一下序列类型的协议。通过这一部分的学习,你会对面向对象以及常见序列类型有更加深刻的认识。

Python为可变序列和不可变序列提供了两个基类Sequence和MutableSequence,这两个基类存在于内置模块collections.abc中,与其他常见的类如intlist等不同,这两个基类都是抽象基类,抽象基类确定了序列类型的协议,所有属于序列类型的都要遵循这个抽象基类的协议。

Sequence和MutableSequence两个类的继承关系如下:

 

可变序列MutableSequence类继承自不可变序列Sequence类,Sequence类又继承了两个类Reversible和Collection,Collection又继承自Container、Iterable、Sized三个抽象基类。通过这个继承图,我们至少应该能够知道,对于标准不可变序列类型Sequence,应该至少实现以下几种方法(遵循这些协议):

__contains__,__iter__,__len__,__reversed__,__getitem__,index,count

 以Python的内置类型list为例说明这几个方法:

  • 实现了__contains__方法,就意味着list可以进行成员运算,即使用innot in
  • 实现了__iter__方法,意味着list是一个可迭代对象,可以进行for循环、拆包、生成器表达式等多种运算;
  • 实现了__len__方法,意味着可以使用内置函数len()。同时,当判断一个list的布尔值时,如果list没有实现__bool__方法,也会尝试调用__len__方法;
  • 实现了__reversed__方法,意味着可以实现反转操作;
  • 实现了__getitem__方法,意味着可以进行索引和切片操作;
  • 实现了indexcount方法,则表示可以按条件取索引和统计频数。

标准的Sequence类型声明了上述方法,这意味着继承自Sequence的子类,其实例化产生的对象将是一个可迭代对象、可以使用for循环、拆包、生成器表达式、in、not in、索引、切片、翻转等等很多操作。这同时也表明,如果我们说一个对象是不可变序列时,暗示这个对象是一个可迭代对象、可以使用for循环、......。

而对于标准可变序列MutableSequence,我们发现,除了要实现不可变序列中几种方法之外,至少还需要实现如下几个方法(遵循这些协议):

__setitem__,__delitem__,insert,append,extend,pop,remove,__iadd__

以Python的内置类型list为例这几个方法:

  • 实现了__setitem__方法,就可以对列表中的元素进行修改,如代码a[0]=2就是在调用这个方法
  • 实现了__delitem__,pop,remove方法,就可以对列表中的元素进行删除,如代码del a[0]就是在调用__delitem__方法
  • 实现了insert,append,extend方法,就可以在序列中插入元素;
  • 实现了__iadd__方法,列表就可以进行增量赋值。

这就是说,对于标准可变序列类型,除了执行不可变类型的查询操作之外,其子类的实例对象都可以执行增删改的操作。

鸭子类型

抽象基类Sequence和MutableSequence声明了对于一个序列类型应该实现那些方法,很显然,如果一个类直接继承自Sequence类,内部也重载了Sequence中的七个方法,那么显然这个类一定是序列类型了,MutableSequence的子类也是一样。确实如此,但是当我们查看列表list、字符序列str、元组tuple的继承链时,发现在其mro列表(Method Resolution Order, MRO代表了类继承的顺序)中并没有Sequence和MutableSequence类,也就是说,这些内置类型并没有直接继承自这两个抽象基类。

>>> list.__mro__
(<class 'list'>, <class 'object'>)
>>> tuple.__mro__
(<class 'tuple'>, <class 'object'>)
>>> str.__mro__
(<class 'str'>, <class 'object'>)

其实,Python中有一种被称为“鸭子类型”的编程风格。在这种风格下,我们并不太关注一个对象的类型是什么,它继承自那个类型,而是关注他能实现那些功能,定义了那些方法。正所谓如果一个东西看起来像鸭子,走起来像鸭子,叫起来像鸭子,那他就是鸭子。

在这种思想之下,如果一个类并不是直接继承自Sequence,但是内部却实现了__contains__,__iter__,__len__,__reversed__,__getitem__,index,count几个方法,我们就可以称之为不可变序列。甚至都不必这么严格,可能只需要实现__len__,__getitem__两个方法就可以称作是不可变序列类型。对于可变序列也同样如此。

序列的操作

序列的特点是由若干元素组成,元素的分布有顺序,因此根据这个特点,它们支持一些共性的操作。

通用操作

以下是所有序列类型均支持的操作:

运算

结果

备注

x in s

如果 s 中的某项等于 x 则结果为 True,否则为 False

x not in s

如果 s 中的某项等于 x 则结果为 False,否则为 True

s + t

与 t 相拼接

s * n 或 n * s

相当于 s 与自身进行 n 次拼接

s[i]

的第 i 项,起始为 0

切片操作

s[i:j]

从 i 到 j 的切片

s[i:j:k]

从 i 到 j 步长为 k 的切片

len(s)

的长度

min(s)

的最小项

max(s)

的最大项

s.index(x[, i[, j]])

在 s 中首次出现项的索引号
(索引号在 i 或其后且在 j 之前)

count 方法

s.count(x)

在 s 中出现的总次数

index 方法

for i in x:pass

迭代

hash(x)

对象的哈希值

仅不可变序列

sorted(x)

排序

all(x) 或者 any(x)

全真或者有真检测

iter(x)

生成迭代器

可变序列类型

以下是仅可变序列支持的操作:

运算

结果:

s[i] = x

将 s 的第 i 项替换为 x

s[i:j] = t

将 s 从 i 到 j 的切片替换为可迭代对象 t 的内容

del s[i:j]

等同于 s[i:j] = []

s[i:j:k] = t

将 s[i:j:k] 的元素替换为 t 的元素

del s[i:j:k]

从列表中移除 s[i:j:k] 的元素

s.append(x)

将 x 添加到序列的末尾
(等同于 s[len(s):len(s)] = [x])

s.clear()

从 s 中移除所有项 (等同于 del s[:])

s.copy()

创建 s 的浅拷贝 (等同于 s[:])

s.extend(t) 或 s += t

用 t 的内容扩展 s
(基本上等同于 s[len(s):len(s)] = t)

s *= n

使用 s 的内容重复 n 次来对其进行更新

s.insert(i, x)

在由 i 给出的索引位置将 x 插入 s
(等同于 s[i:i] = [x])

s.pop() 或 s.pop(i)

提取在 i 位置上的项,并将其从 s 中移除

s.remove(x)

删除 s 中第一个 s[i] 等于 x 的项目。

s.reverse()

就地将列表中的元素逆序。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB中isequal函数转化为C语言

背景 有项目算法使用matlab中isequal函数进行运算&#xff0c;这里需要将转化为C语言&#xff0c;从而模拟算法运行&#xff0c;将算法移植到qt。 MATLAB中isequal简单介绍 语法 tf isequal(A,B) tf isequal(A1,A2,...,An) 说明 如果 A 和 B 等效&#xff0c;则 tf is…

飞书接入ChatGPT,实现智能化问答助手功能,提供高效的解答服务

文章目录 前言环境列表1.飞书设置2.克隆feishu-chatgpt项目3.配置config.yaml文件4.运行feishu-chatgpt项目5.安装cpolar内网穿透6.固定公网地址7.机器人权限配置8.创建版本9.创建测试企业10. 机器人测试 前言 在飞书中创建chatGPT机器人并且对话&#xff0c;在下面操作步骤中…

DataLoader的使用

示例代码&#xff1a; import torchvision from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter# 准备的测试数据集 test_data torchvision.datasets.CIFAR10("./dataset", trainFalse, transformtorchvision.transforms.…

postgresql-窗口函数

postgresql-窗口函数 简介窗口函数的定义分区选项&#xff08;PARTITION BY&#xff09;排序选项&#xff08;ORDER BY&#xff09;窗口选项&#xff08;frame_clause&#xff09; 聚合窗口函数排名窗口函数演示了 CUME_DIST 和 NTILE 函数 取值窗口函数 简介 常见的聚合函数&…

java八股文面试[数据库]——MySql聚簇索引和非聚簇索引区别

聚集索引和非聚集索引 聚集索引和非聚集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致。 1、聚集索引 聚集索引表记录的排列顺序和索引的排列顺序一致&#xff08;以InnoDB聚集索引的主键索引来说&#xff0c;叶子节点中存储的就是行数据&#xff0c;行数据在…

音频母带制作::AAMS V4.0 Crack

自动音频母带制作简介。 使用 AAMS V4 让您的音乐听起来很美妙&#xff01; 作为从事音乐工作的音乐家&#xff0c;您在向公众发布材料时需要尽可能最好的声音&#xff0c;而为所有音频扬声器系统提供良好的商业声音是一项困难且耗时的任务。AI掌握的力量&#xff01; 掌控您…

Web安全——信息收集上篇

Web安全 一、信息收集简介二、信息收集的分类三、常见的方法四、在线whois查询在线网站备案查询 五、查询绿盟的whois信息六、收集子域名1、子域名作用2、常用方式3、域名的类型3.1 A (Address) 记录&#xff1a;3.2 别名(CNAME)记录&#xff1a;3.3 如何检测CNAME记录&#xf…

flutter自定义按钮-文本按钮

目录 前言 需求 实现 前言 最近闲着无聊学习了flutter的一下知识&#xff0c;发现flutter和安卓之间&#xff0c;页面开发的方式还是有较大的差异的&#xff0c;众所周知&#xff0c;android的页面开发都是写在xml文件中的&#xff0c;而flutter直接写在代码里&#xff08;da…

Java 16进制字符串转换成GBK字符串

问题&#xff1a; 现在已知有一个16进制字符串 435550D3C3D3DAD4DABDBBD2D7CFECD3A6CFFBCFA2D6D0B4E6B7C5D5DBBFDBD0C5CFA2A3ACD5DBBFDBBDF0B6EE3130302E3036 而且知道这串的字符串对应的内容是&#xff1a; CUP用于在交易响应消息中存放折扣信息&#xff0c;折扣金额100.06 但…

如何为你的公司选择正确的AIGC解决方案?

如何为你的公司选择正确的AIGC解决方案&#xff1f; 摘要引言词汇解释&#xff08;详细版本&#xff09;详细介绍1. 确定需求2. 考虑技术能力3. 评估可行性4. 比较不同供应商 代码快及其注释注意事项知识总结 博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客&…

java实现多文件压缩zip

1&#xff0c;需求 需求要求实现多个文件压缩为zip文件 2&#xff0c;代码 package com.example.demo;import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import…

三极管,MOS管开关应用总结

目录 一、符号二、共同点1.三级管&#xff0c;mos管符号中都有一个PN结&#xff0c;可以根据PN结方向区分型号 二、区别1. 三级管导通条件&#xff1a;PN结正偏2. mos管导通条件&#xff1a; PN结反偏这样就不需要记哪个极的电压&#xff0c; 一、符号 1.三极管 2.mos管 MOS…

微信小程序scroll-view隐藏滚动条参数不生效问题

如题&#xff0c;先来看看问题是怎么出现的。 先看文档如何隐藏滚动条&#xff1a; 再根据文档实现wxml文件&#xff1a; <scroll-view show-scrollbar"{{false}}" enhanced><view wx:for"{{1000}}">11111</view> </scroll-view>…

(笔记一)利用open_cv在图像上进行点标记,文字注记,画圆、多边形、椭圆

&#xff08;1&#xff09;CV2中的绘图函数&#xff1a; cv2.line() 绘制线条cv2.circle() 绘制圆cv2.rectangle() 绘制矩形cv2.ellipse() 绘制椭圆cv2.putText() 添加注记 &#xff08;2&#xff09;注释 img表示需要绘制的图像color表示线条的颜色&#xff0c;采用颜色矩阵…

【跟小嘉学 Rust 编程】二十、进阶扩展

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…

静态树提升对Vue生态系统的影响和发展

文章目录 1. 了解Vue 3的静态树提升介绍Vue 3的基本概念和优势解释静态树提升的作用和目标 2. 什么是静态树&#xff1f;解释静态树的概念和特点比较静态树和动态树的区别 3. Vue 3中的静态树提升解释Vue 3中静态树提升的原理和工作方式强调静态树提升对性能的影响和优化效果 4…

Spring MVC: 请求参数的获取

Spring MVC 前言通过 RequestParam 注解获取请求参数RequestParam用法 通过 ServletAPI 获取请求参数通过实体类对象获取请求参数附 前言 在 Spring MVC 介绍中&#xff0c;谈到前端控制器 DispatcherServlet 接收客户端请求&#xff0c;依据处理器映射 HandlerMapping 配置调…

OJ练习第156题——带因子的二叉树

带因子的二叉树 力扣链接&#xff1a;823. 带因子的二叉树 题目描述 给出一个含有不重复整数元素的数组 arr &#xff0c;每个整数 arr[i] 均大于 1。 用这些整数来构建二叉树&#xff0c;每个整数可以使用任意次数。其中&#xff1a;每个非叶结点的值应等于它的两个子结点…

0202hdfs的shell操作-hadoop-大数据学习

文章目录 1 进程启停管理2 文件系统操作命令2.1 HDFS文件系统基本信息2.2 介绍2.3 创建文件夹2.4 查看指定文件夹下的内容2.5 上传文件到HDFS2.6 查看HDFS文件内容2.7 下载HDFS文件2.8 HDFS数据删除操作 3 HDFS客户端-jetbrians产品插件3.1 Big Data Tools 安装3.2 配置windows…

活用 命令行通配符

本文是对 阮一峰老师命令行通配符教程[1]的学习与记录 通配符早于正则表达式出现,可以看作是原始的正则表达式. 其功能没有正则那么强大灵活,而胜在简单和方便. - 字符 切回上一个路径/分支 如图: !! 代表上一个命令, 如图: [Linux中“!"的神奇用法](https://www.cnblogs.…