Python2— 标准数据类型、索引与切片
好的今天我们来学习python的标准数据类型、索引与切片;Python 之所以好用,很大程度上归功于它内置了丰富且强大的数据结构。这就像做菜——锅碗瓢盆(语法)就那些,但食材(数据类型)的多样性和新鲜程度,直接决定了你能做出什么级别的菜。
一、数据类型总览:
Python 3 有六个标准数据类型:
- 数字(Number) —— 不可变,非序列
- 字符串(String) —— 不可变,序列
- 列表(List) —— 可变,序列
- 元组(Tuple) —— 不可变,序列
- 字典(Dictionary) —— 可变,非序列(映射)
- 集合(Set) —— 可变(或不可变的 frozenset),非序列
这里面有两条主线:可变性 和 序列性。
- 可变(Mutable)意味着你可以原地增删改,而不改变内存地址;不可变(Immutable)则每次“修改”其实是新建对象。
- 序列(Sequence)意味着元素有顺序,可以通过整数下标(索引)访问;非序列则靠键(字典)或哈希(集合)来定位。
理解了这两条线,后面的方法记忆就会轻松很多。
二、数字(Number)
数字是一切计算的起点,它不可变,也不是序列。Python 里的数字分为四类:
1. 整数(int)
- 理论上可以无限大(只受内存限制),所以你可以放心地算天文数字。
- 例子:999999...,-9999,0,1234。
2. 浮点数(float)
- 带小数点,也可以用科学计数法 e 或 E。
- 例:1.23,1.,3.14e-10,4E210(表示 )。
提醒:浮点数在计算机里是二进制近似存储,所以 0.1 + 0.2 并不精确等于 0.3。如果做金融计算,记得用 decimal.Decimal。
3. 布尔型(bool)
- Python 2 里没有专门的布尔型,用 0 和 1 代替;Python 3 把 True 和 False 变成了关键字,但它们本质上还是 1 和 0,所以 True + 3 的结果是 4,这个特性有时能派上用场(比如 sum([True, False, True]) 统计真值的个数)。
4. 复数(complex)
- 形如 a + bj,虚部用 j 或 J,系数不能省略(比如 1j 是合法的,但 j 单独写会报错)。
- 例:2+1j,1j。
类型转换
Python 不会偷偷帮你转类型,需要你主动调用工厂函数:
|
函数 |
说明 |
|
type(obj) |
查看对象类型 |
|
int([x], base=10) |
转整数,可指定进制(x 需为字符串) |
|
float([x]) |
转浮点数,字符串两头的空格不影响 |
|
bool([x]) |
转布尔,空、None、0、空容器等都是 False |
|
complex([real, imag]) |
转复数 |
大家可以试一试随手敲敲这些转换:
# int() 各种姿势
print(int()) # 0(不传参)
print(int("12")) # 12
# print(int("12.1")) # 报错!浮点型字符串不行
print(int("101010", base=2)) # 42(二进制转十进制)
print(int("0b101010", base=2)) # 42(带前缀也行)
print(int("11", base=8)) # 9(八进制)
print(int("0o11", base=8)) # 9
print(int("17", base=16)) # 23(十六进制)
print(int("0x17", base=16)) # 23
# float()
print(float()) # 0.0
print(float(123)) # 123.0
print(float(" 1.23 ")) # 1.23(忽略空格)
# bool()
print(bool()) # False
print(bool(0)) # False
print(bool(None)) # False
print(bool([])) # False
print(bool(1)) # True
print(bool("0")) # True(非空字符串!)
# complex()
print(complex()) # 0j
print(complex(3.2, 1)) # (3.2+1j)
print(complex("3.2+1j")) # (3.2+1j) 注意加号两边不能有空格
p.s. :bool 是 int 的子类,所以 isinstance(True, int) 返回 True,这个细节在类型检查时可能让你意外,记住就好。
三、字符串(String)
字符串是 不可变的序列,意味着你无法修改某个位置的字符,只能通过生成新字符串来“变相修改”。它用单引号、双引号或三引号定义。
1. 引号嵌套与转义
当你想在字符串里输出引号时,有三种常用方式:
# 方式一:转义字符
print("\"") # 输出一个双引号 "
# 方式二:外层单引号,内层双引号
print('我说:"你好"') # 我说:"你好"
# 方式三:三引号(多行字符串天然支持内部引号)
print("""这里可以随便写" ' 引号""")
2. 转义字符与原始字符串(Raw String)
反斜杠 \ 会引发转义,比如 \n 换行,\t 制表符。但有时候我们不希望它转义(比如 Windows 路径或正则表达式),这时可以在字符串前面加 r 或 R,让它变成 原始字符串:
print("123\n456") # 换行输出
print(r"123\n456") # 原样输出 123\n456
print(R"www.baidu.com\n") # 也是原样
3. 字符串格式化
① % 格式化(传统,但偶尔会碰到)
常用符号:%s(字符串,调用 str())、%r(字符串,调用 repr(),会带引号)、%d(整数)、%f(浮点数)等。还可以加辅助指令,比如 - 左对齐,+ 显示正号,# 显示进制前缀,m.n 控制宽度和小数位。
PI = 3.141592653
print("pi1 = %10.3f" % PI) # 总宽10,保留3位小数
print("pi2 = %.5f" % PI) # 保留5位
print("pi3 = %012.3f" % PI) # 用0填充空位
print("pi4 = %-10.3f" % PI) # 左对齐
print("pi5 = %+f" % PI) # 显示正号
print("%o" % 12) # 14(八进制)
print("%#o" % 12) # 0o14(显示前缀)
print("%x" % 24) # 18(十六进制)
print("%#x" % 24) # 0x18
② format() 方法(Python 2.6+,功能强大)
使用 {} 占位符,可以按顺序、按索引、按关键字传值,还支持对齐、填充、进制、千分位等。
# 顺序
a = "它说它叫{},今年{}岁。".format("旺财", 2)
# 索引
b = "它说它叫{1},今年{0}岁。".format(2, "旺财")
# 关键字
c = "它说它叫{name},今年{age}岁。".format(name="旺财", age=2)
# 复用
d = "它说它叫{name},今年{age}岁,宝宝也是{age}个月。".format(name="旺财", age=2)
# 对齐与填充
print('{:>8}'.format('1')) # 右对齐,总宽8
print('{:0>8}'.format('1')) # 右对齐,0填充
print('{:a<8}'.format('1')) # 左对齐,a填充
print('{:.2f}'.format(3.14159)) # 保留两位小数
print('{:8.2f}'.format(3.14159)) # 总宽8,保留两位
print('{:,}'.format(1234567890)) # 千分位分隔符
print('{:b}'.format(100)) # 二进制 1100100
print('{:o}'.format(100)) # 八进制 144
print('{:x}'.format(100)) # 十六进制 64
③ f-string(Python 3.6+,墙裂推荐)
在字符串前加 f,大括号里可以直接写变量或表达式,甚至调用函数。它是目前最直观、性能最好的方式。
name = "wangCai"
age = 2
print(f"它说它叫{name},它{age}岁,宝宝{age}月了!")
print(f"它说它叫{name.upper()},它{4//2}岁,宝宝{age}月了!") # 表达式和函数
# 同样支持格式化
num = 1
PI = 3.141592653
print(f'{num:>8}') # 右对齐
print(f'{num:0>8}') # 0填充
print(f'{PI:.2f}') # 保留两位小数
print(f'{PI:8.2f}') # 总宽8
print(f'{1234567890:,}') # 千分位
个人建议:新代码统一用 f-string,老项目用 % 或 format 也不奇怪,至少你得能看懂。
4. 字符串的常用方法(别死记,多用就熟了)
我把最常用的整理成表格,再附上几个容易混淆的示例:
|
方法 |
作用 |
|
replace(old, new[, count]) |
替换子串,返回新字符串 |
|
strip([chars]) / lstrip / rstrip |
移除两端(左/右)指定字符,默认空白 |
|
center(width[, fill]) / ljust / rjust |
居中/左/右对齐,填充字符 |
|
partition(sep) / rpartition |
按分隔符拆分,返回三元组(包含分隔符本身) |
|
startswith(prefix[, start[, end]]) / endswith |
检查前缀/后缀 |
|
isalnum() / isalpha() / isdigit() / isspace() |
判断字符类别 |
|
split(sep=None, maxsplit=-1) / rsplit |
分割成列表,默认按空白,丢弃空串 |
|
count(sub[, start[, end]]) |
统计子串出现次数(非重叠) |
|
find(sub[, start[, end]]) / rfind |
查找索引,找不到返回 -1 |
|
index(sub[, start[, end]]) / rindex |
同 find,但找不到会抛异常 |
演示几个容易踩坑的点:
s = "Line1-Li ne2-Line4"
# replace
print(s.replace("Li", "b")) # 全部替换
print(s.replace("Li", "b", 2)) # 只替换前2个
# strip 是移除字符集合中的任意组合,不是移除整个子串
str3 = 'www.example.com'
print(str3.strip("mecow.")) # 'xampl'(移除两端的任意 m/e/c/o/w/.)
# partition 一定返回三个元素,很适合拆固定格式
print("hello world".partition("l")) # ('he', 'l', 'lo world')
print("hello world".rpartition("l")) # ('hello wor', 'l', 'd')
# split 默认按空白,忽略空串
s = "Line1- abcdef \nLine2- abc \nLine4- abcd"
print(s.split()) # ['Line1-', 'abcdef', 'Line2-', 'abc', 'Line4-', 'abcd']
print(s.split(" ")) # 按单个空格分割,可能包含空串
四、列表(List)
列表是可变序列,你可以随时增删改,它基于动态数组实现,自动管理容量。用方括号 [] 定义。
1. 列表的构造与修改
# 构造
list0 = []
list1 = ['china', 1997, 2000]
list2 = list("hello") # ['h','e','l','l','o']
list3 = list({1:2, 3:4}) # [1, 3](取字典的键)
# 修改(索引和切片赋值)
li = ["h", "e", "l", "l", "o", "l", "l", "牛", "3"]
li[4] = "-" # 改单个元素
li[0:4] = ["H", "E", "L", "L"] # 切片替换,长度可以不同
print(li) # ['H','E','L','L','-','l','l','牛','3']
注意:列表的切片赋值是“变长”的,你甚至可以用空列表 [] 来删除一段切片,或者用更长的列表来插入。
2. 列表的常用方法(增删改查排)
|
方法 |
说明 |
|
append(x) |
末尾添加一个元素 |
|
extend(iterable) |
末尾添加可迭代对象中的每个元素 |
|
insert(i, x) |
在索引 i 处插入 |
|
pop([i]) |
删除并返回索引 i 处的元素,默认最后一个 |
|
remove(x) |
删除第一个匹配项,无返回值 |
|
index(x[, start[, end]]) |
返回第一个匹配的索引,找不到抛异常 |
|
count(x) |
统计 x 出现次数 |
|
sort(key=None, reverse=False) |
原地排序 |
|
reverse() |
原地反转 |
|
copy() |
浅拷贝 |
|
clear() |
清空 |
演示一下容易混淆的 `append` 和 `extend`:
li = [1, 2]
li.append([3, 4]) # [1, 2, [3, 4]](把列表当整体添加)
li.extend([5, 6]) # [1, 2, [3, 4], 5, 6](展开添加)
排序和反转的example:
num_list = [1, -2, 5, -3]
num_list.sort() # 升序 [-3,-2,1,5]
num_list.sort(reverse=True) # 降序 [5,1,-2,-3]
num_list.sort(key=abs) # 按绝对值排序 [1,-2,-3,5](注意原数据顺序)
# reverse 和 reversed 的区别
li = [1, 3, 5, 2]
li.reverse() # 原地反转,无返回值
print(li) # [2,5,3,1]
# reversed 返回反向迭代器,不改变原数据
seq = "helloworld"
it = reversed(seq)
print(list(it)) # ['d','l','r','o','w','o','l','l','e','h']
3. sort 与 sorted 的区别
- list.sort() 是列表的方法,原地修改,返回 None。
- sorted(iterable, key=None, reverse=False) 是内置函数,返回新列表,不修改原数据,适用于任何可迭代对象。
tup = (1, -2, 5, -3)
new_list = sorted(tup, key=abs) # [1, -2, -3, 5] 原元组不变
4. 浅拷贝(copy)的陷阱
list.copy() 和 list[:] 都是浅拷贝——只复制外层引用,内部嵌套的可变对象仍然共享。
original = [[1, 2], 3]
copied = original.copy()
copied[0].append(99) # 修改内部列表,original 也会变
copied[1] = 4 # 重新赋值,不影响 original
print(original) # [[1, 2, 99], 3]
如果完全独立,需要使用 copy.deepcopy()(从 copy 模块导入)。
五、元组(Tuple)
元组是不可变序列,用圆括号 () 定义,但逗号是核心。
t1 = () # 空元组
t2 = (1,) # 单元素元组,逗号不能少
t3 = (1) # 这是整数!
t4 = 1, 2, 3 # 不加括号也是元组(俗称“元组打包”)
因为不可变,元组的方法很少(只有 count() 和 index()),但它占用内存小、创建快,还能作为字典的键(因为可哈希)。不过要记住:如果元组内部包含可变元素,那个元素本身还是可变的,只是元组的引用不变。
六、字典(Dictionary)
字典是基于哈希表实现的,查找速度 O(1)。它是可变映射,键必须是不可变类型(可哈希)。
1. 创建字典的六种姿势
# ① 直接 {}
a = {'姓名': '张三', '年龄': 28}
# ② 空字典,然后赋值
a = {}
a['姓名'] = '张三'
# ③ 关键字参数(键名必须符合标识符规则)
a = dict(姓名="张三", 年龄=28)
# ④ 可迭代对象(列表/元组/集合)包含键值对
a = dict([("姓名", "张三"), ("年龄", 28)])
# ⑤ zip 打包
a = dict(zip(["姓名", "年龄"], ["张三", 28]))
# ⑥ fromkeys 类方法(批量初始化,值默认 None)
dic1 = dict.fromkeys(("name", "age", "sex"))
dic2 = dict.fromkeys(("name", "age", "sex"), "I don't know!")
2. 访问与修改
d1 = {"身高": 175, "体重": 65}
print(d1["身高"]) # 175,键不存在会抛 KeyError
d1["身高"] = "1米75" # 修改
d1["名字"] = "张三" # 新增
安全取值用 `get()`,还可以设置默认值:
print(d1.get("体重")) # 65
print(d1.get("体", "不存在")) # "不存在"
3. 视图对象(动态变化)
keys()、values()、items() 返回视图,会实时反映字典的修改。
d = {'a': 1, 'b': 2}
view = d.keys()
print(list(view)) # ['a', 'b']
d['c'] = 3
print(list(view)) # ['a', 'b', 'c'](动态更新)
4. 常用字典方法
|
方法 |
说明 |
|
get(key[, default]) |
安全取值 |
|
update([other]) |
合并更新,覆盖相同的键 |
|
pop(key[, default]) |
删除并返回,不存在可返回默认或抛异常 |
|
popitem() |
删除并返回最后一个键值对(Python 3.7+ 有序) |
|
setdefault(key[, default]) |
若存在返回其值,否则插入 key:default |
|
copy() |
浅拷贝 |
|
clear() |
清空 |
`setdefault` 的妙用(常用于计数):
counts = {}
for char in "abracadabra":
counts[char] = counts.setdefault(char, 0) + 1
print(counts) # {'a':5, 'b':2, 'r':2, 'c':1, 'd':1}
不过更推荐 collections.Counter。
七、集合(Set)—— 去重与关系运算的利器
集合是可变、无序、不重复的容器,用花括号 {} 或 set() 定义(空集合必须用 set(),因为 {} 是空字典)。元素必须是不可变类型。
1. 基本操作
# 去重
li = [1, 2, 4, 5, 7, 7, 4, 5]
s = set(li) # {1,2,4,5,7}
# 集合运算
a = set("abdefga")
b = set("abc")
c = set("aef")
print(c <= a) # True(判断子集)
print(a | b) # 并集
print(a & b) # 交集
print(a - b) # 差集
print(a ^ b) # 对称差
2. 方法一览(分两大类)
① 所有 set 和 frozenset 都支持的方法(返回新集合)
|
方法 |
运算符版本 |
说明 |
|
union(*others) |
| |
并集 |
|
intersection(*others) |
& |
交集 |
|
difference(*others) |
- |
差集 |
|
symmetric_difference(other) |
^ |
对称差 |
|
issubset(other) |
<= |
子集判断 |
|
issuperset(other) |
>= |
超集判断 |
|
isdisjoint(other) |
无 |
无交集则 True |
② 仅 set 对象可用的原地修改方法
|
方法 |
说明 |
|
update(*others) |
添加所有元素 |
|
intersection_update(*others) |
只保留交集 |
|
difference_update(*others) |
移除所有 others 中的元素 |
|
symmetric_difference_update(other) |
更新为对称差 |
|
add(elem) |
添加元素 |
|
remove(elem) |
删除,不存在抛 KeyError |
|
discard(elem) |
删除,不存在也不报错 |
|
pop() |
弹出任意一个元素 |
|
clear() |
清空 |
注意 `frozenset`:它是不可变的集合,可以作为普通集合的元素或字典的键。
fset = frozenset([1,2,3])
set1 = {"four"}
set1.add(fset) # 合法,因为 fset 可哈希
print(set1) # {'four', frozenset({1,2,3})}
八、序列的索引与切片
字符串、列表、元组都是序列,它们共享一套强大的索引和切片语法。
1. 索引(Indexing):正着数、倒着数
- 正向索引从 0 到 len-1
- 负向索引从 -1 到 -len(-1 永远指向最后一个元素)
s = "Hello 1牛3 Python"
print(s[0]) # 'H'
print(s[-1]) # 'n'
print(s[6]) # '1'(空格也算一个字符)
print(s[-11]) # ' '(注意空格)
2. 切片(Slicing):[start:stop:step]
- 包含 start,不包含 stop(左闭右开)
- 默认 start=0,stop=len,step=1
- 步长为负时从右往左取
s = "Hello 1牛3 Python"
print(s[0:2]) # 'He'
print(s[:2]) # 'He'
print(s[1:]) # 从索引1到末尾
print(s[:]) # 完整复制
print(s[-11:-9]) # ' 1'
print(s[2:6]) # 'llo '
print(s[1:10:2]) # 'el 牛'(步长2)
print(s[::-1]) # 反转字符串
3. 一个重要区别:索引会降维,切片不降维
对列表而言,索引得到的是该位置的元素本身(可能是个整数、字符串甚至另一个列表),而切片得到的是新的列表(即使只取一个元素,它也是列表)。
li = [123, "e", "l", "l", "o"]
print(li[0]) # 123(int)
print(li[:1]) # [123](list)
这个特性在嵌套数据结构中尤其需要注意。
九、补充神器:len() 与 del
- len(obj) 返回长度,O(1) 时间(容器内部维护了大小)。
- del 删除变量或序列中的元素,本质是解除引用,当引用计数归零时,内存会被回收。
a = [1, 2, 3]
del a[0] # 删除第一个元素
del a[1:3] # 删除切片
del a[:] # 清空列表
del a # 删除变量本身
收尾
到这里,我们已经把 Python 的六大标准数据类型以及索引切片的边边角角都梳理了一遍。从数字到集合,从不可变到可变,从索引到切片——这些内容会在你日后的每一行代码里反复出现。初学阶段不要怕记不住,多用、多查、多调试,自然就刻在脑子里了。如果非要给个建议:字符串、列表、字典 是日常使用频率最高的三巨头,把它们的方法玩熟,你就已经能解决80% 的问题了。
下图为本文知识点的思维导图(示意):

感谢阅读,祝你编码愉快!有任何疑问,随时欢迎来聊。
更多推荐


所有评论(0)