python 数据类型(容器)的比较,:有序数据类型,可变数据类型。序列数据类型,映射类型等

##题外话:先说下个人对数据结构的理解 ( 初学者观点,佬们友好指出错误,不喜勿喷)
数据结构: 是数据存储方式 + 对数据存储方式的操作规则的无穷排列组合。Python 的 4 种容器(List/Tuple/Set/Dict)是内置的数据结构实现.
数据结构 = 数据存储的底层方式(Python 的数据类型(容器)是它的具体实现)。
数据结构是“容器”:
决定了数据如何存储(如 list 连续内存,dict 哈希分散存储,列表是动态数组, 元组是静态数组)。
算法 = 操作这些数据结构的具体逻辑(如 sort()、in)。
算法是“方法”:
基于数据结构实现的具体操作(如 list.append()、dict.get())。

##题外话说完了,先看表你们要找的我要讲的都在表格里
请添加图片描述
该图重点:
有序or无序: 学完防止报错,
可变or不可变: 学完做题时防止用混方法名,比如列表的append用到其他数据类型str,报错
你们可以先去学一下每个数据类型的基础
比如下面的大佬总结的也很全(抱歉借用一下谢谢):https://www.cnblogs.com/miki-peng/p/12228399.html

  1. 有序数据类型or无序数据类型:
    请添加图片描述

有序数据类型:

# 1.有序数据类型
# 列表(List)
lst = [3, 1, 4, 1, 5]
print(lst[2])  # 输出: 4 (可以通过索引访问)
print(lst)     # 输出: [3, 1, 4, 1, 5] (保持插入顺序)

# 元组(Tuple)
tup = (3, 1, 4, 1, 5)
print(tup[2])  # 输出: 4 (可以通过索引访问)
print(tup)     # 输出: (3, 1, 4, 1, 5) (保持插入顺序)

# 字符串(String)
s = "hello"
print(s[1])    # 输出: 'e' (可以通过索引访问)
print(s)       # 输出: hello (保持字符顺序)

无序数据类型:

# 2.无序数据类型
# 集合(Set):索引输出报错
st = {3, 1, 4, 1, 5}
print(st)      # 输出可能是: {1, 3, 4, 5} (集合顺序不固定)
# print(st[0])         # 会报错,集合不支持索引

# 字典(Dict):顺序不固定
dct = {'a': 1, 'b': 2, 'c': 3}
print(dct)     # 输出可能是: {'a': 1, 'c': 3, 'b': 2}
# 虽然Python 3.7及之后版本字典保持插入顺序,但设计上仍应视为无序

1.1. 有序or无序
用处:防止用循环做题,想把无序数据类型一个个print(a[i]),然后疯狂报错
如果非要把里面的元素一个个输出:方法如下

# 1. 为什么报错?
# 无序类型(如 set)没有 [i] 的索引机制,因为它们基于哈希表存储,元素顺序不固定。

a = {1, 2, 3}
print(a[0])  # 报错:TypeError: 'set' object is not subscriptable

# 字典的键是无序的(尽管 Python 3.7+ 保留插入顺序,但设计上仍不建议依赖索引):
b = {'a': 1, 'b': 2}
print(b[0])  # 报错:KeyError: 0(字典的索引是键名,不是数字!)

# 2. 如何不循环遍历?
# 方法 1:转换为有序类型后再索引
# 将无序类型转为 list 或 tuple,然后按索引访问:

a = {1, 2, 3}
a_list = list(a)  # 转为列表(顺序随机)
print(a_list[0])  # 输出第一个元素(但顺序不确定!)

b = {'a': 1, 'b': 2}
b_keys = list(b.keys())  # 转为键列表
print(b_keys[0])         # 输出第一个键

# 方法 2:直接解包赋值
# 通过解包(Unpacking)一次性获取所有元素(需提前知道元素数量):

a = {1, 2, 3}
x, y, z = a  # 解包到变量(顺序随机)
print(x)     # 输出其中一个元素

# 方法 3:使用 next() 和 iter()
# 通过迭代器逐个获取元素(本质是隐藏循环,但代码中没有显式 for):

a = {1, 2, 3}
a_iter = iter(a)        # 创建迭代器
print(next(a_iter))     # 输出第一个元素
print(next(a_iter))     # 输出第二个元素

# 3. 特别注意
# 无序类型的顺序不可控!即使转为 list,顺序也可能每次运行不同(尤其是 set)。
# 如果必须按固定顺序访问,建议:
# 使用有序类型(如 list)存储数据。
# 对 set 或 dict 的键排序后再操作:
a = {3, 1, 2}
a_sorted = sorted(a)  # 返回排序后的列表 [1, 2, 3]
print(a_sorted[0])    # 输出最小值 1

2.可变数据类型or不可变数据类型(重点看列表元组集合字典字符串)
请添加图片描述
可变数据类型:

# 可变对象引用性质
# 引用传递:n2 = n1 并没有创建新列表,而是让 n2 和 n1 指向同一个内存对象
# 修改同步:通过任一引用修改对象内容,另一个引用访问时也会看到变化
# 内存地址相同:id(n1) == id(n2) 证明是同一个对象

# 一、可变数据类型(内容可修改:增删改查)
# 1. 列表(list)
lst = [1, 2, 3]
print('引用并修改前:',lst,id(lst))   # 输出内存地址:如140245316632832
lst1=lst            #lst1引用lst后
lst1.append(4)
lst1[0] = 99
print('引用并修改后:',lst,id(lst))  # 内存地址不变!
print(lst1,id(lst1))

# 2. 集合(set)
s = {1, 2, 3}
print('引用并修改前:',s,id(s))    # 如140245316636864
s1=s
s1.add(4)
s1.remove(1)
print('引用并修改后:',s,id(s))    # 内存地址不变
print(s1,id(s1))

# 3. 字典(dict)
d = {'a': 1, 'b': 2}
print('引用并修改前:',d,id(d))    # 如140245316643712
d1=d
d1['c'] = 3      
d1['a'] = 100
del d['b']
print('引用并修改后:',d,id(d))    # 内存地址不变
print(d1,id(d1))

不可变数据类型:

# 二、不可变数据类型(内容不可修改)
# 1. 整数(int)
x = 10
print(id(x))  # 如140245316636864
x += 1        # 实际上是创建了新对象,有等号=,创建了新变量
print(id(x))  # 内存地址改变,id(pi)!=140245316636896(例如)

# 2. 浮点数(float)
pi = 3.14
print(id(pi))  # 如140245316636896
pi += 0.1      # 创建新对象
print(id(pi))  # 内存地址改变,id(pi)!=140245316636896(例如)

# 3. 字符串(str)
s = "hello"
print(id(s))   # 如140245316636928
s += " world"  # 创建新字符串对象
print(id(s))   # 内存地址改变

# 4. 元组(tuple)
t = (1, 2, [3])
print(id(t))   # 如140245316643712

t[0] = 99       # 报错!TypeError: 'tuple' object does not support item assignment
t[2].append(4) # 元组内的可变对象仍可修改
print(t)       # 输出(1, 2, [3, 4])
print(id(t))   # 内存地址不变(但这是特殊情况)

2.1. 问题来了:如果我想在创建了一个对象引用原对象后,改变可变数据类型的地址呢(下图内容为选学,如果基础薄弱可暂时跳过)

# 必须有类似可变对象引用基础上,可以通过创建新对象(深拷贝)来实现。Python中可以使用以下几种方法创建新对象:

# 对于列表:
# 方法1:切片复制:lst1 = lst[:]
lst = [1, 2, 3]
print('修改前:', lst, id(lst))  # 原地址
lst1 = lst[:]  # 创建新对象
lst1.append(4)
lst1[0] = 99
print('修改后:', lst, id(lst))  # 原地址不变
print(lst1, id(lst1))  # 新地址

# 方法2:list()构造函数:lst1 = list(lst)
lst = [1, 2, 3]
lst1 = list(lst)
lst1.append(6)
lst1[1] = 88
print(f"list构造: 原对象 {lst}({id(lst)}) | 新对象 {lst1}({id(lst1)})")

# 方法3:copy模块(浅拷贝与深拷贝)
#       import copy
#       lst1 = copy.copy(lst)  # 浅拷贝

import copy
lst = [1, 2, [3, 4]]
lst2 = copy.copy(lst)  # 浅拷贝
lst2.append(7)
print(f"浅拷贝: 原对象 {lst}({id(lst)}) | 新对象 {lst2}({id(lst2)})")
# 与深拷贝对比:
lst_deep = copy.deepcopy(lst)#深拷贝
lst_deep[2][1] = 999
print(f"深拷贝: 原对象 {lst}({id(lst)}) | 新对象 {lst_deep}({id(lst_deep)})")


# 对于集合:
# 方法1:set()构造函数:s1 = set(s)
s0={1,2,3}
s1 = set(s0)
s1.add(4)
s1.remove(1)
print(f"set构造: 原对象 {s0}({id(s0)}) | 新对象 {s1}({id(s1)})")

# 方法2:copy()方法:s1 = s0.copy()
s2 = s0.copy()
s2.add(5)
print(f"copy方法: 原对象 {s0}({id(s0)}) | 新对象 {s2}({id(s2)})")


# 对于字典:
# 方法1:dict()构造函数:d1 = dict(d)
d={'a': 1, 'b': [2, 3]}
d1 = dict(d)
d1['c'] = 4
d1['a'] = 100
print(f"dict构造: 原对象 {d}({id(d)}) | 新对象 {d1}({id(d1)})")

# 方法2:copy()方法:d1 = d.copy()
d2 = d.copy()
d2['b'][0] = 99  # 修改嵌套列表
print(f"copy方法: 原对象 {d}({id(d)}) | 新对象 {d2}({id(d2)})")

# 方法3:解包操作(Python 3.5及之后版本有):d1 = {**d}
d3 = {**d}
d3['d'] = 'new'
print(f"解包操作: 原对象 {d}({id(d)}) | 新对象 {d3}({id(d3)})")

# 用浅拷贝与深拷贝的适用场合(选学,基础薄弱可以暂时不看)
# 要让代码在修改后输出不同的内存地址(即创建新对象),可以使用浅拷贝 或 深拷贝方法,而不是直接赋值(引用)
#       直接赋值(ls1=ls):只是引用,修改会影响原对象(内存地址不变)。
#       浅拷贝(ls1.copy()):创建新对象,但不复制嵌套结构(适用于简单数据)。
#       深拷贝(ls1.deepcopy()):完全独立的新对象,适用于嵌套数据。
# 方法 1:浅拷贝(适用于简单数据结构)
# 适用场景:如果数据结构没有嵌套(如列表里没有子列表、字典里没有嵌套字典等),可以使用浅拷贝(copy() 方法或 [:] 切片)。
# 特点:浅拷贝只复制外层对象,不会复制嵌套对象(如果有的话)。
# 修改后的代码:
# 1. 列表(list)
lst = [1, 2, 3]
print('修改前:', lst, id(lst))  # 原对象地址
lst1 = lst.copy()  # 浅拷贝(或 lst1 = lst[:])
lst1.append(4)
print('修改后:', lst1, id(lst1))  # 新对象地址

# 2. 集合(set)
s = {1, 2, 3}
print('修改前:', s, id(s))  # 原对象地址
s1 = s.copy()  # 浅拷贝
s1.add(4)
print('修改后:', s1, id(s1))  # 新对象地址

# 3. 字典(dict)
d = {'a': 1, 'b': 2}
print('修改前:', d, id(d))  # 原对象地址
d1 = d.copy()  # 浅拷贝
d1['c'] = 3
print('修改后:', d1, id(d1))  # 新对象地址

# 方法 2:深拷贝(适用于嵌套数据结构)
# 适用场景:如果数据结构有嵌套(如列表套列表、字典套字典等),必须使用 copy.deepcopy() 进行深拷贝。
# 特点:深拷贝会递归复制所有嵌套对象,确保完全独立。
# 修改后的代码
import copy

# 1. 列表(list)
lst = [1, 2, [3, 4]]
print('修改前:', lst, id(lst))  # 原对象地址
lst1 = copy.deepcopy(lst)  # 深拷贝
lst1[-1].append(5)
print('修改后:', lst1, id(lst1))  # 新对象地址

# 2. 字典(dict)
d = {'a': 1, 'b': {'x': 2}}
print('修改前:', d, id(d))  # 原对象地址
d1 = copy.deepcopy(d)  # 深拷贝
d1['b']['y'] = 3
print('修改后:', d1, id(d1))  # 新对象地址
# 深拷贝对比
d_deep = copy.deepcopy(d)
d_deep['b'][1] = 999
print(f"深拷贝: 原对象 {d}({id(d)}) | 新对象 {d_deep}({id(d_deep)})")

2.2. 可变or不可变的
用处:防止用混方法名,比如列表的append用到其他数据类型str,报错
比如元组+字符串是不可变数据类型,所以列表的修改元素的方法增删改查都不能用在元组(如下图)

#比如元组+字符串是不可变数据类型列表的修改元素的方法增删改查都不能用在元组,
#一.元组(tuple)无法使用的列表(list)方法
# 1. append() - 元组不能追加元素
# 列表可以使用append
lst = [1, 2, 3]
lst.append(4)  # 正常
print(lst)  # [1, 2, 3, 4]

# 元组不能使用append
t = (1, 2, 3)
# t.append(4)  # 报错: AttributeError: 'tuple' object has no attribute 'append'

# 2. extend() - 元组不能扩展
# 列表可以使用extend
lst = [1, 2]
lst.extend([3, 4])  # 正常
print(lst)  # [1, 2, 3, 4]

# 元组不能使用extend
t = (1, 2)
# t.extend((3, 4))  # 报错: AttributeError: 'tuple' object has no attribute 'extend'

# 3. insert() - 元组不能插入元素
# 列表可以使用insert
lst = ['a', 'b', 'd']
lst.insert(2, 'c')  # 正常
print(lst)  # ['a', 'b', 'c', 'd']

# 元组不能使用insert
t = ('a', 'b', 'd')
# t.insert(2, 'c')  # 报错: AttributeError: 'tuple' object has no attribute 'insert'

# 4. remove() - 元组不能删除元素
# 列表可以使用remove
lst = [1, 2, 3, 2]
lst.remove(2)  # 正常,删除第一个匹配项
print(lst)  # [1, 3, 2]

# 元组不能使用remove
t = (1, 2, 3, 2)
# t.remove(2)  # 报错: AttributeError: 'tuple' object has no attribute 'remove'


# 二.字符串(str)无法使用的列表(list)方法
# 1. append() - 字符串不能追加
# 列表可以使用append
lst = ['h', 'e', 'l']
lst.append('l')  # 正常
print(lst)  # ['h', 'e', 'l', 'l']

# 字符串不能使用append
s = "hel"
# s.append("l")  # 报错: AttributeError: 'str' object has no attribute 'append'

# 2. extend() - 字符串不能扩展
# 列表可以使用extend
lst = ['h', 'e']
lst.extend(['l', 'l'])  # 正常
print(lst)  # ['h', 'e', 'l', 'l']

# 字符串不能使用extend
s = "he"
# s.extend("ll")  # 报错: AttributeError: 'str' object has no attribute 'extend'

# 3. insert() - 字符串不能插入
# 列表可以使用insert
lst = ['h', 'e', 'l', 'o']
lst.insert(2, 'l')  # 正常
print(lst)  # ['h', 'e', 'l', 'l', 'o']

# 字符串不能使用insert
s = "heo"
# s.insert(2, "l")  # 报错: AttributeError: 'str' object has no attribute 'insert'

# 4. remove() - 字符串不能删除字符
# 列表可以使用remove
lst = ['h', 'e', 'l', 'l', 'o']
lst.remove('l')  # 正常,删除第一个'l'
print(lst)  # ['h', 'e', 'l', 'o']

# 字符串不能使用remove
s = "hello"
# s.remove('l')  # 报错: AttributeError: 'str' object has no attribute 'remove'

# 5. pop() - 字符串不能弹出字符
# 列表可以使用pop
lst = ['h', 'e', 'l', 'l', 'o']
char = lst.pop(2)  # 正常,弹出索引2的字符
print(char, lst)  # 'l' ['h', 'e', 'l', 'o']

# 字符串不能使用pop
s = "hello"
# char = s.pop(2)  # 报错: AttributeError: 'str' object has no attribute 'pop'

# 6. clear() - 字符串不能清空
# 列表可以使用clear
lst = ['h', 'e', 'l', 'l', 'o']
lst.clear()  # 正常
print(lst)  # []

# 字符串不能使用clear
s = "hello"
# s.clear()  # 报错: AttributeError: 'str' object has no attribute 'clear'

但是反过来:元组+字符串作为不可变数据类型,元组+字符串的方法能用在可变数据类型(列表集合字典)中吗?你们自己动手尝试敲代码试试呗


好的,今天的内容先到这里。大家可以指出哪里代码不对,或者排版不好,或者用词不够严谨,哪里还可以补充其他知识点。在此基础上如果我能做好,还可以进一步说一下大家经常不会的有哪些,我后面如果时间够会考虑给大家讲,下一期看情况可能会讲python函数中的LEGB原则
辛苦大家,由衷感谢各位大佬的阅读

最后版权所有, 本文为博主原创文章,未经博主允许不得转载,转载请标注出处,感谢。违者根据《中华人民共和国著作权法》第10条和第53条,根据《信息网络传播权保护条例》第2条,根据《计算机软件保护条例》第二十五条,以及视您的情节严重程度可能触犯《刑法》第217条侵犯著作权罪等相关法律及条例,博主将通过相关维权渠道争取合法权益,包括但不限于对案情相关被告责任人发起官司及民事诉讼,违者可能将受到民事责任与行政处罚

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值