# 🐍 前端开发 0 基础学 Python 入门指南: Python 元组和映射类型深入指南

Python 元组和映射类型深入指南

📌 引言

在上一篇《Python 数据类型完全指南》中,我们概览了 Python 的各种数据类型。本文将深入探讨两个重要但常被忽视的类型:元组(Tuple)映射类型(主要是 Dict)。这两种类型在 Python 编程中扮演着关键角色。


第一部分:元组(Tuple)深度解析

🎯 什么是元组?

元组是 Python 中的不可变序列,一旦创建就不能修改。你可以把它理解为"只读列表"或"常量数组"。

📝 创建元组的多种方式

python 复制代码
# 1. 使用圆括号
coordinates = (3, 4)
rgb = (255, 128, 0)

# 2. 省略圆括号(隐式元组)
point = 10, 20
user_info = "张三", 25, "北京"

# 3. 单元素元组(注意逗号!)
single = (1,)        # ✅ 这是元组
not_tuple = (1)      # ❌ 这只是整数 1,括号被当作运算符

# 4. 空元组
empty = ()
empty2 = tuple()

# 5. 使用 tuple() 构造函数
from_list = tuple([1, 2, 3])
from_string = tuple("abc")  # ('a', 'b', 'c')
from_range = tuple(range(5))  # (0, 1, 2, 3, 4)

🔍 访问和操作元组

python 复制代码
fruits = ('apple', 'banana', 'cherry', 'date', 'elderberry')

# 索引访问
print(fruits[0])      # 'apple'
print(fruits[-1])     # 'elderberry'(负索引)
print(fruits[-2])     # 'date'

# 切片操作(返回新元组)
print(fruits[1:3])    # ('banana', 'cherry')
print(fruits[:2])     # ('apple', 'banana')
print(fruits[2:])     # ('cherry', 'date', 'elderberry')
print(fruits[::2])    # ('apple', 'cherry', 'elderberry')(步长为2)
print(fruits[::-1])   # 反转元组

# 元组拼接(创建新元组)
t1 = (1, 2)
t2 = (3, 4)
t3 = t1 + t2  # (1, 2, 3, 4)

# 元组重复
repeated = (1, 2) * 3  # (1, 2, 1, 2, 1, 2)

# 检查元素是否存在
print('apple' in fruits)      # True
print('grape' not in fruits)  # True

⚠️ 元组的不可变性

python 复制代码
t = (1, 2, 3)

# ❌ 以下操作都会报错
# t[0] = 10          # TypeError: 'tuple' object does not support item assignment
# t.append(4)        # AttributeError: 'tuple' object has no attribute 'append'
# del t[1]           # TypeError: 'tuple' object doesn't support item deletion

# ✅ 但可以重新赋值变量
t = (4, 5, 6)  # 这是创建了新元组,而不是修改原元组

# ⚠️ 特殊情况:元组包含可变对象
mixed = (1, 2, [3, 4])
# mixed[2] = [5, 6]  # ❌ 不能替换列表
mixed[2][0] = 99     # ✅ 但可以修改列表内容
print(mixed)         # (1, 2, [99, 4])

🛠️ 元组的方法(只有 2 个!)

由于元组不可变,它只有两个方法:

python 复制代码
numbers = (1, 2, 3, 2, 4, 2, 5)

# 1. count() - 统计某个值出现的次数
print(numbers.count(2))  # 3
print(numbers.count(10)) # 0

# 2. index() - 查找某个值首次出现的索引
print(numbers.index(2))        # 1
print(numbers.index(2, 2))     # 从索引2开始查找:3
print(numbers.index(2, 4, 7))  # 在索引4-7范围内查找:5

# 找不到会报错
try:
    numbers.index(10)
except ValueError as e:
    print(f"错误:{e}")  # 错误:tuple.index(x): x not in tuple

🎁 元组解包(Unpacking)

这是元组最强大的特性之一!

基础解包
python 复制代码
# 基本解包
point = (3, 4)
x, y = point
print(f"x={x}, y={y}")  # x=3, y=4

# 多值解包
user = ("张三", 25, "北京", "工程师")
name, age, city, job = user

# 交换变量(Python 的优雅之处!)
a, b = 10, 20
a, b = b, a  # 交换
print(a, b)  # 20 10

# 函数返回多个值
def get_min_max(numbers):
    return min(numbers), max(numbers)

minimum, maximum = get_min_max([3, 1, 4, 1, 5])
print(f"最小值:{minimum},最大值:{maximum}")
扩展解包(*运算符)
python 复制代码
# * 收集剩余元素
first, *rest = (1, 2, 3, 4, 5)
print(first)  # 1
print(rest)   # [2, 3, 4, 5](注意:rest 是列表!)

# * 在中间
first, *middle, last = (1, 2, 3, 4, 5)
print(first)   # 1
print(middle)  # [2, 3, 4]
print(last)    # 5

# * 在开头
*head, second_last, last = (1, 2, 3, 4, 5)
print(head)         # [1, 2, 3]
print(second_last)  # 4
print(last)         # 5

# 只取部分值,忽略其他
name, _, _, job = ("张三", 25, "北京", "工程师")
print(name, job)  # 张三 工程师

# 嵌套解包
person = ("李四", (170, 65))  # 姓名和(身高, 体重)
name, (height, weight) = person
print(f"{name}: {height}cm, {weight}kg")

🎯 元组的实际应用场景

1. 函数返回多个值
python 复制代码
def divide_with_remainder(dividend, divisor):
    """返回商和余数"""
    quotient = dividend // divisor
    remainder = dividend % divisor
    return quotient, remainder

q, r = divide_with_remainder(17, 5)
print(f"17 ÷ 5 = {q} ... {r}")  # 17 ÷ 5 = 3 ... 2

# 内置函数 divmod() 就是这样做的
q, r = divmod(17, 5)
2. 字典的键(列表不能作为键)
python 复制代码
# ✅ 元组可以作为字典的键
locations = {
    (40.7128, -74.0060): "纽约",
    (51.5074, -0.1278): "伦敦",
    (35.6762, 139.6503): "东京"
}

# ❌ 列表不能作为键
# locations[[40.7128, -74.0060]] = "纽约"  # TypeError: unhashable type: 'list'

# 查询坐标
coords = (40.7128, -74.0060)
print(locations[coords])  # 纽约
3. 保护数据不被修改
python 复制代码
# 配置常量
DATABASE_CONFIG = (
    "localhost",
    5432,
    "mydb",
    "username"
)

# RGB 颜色值
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

# 尺寸规格
SIZES = (
    ("S", 85, 160),
    ("M", 90, 165),
    ("L", 95, 170),
    ("XL", 100, 175)
)
4. 作为轻量级数据结构
python 复制代码
# 比定义类更轻量
students = [
    ("张三", 18, 85),
    ("李四", 19, 92),
    ("王五", 18, 78)
]

for name, age, score in students:
    print(f"{name}({age}岁): {score}分")

# 当然,如果数据复杂,还是应该用命名元组或类
from collections import namedtuple
Student = namedtuple('Student', ['name', 'age', 'score'])
s = Student("张三", 18, 85)
print(s.name, s.score)  # 张三 85

⚡ 元组 vs 列表:性能对比

python 复制代码
import sys
import timeit

# 内存占用
list_data = [1, 2, 3, 4, 5]
tuple_data = (1, 2, 3, 4, 5)

print(f"列表大小:{sys.getsizeof(list_data)} 字节")
print(f"元组大小:{sys.getsizeof(tuple_data)} 字节")
# 列表大小:104 字节
# 元组大小:80 字节

# 创建速度
list_time = timeit.timeit('[1, 2, 3, 4, 5]', number=1000000)
tuple_time = timeit.timeit('(1, 2, 3, 4, 5)', number=1000000)

print(f"创建列表耗时:{list_time:.4f} 秒")
print(f"创建元组耗时:{tuple_time:.4f} 秒")
# 元组通常更快

💡 元组最佳实践

python 复制代码
# ✅ 使用元组表示固定结构的数据
def get_user_info():
    return "张三", 25, "engineer"

# ✅ 使用元组作为字典键
cache = {
    ('user', 123): {'name': '张三', 'age': 25},
    ('user', 456): {'name': '李四', 'age': 30}
}

# ✅ 元组解包让代码更清晰
for name, age, job in [("张三", 25, "工程师"), ("李四", 30, "设计师")]:
    print(f"{name} - {job}")

# ✅ 使用 _ 忽略不需要的值
name, _, job = ("张三", 25, "工程师")

# ❌ 避免过长的元组(超过5个元素考虑用命名元组或类)
# bad = ("张三", 25, "北京", "工程师", "男", "13800138000", "...")

# ✅ 对于复杂数据,使用命名元组
from collections import namedtuple
Person = namedtuple('Person', ['name', 'age', 'city', 'job'])
p = Person("张三", 25, "北京", "工程师")
print(p.name, p.job)

第二部分:映射类型深度解析

🗺️ 字典(Dict)详解

字典是 Python 中最强大的数据类型之一,用于存储键值对。

📝 创建字典的多种方式

python 复制代码
# 1. 字面量语法
user = {
    'name': '张三',
    'age': 25,
    'email': 'zhangsan@example.com'
}

# 2. dict() 构造函数
user2 = dict(name='李四', age=30, email='lisi@example.com')

# 3. 从键值对列表创建
pairs = [('name', '王五'), ('age', 28)]
user3 = dict(pairs)

# 4. 使用 fromkeys() 创建(所有键共享同一个值)
default_config = dict.fromkeys(['host', 'port', 'database'], None)
# {'host': None, 'port': None, 'database': None}

# 5. 字典推导式
squares = {x: x**2 for x in range(6)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 6. 使用 zip() 组合两个序列
keys = ['name', 'age', 'city']
values = ['赵六', 35, '上海']
user4 = dict(zip(keys, values))

🔍 访问字典元素

python 复制代码
user = {
    'name': '张三',
    'age': 25,
    'city': '北京',
    'hobbies': ['reading', 'coding']
}

# 方法1:使用 [] 索引(键不存在会报错)
print(user['name'])  # '张三'
# print(user['phone'])  # ❌ KeyError: 'phone'

# 方法2:使用 get()(推荐,更安全)
print(user.get('name'))        # '张三'
print(user.get('phone'))       # None
print(user.get('phone', '未设置'))  # '未设置'(提供默认值)

# 检查键是否存在
if 'email' in user:
    print(user['email'])
else:
    print("邮箱未设置")

# 使用 setdefault()(获取值,不存在则设置默认值)
phone = user.setdefault('phone', '13800138000')
print(phone)  # '13800138000'
print(user)   # 字典中已添加 'phone' 键

✏️ 修改和更新字典

python 复制代码
user = {'name': '张三', 'age': 25}

# 1. 直接赋值(修改或添加)
user['age'] = 26           # 修改
user['email'] = 'z@ex.com' # 添加

# 2. update() - 批量更新
user.update({'city': '北京', 'age': 27})
user.update(phone='13800138000', job='工程师')

# 3. 合并字典(Python 3.9+)
defaults = {'theme': 'dark', 'language': 'zh'}
custom = {'language': 'en', 'fontSize': 14}

# 方法1:使用 | 运算符
config = defaults | custom
# {'theme': 'dark', 'language': 'en', 'fontSize': 14}

# 方法2:使用 |= 就地更新
defaults |= custom

# 方法3:使用解包(适用于所有 Python 3.5+)
config = {**defaults, **custom}

# 4. 嵌套更新(合并嵌套字典)
def deep_merge(dict1, dict2):
    """深度合并字典"""
    result = dict1.copy()
    for key, value in dict2.items():
        if key in result and isinstance(result[key], dict) and isinstance(value, dict):
            result[key] = deep_merge(result[key], value)
        else:
            result[key] = value
    return result

base = {'a': 1, 'b': {'x': 10, 'y': 20}}
update = {'b': {'y': 30, 'z': 40}, 'c': 3}
merged = deep_merge(base, update)
# {'a': 1, 'b': {'x': 10, 'y': 30, 'z': 40}, 'c': 3}

🗑️ 删除字典元素

python 复制代码
user = {
    'name': '张三',
    'age': 25,
    'email': 'z@ex.com',
    'phone': '13800138000'
}

# 1. del - 删除指定键
del user['phone']

# 2. pop() - 删除并返回值
email = user.pop('email')
print(email)  # 'z@ex.com'

# pop() 提供默认值(避免 KeyError)
website = user.pop('website', None)

# 3. popitem() - 删除并返回最后一个键值对(Python 3.7+ 保证顺序)
item = user.popitem()
print(item)  # ('age', 25)

# 4. clear() - 清空字典
user.clear()
print(user)  # {}

🔄 遍历字典

python 复制代码
user = {
    'name': '张三',
    'age': 25,
    'city': '北京',
    'job': '工程师'
}

# 1. 遍历键(默认行为)
for key in user:
    print(key)

# 明确遍历键
for key in user.keys():
    print(f"{key}: {user[key]}")

# 2. 遍历值
for value in user.values():
    print(value)

# 3. 遍历键值对(推荐)
for key, value in user.items():
    print(f"{key}: {value}")

# 4. 带索引遍历
for index, (key, value) in enumerate(user.items()):
    print(f"{index}. {key} = {value}")

# 5. 反向遍历(Python 3.8+)
for key in reversed(user):
    print(key)

# 6. 按键排序遍历
for key in sorted(user.keys()):
    print(f"{key}: {user[key]}")

# 7. 按值排序遍历
for key, value in sorted(user.items(), key=lambda item: item[1]):
    print(f"{key}: {value}")

🛠️ 字典的高级方法

1. keys(), values(), items()
python 复制代码
user = {'name': '张三', 'age': 25, 'city': '北京'}

# 返回视图对象(动态反映字典变化)
keys = user.keys()      # dict_keys(['name', 'age', 'city'])
values = user.values()  # dict_values(['张三', 25, '北京'])
items = user.items()    # dict_items([('name', '张三'), ('age', 25), ('city', '北京')])

# 视图是动态的
user['job'] = '工程师'
print(keys)  # dict_keys(['name', 'age', 'city', 'job'])

# 转换为列表
keys_list = list(user.keys())
values_list = list(user.values())
2. copy() - 浅拷贝
python 复制代码
original = {'a': 1, 'b': [2, 3]}

# 浅拷贝
shallow = original.copy()
shallow['a'] = 10
shallow['b'].append(4)

print(original)  # {'a': 1, 'b': [2, 3, 4]} - 嵌套对象被影响
print(shallow)   # {'a': 10, 'b': [2, 3, 4]}

# 深拷贝
import copy
deep = copy.deepcopy(original)
deep['b'].append(5)
print(original)  # {'a': 1, 'b': [2, 3, 4]} - 不受影响
print(deep)      # {'a': 1, 'b': [2, 3, 4, 5]}
3. setdefault() - 获取或设置默认值
python 复制代码
# 统计字符出现次数
text = "hello world"
char_count = {}

for char in text:
    char_count[char] = char_count.get(char, 0) + 1
    # 或使用 setdefault
    # char_count.setdefault(char, 0)
    # char_count[char] += 1

print(char_count)

🎯 字典推导式

python 复制代码
# 基础推导式
squares = {x: x**2 for x in range(6)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 带条件过滤
even_squares = {x: x**2 for x in range(10) if x % 2 == 0}
# {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

# 转换现有字典
prices = {'apple': 3, 'banana': 2, 'cherry': 5}
discounted = {item: price * 0.8 for item, price in prices.items()}
# {'apple': 2.4, 'banana': 1.6, 'cherry': 4.0}

# 键值互换
original = {'a': 1, 'b': 2, 'c': 3}
swapped = {value: key for key, value in original.items()}
# {1: 'a', 2: 'b', 3: 'c'}

# 从两个列表创建字典
keys = ['name', 'age', 'city']
values = ['张三', 25, '北京']
user = {k: v for k, v in zip(keys, values)}

🎯 字典的实际应用场景

1. 数据聚合和分组
python 复制代码
students = [
    {'name': '张三', 'class': '1班', 'score': 85},
    {'name': '李四', 'class': '2班', 'score': 92},
    {'name': '王五', 'class': '1班', 'score': 78},
    {'name': '赵六', 'class': '2班', 'score': 88}
]

# 按班级分组
from collections import defaultdict
grouped = defaultdict(list)

for student in students:
    grouped[student['class']].append(student)

for class_name, students_list in grouped.items():
    print(f"{class_name}: {len(students_list)}人")
2. 缓存和记忆化
python 复制代码
# 使用字典缓存计算结果
def fibonacci(n, cache={}):
    if n in cache:
        return cache[n]

    if n <= 1:
        return n

    result = fibonacci(n-1, cache) + fibonacci(n-2, cache)
    cache[n] = result
    return result

print(fibonacci(100))  # 很快!
3. 配置管理
python 复制代码
# 多层配置合并
default_config = {
    'database': {
        'host': 'localhost',
        'port': 5432,
        'name': 'mydb'
    },
    'cache': {
        'enabled': True,
        'ttl': 3600
    }
}

user_config = {
    'database': {
        'host': '192.168.1.100'
    },
    'cache': {
        'ttl': 7200
    }
}

# 合并配置(保留嵌套结构)
final_config = {**default_config, **user_config}
4. 计数器(使用 Counter)
python 复制代码
from collections import Counter

# 统计词频
text = "apple banana apple cherry banana apple"
word_count = Counter(text.split())
print(word_count)  # Counter({'apple': 3, 'banana': 2, 'cherry': 1})

# 最常见的元素
print(word_count.most_common(2))  # [('apple', 3), ('banana', 2)]

# Counter 运算
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)
print(c1 + c2)  # Counter({'a': 4, 'b': 3})
print(c1 - c2)  # Counter({'a': 2})

🔒 其他映射类型

1. defaultdict - 带默认值的字典
python 复制代码
from collections import defaultdict

# 普通字典需要检查键是否存在
normal_dict = {}
for item in ['a', 'b', 'a', 'c', 'b', 'a']:
    if item not in normal_dict:
        normal_dict[item] = 0
    normal_dict[item] += 1

# defaultdict 自动处理
dd = defaultdict(int)  # 默认值为 0
for item in ['a', 'b', 'a', 'c', 'b', 'a']:
    dd[item] += 1

print(dd)  # defaultdict(<class 'int'>, {'a': 3, 'b': 2, 'c': 1})

# 用于分组
grouped = defaultdict(list)
for key, value in [('fruit', 'apple'), ('veg', 'carrot'), ('fruit', 'banana')]:
    grouped[key].append(value)

print(grouped)  # defaultdict(<class 'list'>, {'fruit': ['apple', 'banana'], 'veg': ['carrot']})
2. OrderedDict - 记住插入顺序的字典
python 复制代码
from collections import OrderedDict

# 注意:Python 3.7+ 普通 dict 也保证顺序,OrderedDict 现在主要用于明确表达意图
od = OrderedDict()
od['first'] = 1
od['second'] = 2
od['third'] = 3

# 移动到末尾
od.move_to_end('first')
print(od)  # OrderedDict([('second', 2), ('third', 3), ('first', 1)])

# 移动到开头
od.move_to_end('third', last=False)
print(od)  # OrderedDict([('third', 3), ('second', 2), ('first', 1)])
3. ChainMap - 链式字典
python 复制代码
from collections import ChainMap

# 合并多个字典,按顺序查找
defaults = {'color': 'red', 'user': 'guest'}
environment = {'user': 'admin'}
cli_args = {'color': 'blue'}

combined = ChainMap(cli_args, environment, defaults)
print(combined['color'])  # 'blue'(从第一个字典找到)
print(combined['user'])   # 'admin'(从第二个字典找到)

# 查看所有字典
print(combined.maps)  # [{'color': 'blue'}, {'user': 'admin'}, {'color': 'red', 'user': 'guest'}]

💡 字典最佳实践

python 复制代码
# ✅ 使用 get() 而不是 []
value = user.get('key', default_value)

# ✅ 使用 items() 遍历键值对
for key, value in user.items():
    print(key, value)

# ✅ 使用字典推导式
squared = {x: x**2 for x in range(10)}

# ✅ 使用 defaultdict 简化逻辑
from collections import defaultdict
dd = defaultdict(list)

# ✅ 使用 ** 解包合并字典
merged = {**dict1, **dict2}

# ❌ 避免在遍历时修改字典大小
# for key in user:
#     del user[key]  # 会报错

# ✅ 创建副本来修改
for key in list(user.keys()):
    if condition:
        del user[key]

# ✅ 使用类型提示(Python 3.9+)
from typing import Dict, List
user_scores: Dict[str, int] = {'Alice': 95, 'Bob': 87}

📊 总结对比

元组 vs 列表

特性 元组 列表
可变性 不可变 可变
语法 () []
性能 更快,占用内存更少 稍慢
方法 只有 count()index() 丰富的修改方法
用途 固定数据、函数返回值、字典键 动态数据集合

字典的关键特性

  • ✅ 键必须是不可变类型(如字符串、数字、元组)
  • ✅ Python 3.7+ 保证插入顺序
  • ✅ 平均 O(1) 查找时间
  • ✅ 支持推导式语法
  • ✅ 可以嵌套任意深度

🚀 实战练习

练习 1:统计文本中单词出现次数

python 复制代码
def word_frequency(text):
    """统计单词频率并返回排序后的结果"""
    from collections import Counter
    words = text.lower().split()
    counter = Counter(words)
    return counter.most_common()

text = "apple banana apple cherry banana apple orange"
print(word_frequency(text))

练习 2:实现 LRU 缓存

python 复制代码
from collections import OrderedDict

class LRUCache:
    def __init__(self, capacity):
        self.cache = OrderedDict()
        self.capacity = capacity

    def get(self, key):
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key)
        return self.cache[key]

    def put(self, key, value):
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)

# 使用示例
cache = LRUCache(2)
cache.put(1, "a")
cache.put(2, "b")
print(cache.get(1))  # "a"
cache.put(3, "c")    # 淘汰键 2
print(cache.get(2))  # -1

📚 延伸阅读


本文档最后更新:2025 年 10 月 23 日

相关推荐
_志哥_5 小时前
多行文本超出,中间显示省略号的终极方法(适配多语言)
前端·javascript·vue.js
王六岁5 小时前
# 🐍 前端开发 0 基础学 Python 入门指南:常用的数据类型和列表
前端·javascript·python
1_2_3_5 小时前
神级JS API,谁用谁好用
前端·javascript
南枝异客5 小时前
查找算法-顺序查找
python·算法
冬至已至5 小时前
AI 时代的自动化信息获取与整合
前端
花开花富贵5 小时前
不敢去表白?来用代码画♥
python
用户6600676685395 小时前
从送花表白实例读懂 JavaScript 对象字面量与代理模式
javascript
我是日安5 小时前
从零到一打造 Vue3 响应式系统 Day 29 - readonly:数据保护实现
前端·javascript·vue.js
时代拖油瓶5 小时前
我劝你必须知道——Intl.Segmenter
前端·javascript