在 C# 里写 for 循环,你得这样:

for (int i = 0; i < 10; i++)
{
    Console.WriteLine(i);
}

在 Python 里?这样:

for i in range(10):
    print(i)

发现了没?Python 的 for 循环没有 i++,没有 i < 10,只有一个 range(10)

还有那个让 C# 程序员崩溃的 for...else——循环没被 break 就执行 else?这是什么鬼?

今天咱们来聊聊循环结构——C# 的"传统 for" vs Python 的"for-in"。

for 循环

C# 版本:

// 传统 for 循环
for (int i = 0; i < 10; i++)
{
    Console.WriteLine(i);
}

// foreach 循环
int[] numbers = { 1, 2, 3, 4, 5 };
foreach (var num in numbers)
{
    Console.WriteLine(num);
}

// 倒序遍历
for (int i = numbers.Length - 1; i >= 0; i--)
{
    Console.WriteLine(numbers[i]);
}

// 带索引的 foreach
for (int i = 0; i < numbers.Length; i++)
{
    Console.WriteLine($"{i}: {numbers[i]}");
}

Python 版本:

# for-in 循环(Python 只有这一种 for)
for i in range(10):
    print(i)

# 遍历列表
numbers = [1, 2, 3, 4, 5]
for num in numbers:
    print(num)

# 倒序遍历
for num in reversed(numbers):
    print(num)

# 带索引的遍历
for i, num in enumerate(numbers):
    print(f"{i}: {num}")
对比项 C# Python
传统 for for (int i=0; i<10; i++) for i in range(10):
foreach foreach (var x in list) for x in list:
倒序 for (int i=len-1; i>=0; i--) for x in reversed(list):
带索引 手动维护 i enumerate(list)
步长 i += 2 range(0, 10, 2)

Python 的 for 循环是"for-in"风格——你只需要说"遍历这个",不需要告诉它怎么走。C# 的传统 for 循环是"命令式"的,你得告诉它从哪开始、到哪结束、每次加多少。

while 循环

C# 版本:

int i = 0;
while (i < 10)
{
    Console.WriteLine(i);
    i++;
}

// do-while 循环(Python 没有)
int j = 0;
do
{
    Console.WriteLine(j);
    j++;
} while (j < 10);

Python 版本:

i = 0
while i < 10:
    print(i)
    i += 1

# Python 没有 do-while,但可以模拟
# 方法 1:while True + break
while True:
    print(i)
    i += 1
    if i >= 10:
        break

# 方法 2:使用 walrus 运算符(3.8+)
while (i := i + 1) <= 10:
    print(i)

C# 有 do-while,Python 没有。Python 的替代方案是 while True + break,或者用 walrus 运算符 :=

循环控制

C# 版本:

for (int i = 0; i < 100; i++)
{
    if (i == 5) continue;  // 跳过 5
    if (i == 10) break;    // 遇到 10 停止
    Console.WriteLine(i);
}

Python 版本:

for i in range(100):
    if i == 5: continue  # 跳过 5
    if i == 10: break    # 遇到 10 停止
    print(i)

breakcontinue 的用法在两种语言里几乎一样——这是少数几个完全相同的地方。

for-else:Python 独有的"反直觉"设计

这是 Python 最让人困惑的特性之一:

# for-else 在循环正常结束时执行(没有 break)
for i in range(10):
    if i == 5:
        print("找到了 5")
        break
else:
    # 循环正常结束(没有 break)才执行
    print("没找到 5")

# 实际应用场景:搜索
def find_item(lst, target):
    for item in lst:
        if item == target:
            return item
    else:
        # 循环正常结束,没有 return
        return None

C# 没有 for-else,Python 的 for-else 在循环没有被 break 时执行 else 块。这个设计很反直觉,但用对了场景会很方便。

enumerate 和 zip

C# 的索引遍历:

string[] names = { "Alice", "Bob", "Charlie" };
for (int i = 0; i < names.Length; i++)
{
    Console.WriteLine($"{i + 1}. {names[i]}");
}

// 并行遍历
string[] names2 = { "Alice", "Bob" };
int[] ages = { 25, 30 };
for (int i = 0; i < names2.Length; i++)
{
    Console.WriteLine($"{names2[i]}: {ages[i]}");
}

Python 的 enumerate 和 zip:

# enumerate(自动索引)
names = ["Alice", "Bob", "Charlie"]
for i, name in enumerate(names, start=1):
    print(f"{i}. {name}")

# zip(并行遍历)
names2 = ["Alice", "Bob"]
ages = [25, 30]
for name, age in zip(names2, ages):
    print(f"{name}: {age}")

# enumerate + zip
students = ["Alice", "Bob"]
scores = [90, 85]
for i, (name, score) in enumerate(zip(students, scores)):
    print(f"{i + 1}. {name}: {score}")

Python 的 enumerate()zip() 让你告别手动索引的痛苦。C# 得手动维护 i,Python 直接解包就行。

推导式预告

Python 的列表推导式(第二阶段详细讲):

numbers = [1, 2, 3, 4, 5]

# 传统 for 循环
squares = []
for x in numbers:
    squares.append(x ** 2)

# 列表推导式
squares = [x ** 2 for x in numbers]  # [1, 4, 9, 16, 25]

# 带条件的推导式
even_squares = [x ** 2 for x in numbers if x % 2 == 0]  # [4, 16]

# 字典推导式
square_dict = {x: x ** 2 for x in numbers}  # {1: 1, 2: 4, ...}

# 集合推导式
square_set = {x ** 2 for x in numbers}  # {1, 4, 9, 16, 25}

列表推导式是 Python 的杀手锏——一行代码顶 C# 的 Select().ToList()

设计哲学

C# 的哲学是多种循环——for、foreach、while、do-while,传统风格,类型安全,LINQ 扩展。

Python 的哲学是统一风格——for x in iterable 是唯一写法,迭代器协议让任何可迭代对象都能用 for,enumerate 和 zip 告别手动索引。

C# 的 for 循环是"命令式"的,你得告诉它怎么走; Python 的 for 循环是"声明式"的,你只需要说"遍历这个"。

更深层的原因

  • C# 的 for 循环需要明确的起始、终止、步长,这是C 风格的延续

  • Python 的 for 循环只需要一个可迭代对象,这是迭代器协议的体现

真实场景:在处理文件行时,Python 的 for 循环非常优雅:

# 逐行读取文件
with open("data.txt") as f:
    for line in f:  # 直接遍历文件对象
        print(line.strip())

C# 得这样写:

// 逐行读取文件
foreach (var line in File.ReadLines("data.txt"))
{
    Console.WriteLine(line);
}

Python 的 for line in f 直接遍历文件对象,C# 需要 File.ReadLines() 方法。

迁移指南:C# 开发者最容易犯的错

  1. 忘记 range 是左闭右开range(5) 是 0-4,不是 0-5

  2. 以为 for-else 很奇怪:它是用来搜索的,找到就 break,没找到就执行 else

  3. 忘记 enumerate:Python 不需要手动维护索引

  4. 以为 zip 会以最长为准:zip 以最短为准,用 zip_longest 以最长为准

  5. 遍历时修改列表:Python 不允许,用推导式或遍历副本

推荐工具:用 VS Code + Python 插件,它会帮你检查循环的常见错误。

坑点提醒

range 的边界——range 是左闭右开的:

for i in range(5):  # 0, 1, 2, 3, 4(不包括 5)
    print(i)

for i in range(1, 5):  # 1, 2, 3, 4
    print(i)

for-else 的执行条件——循环没有 break 才执行 else:

for i in range(10):
    if i == 5:
        break
else:
    print("循环没有 break")  # 这不会执行(因为 break 了)

修改列表的陷阱——遍历时不能安全修改列表:

lst = [1, 2, 3, 4, 5]
# 正确做法:遍历副本或使用推导式
lst = [x for x in lst if x % 2 != 0]

zip 的长度——zip 以最短的为准:

a = [1, 2, 3]
b = ['a', 'b']
print(list(zip(a, b)))  # [(1, 'a'), (2, 'b')]

# 如果要以最长为准,用 itertools.zip_longest
from itertools import zip_longest
print(list(zip_longest(a, b, fillvalue=None)))

一句话总结

Python 的 for...else 是个"反直觉"的设计——循环没被 break 就执行 else。

下一篇咱们来聊聊列表和数组——C# 的 ArrayList 在 Python 里合体了,还有那个让 C# 程序员眼红的列表推导式。


📦 代码仓库

💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!

更多推荐