☰
Python使用for循环遍历字符串列表字典的操作流程
2026/10/16 3:52:16 网站建设 项目流程

前言


for是 Python 中出现频率最高的关键字之一,但很多刚上手的人会默认「for就是按下标走一遍」,于是写出for i in range(len(data))这种从 C/Java 迁移过来的写法。Python 的for实际是迭代器协议(iterator protocol)驱动的:它调用iter(对象)拿到一个迭代器,然后反复调用next(),直到抛出StopIteration。理解这一点,才能解释为什么遍历字符串得到的是单个字符、遍历字典默认得到的是键而不是值。


另一个常见误解是「字符串也能像列表一样边遍历边删」。实际上str是不可变(immutable)类型,根本不支持原地删除;而dict、set在遍历过程中改变大小会直接抛RuntimeError。本文就把字符串、列表、字典这三类最常被for踩坑的对象的遍历流程讲清楚,并给出可复制的示例。


需要提前说明的是:本文全部使用 Python 3 的写法。Python 2.7 已于 2020 年 1 月 1 日停止维护,Python 2 里的dict.iteritems()、iterkeys()、itervalues()在 Python 3 中已被移除,请统一改用.items()、.keys()、.values()。


一、for 循环背后到底发生了什么


Python 的for x in obj:大致等价于下面这段手动展开的代码:


# 适用于 Python 3.8+
obj = [1, 2, 3]
it = iter(obj) # 取得迭代器
while True:
try:
x = next(it) # 取下一个元素
except StopIteration:
break # 迭代结束,安静地退出
print(x)

这里有三点必须记住:



  1. for只要求对象实现__iter__()(返回迭代器)或者__getitem__()(支持从 0 开始的整数下标),并不要求它是list。

  2. 结束信号是异常StopIteration,for会自己吞掉它,所以正常遍历不会看到这个异常。

  3. 迭代器是一次性的。同一个迭代器对象遍历完一遍后就是空的,想再遍历必须重新iter()。


二、遍历字符串:得到的是字符


在 Python 3 里,str是 Unicode 码点序列,因此逐个遍历拿到的是长度为 1 的字符串:


# 适用于 Python 3.8+
s = "Python"
for ch in s:
print(ch)
# P
# y
# t
# h
# o
# n

如果需要「字符 + 下标」,用内置函数enumerate(),不要自己维护计数器:


# 适用于 Python 3.8+
s = "abc"
for i, ch in enumerate(s):
print(i, ch)
# 0 a
# 1 b
# 2 c

enumerate(iterable, start=0)返回的是(下标, 元素)这样的二元组,start可以指定起始编号。


需要注意:Python 3 里len("中文")是 2,遍历也确实是 2 次,因为每个汉字是一个码点。但如果字符串里含组合字符或 emoji(比如带肤色修饰符的表情),它们可能由多个码点组成,逐个遍历会得到「半个字」。这属于 Unicode 本身的特性,不是 bug。


三、遍历列表:不要在循环里改长度


列表是最直观的:for依次给出元素本身。


# 适用于 Python 3.8+
nums = [10, 20, 30]
for n in nums:
print(n)

如果确实需要下标,有两条路:


# 适用于 Python 3.8+
nums = [10, 20, 30]

# 方式 A:enumerate,推荐
for i, n in enumerate(nums):
print(i, n)

# 方式 B:range(len(...)),能用但不 Pythonic
for i in range(len(nums)):
print(i, nums[i])

在遍历列表时增删元素是典型的坑。下面的代码不会报错,但结果和直觉不符:


# 适用于 Python 3.8+
nums = [1, 2, 3, 4, 5]
for n in nums:
if n % 2 == 0:
nums.remove(n)
print(nums) # [1, 3, 5]

这里2被删掉了,4却留了下来——因为删除2之后,后面的元素整体前移,迭代器的内部下标已经越过4。正确做法是遍历副本或者用列表推导式生成新列表:


# 适用于 Python 3.8+
nums = [1, 2, 3, 4, 5]
odds = [n for n in nums if n % 2 != 0]
print(odds) # [1, 3, 5]

四、遍历字典:默认给键,不是给键值对


这是初学者最容易踩的一个点。直接for k in d拿到的是键:


# 适用于 Python 3.8+
user = {"name": "Alice", "age": 30}

for k in user: # 等价于 for k in user.keys()
print(k)
# name
# age

要同时拿到键和值,用.items():


# 适用于 Python 3.8+
user = {"name": "Alice", "age": 30}

for k, v in user.items():
print(k, "=", v)
# name = Alice
# age = 30

.keys()、.values()、.items()在 Python 3 里返回的是视图(view)对象,不是列表。视图是动态的:字典变了,视图跟着变;但它不支持下标访问,需要列表时得显式list(user.keys())。


从Python 3.7 起,字典保证按插入顺序迭代(3.6 是 CPython 的实现细节,不应依赖)。所以下面的输出顺序是确定的:


# 适用于 Python 3.7+
d = {}
d["b"] = 1
d["a"] = 2
print(list(d)) # ['b', 'a'],按插入顺序

三种可迭代对象的对比:




对象类型for x in obj得到是否可变遍历中改大小会怎样



str单个字符(长度 1 的str)不可变无法原地修改,不涉及

list元素本身可变不报错,但结果可能不符合预期

dict键(key)可变抛RuntimeError

set元素本身,顺序不保证可变抛RuntimeError



五、实战:一次遍历三种结构


下面这段程序演示了对字符串、列表、字典的统一处理流程,可直接存为demo.py运行:


# 适用于 Python 3.8+

def iter_text(text):
for i, ch in enumerate(text):
print("字符", i, "->", ch)

def iter_nums(nums):
total = 0
for n in nums:
total += n
return total

def iter_mapping(mapping):
result = []
for key, value in mapping.items():
result.append(f"{key}:{value}")
return ", ".join(result)

if __name__ == "__main__":
iter_text("abc")
print("求和 =", iter_nums([1, 2, 3, 4]))
print(iter_mapping({"name": "Alice", "age": 30}))

输出:


字符 0 -> a
字符 1 -> b
字符 2 -> c
求和 = 10
name:Alice, age:30

常见坑点



  1. 遍历字典时以为拿到的是键值对


❌for x in user: print(x, user[x])里x是键,写法啰嗦又容易和值搞混 ✅for k, v in user.items(): print(k, v)直接用.items()



  1. 遍历列表时删除元素


❌for n in nums: if cond(n): nums.remove(n)会漏删、错删 ✅nums = [n for n in nums if not cond(n)]或for n in nums[:]:



  1. 遍历字典时新增或删除键


❌for k in d: d["new"] = 1抛RuntimeError: dictionary changed size during iteration✅ 先收集要改的键keys = list(d),遍历keys再去改d



  1. 用range(len(...))硬凑下标


❌for i in range(len(s)): print(s[i])✅for ch in s: print(ch);需要下标时用enumerate(s)



  1. 以为.keys()返回列表可以下标访问


❌d.keys()[0]在 Python 3 里抛TypeError(视图不支持下标) ✅list(d.keys())[0],或者直接用next(iter(d))



  1. 把迭代器当成可重复遍历的对象


❌it = iter(nums)之后sum(it)再用sum(it),第二次得到0✅ 需要多次遍历就每次都从原容器iter(),或先把迭代器转成list



  1. 沿用 Python 2 的iteritems()


❌for k, v in d.iteritems():在 Python 3 中是AttributeError(该方法已移除) ✅for k, v in d.items():(Python 3 的.items()本身就是惰性视图,不再需要iter*变体)



  1. 遍历字符串时按「可见字符」数下标


❌for i, ch in enumerate("👍"):期望只循环一次,实际可能循环多次(组合码点) ✅ 明确需求:按码点循环就用for ch in s;按字节循环就s.encode("utf-8")后遍历


总结




遍历目标推荐写法关键点



字符串for ch in s得到单个字符,Python 3 按码点

需要下标for i, x in enumerate(obj)默认从 0 开始,可传start

列表元素for x in lst循环内别改长度

字典键for k in d3.7+ 保证插入顺序

字典键值for k, v in d.items()视图对象,实时反映字典

两个序列并行for a, b in zip(x, y)以短的那个为准



for循环的语义由迭代器协议决定,而不是下标。记住「字符串给字符、列表给元素、字典给键」这条主线,再配合enumerate()和.items(),绝大多数遍历写法就不会出错。涉及增删的场合,一律先复制或改为生成新对象,避免掉进「改了却看不到效果」的陷阱。




需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询