视频封面

视频元信息

  • 时长:未提供(约为CS50标准讲座长度)

TL;DR

本讲从C语言过渡到Python,展示Python的简洁语法与高级抽象:无需编译、无指针、内置数据结构、异常处理等。通过重写拼写检查器和图像滤镜,对比了开发效率与运行性能的取舍,并演示了第三方库的安装与使用。


一、课程导语:为什么先学C再学Python

[45.1s] 本讲是第6周,正式从C语言转向Python。学习C的意义在于建立对计算机底层运作的自底向上理解——内存、指针、编译过程等。掌握这些后,学习Python、Java、C++、Swift等语言时,能真正明白它们“在做什么”。

  • 理解:C语言提供了最接近硬件的抽象,而Python等高级语言隐藏了这些细节。先学C再学Python,能让你既会用工具,又懂工具的原理。
  • 目标:CS50希望你在课程结束后,拥有“尝过”几种语言的能力,能够自学新语言——识别相似范式,遇到语法差异时知道去Google或问AI。

二、第一个Python程序:hello.py

[260.4s] 在C中,编译需要 make,运行 ./a.out;而Python中,直接运行 python hello.py 即可。Python本身既是语言也是程序,它逐行解释你的代码。

print("hello, world")

[298s] 与C对比,明显差异:

  • 没有 #include、int main(void)、花括号
  • printf 变成了 print(更友好,但f-string仍保留格式化功能)
  • 不需要 \n —— print默认自动换行
  • 不需要分号

易错点:Python中语句结尾不需要分号,但如果你写了分号,语法上也是允许的(不推荐)。不要习惯性地加C的分号。

三、Python与C的语法对比

1. 函数定义与库

  • C:#include <cs50.h>,使用头文件提供函数原型
  • Python:使用 from cs50 import get_string 或 import cs50,更精确地导入所需功能
  • Python中“库”称为模块(module)或包(package),包是模块的集合

2. 输入输出

C版本:

string answer = get_string("What's your name? ");
printf("hello, %s\n", answer);

Python版本:

answer = get_string("What's your name? ")
print("hello, " + answer)
  • 加号 + 用于字符串拼接(类似Scratch中的join积木)
  • 无需占位符 %s,也可以用f-string更显式地插值
  • 变量无需声明类型,Python根据上下文自动推断

3. 更Pythonic的写法

answer = input("What's your name? ")
print("hello,", answer)   # print可接收多个参数,默认用空格分隔
  • input 是Python内置函数,替代 get_string(训练轮)
  • f-string:print(f"hello, {answer}"),花括号内变量会被插值(interpolate)

易错点:f-string必须在字符串前加 f,否则花括号内容不会替换,而是原样输出。例如 print("hello, {answer}") 会输出 hello, {answer}。

4. 命名参数

  • Python函数支持命名参数(named parameters),调用时可以不按顺序,通过名字指定参数值
  • 例:print("hello", end="") 覆盖默认换行行为(默认 end="\n")
  • 文档:docs.python.org 是官方权威文档,类似C的man pages

5. 引号灵活性

  • Python中单引号 '...' 和双引号 "..." 均可用于字符串,只需保持一致
  • C中单引号只能用于字符,双引号用于字符串

四、用Python重写Pset5拼写检查器

[382.4s] 演示:用约19行Python代码(含空行)实现Pset5的拼写检查器(dictionary.py),核心是使用set数据类型,自动处理哈希表、去重、内存管理。

words = set()

def check(word):
    return word.lower() in words

def load(dictionary):
    with open(dictionary) as file:
        words.update(file.read().split())
    return True

def size():
    return len(words)

def unload():
    return True
  • 理解:set 是Python内置数据结构,相当于自动实现的哈希表,无需自己管理冲突、扩容等
  • with open(...) as file 自动关闭文件,避免内存泄漏
  • return True 表示成功,无需手动释放内存(Python垃圾回收)

性能对比:

  • C实现:1.32秒
  • Python实现:1.87秒

考点:Python代码开发效率高,但运行速度通常慢于编译型语言(C)。原因是C编译为机器码直接执行,而Python解释器逐行读取执行。不过Python内部会编译为字节码(bytecode)以提高效率。

五、用Python实现图像滤镜(PIL)

[774.5s] 使用Python图像库(PIL)几行代码实现Pset4的模糊和边缘检测。

模糊(blur.py):

from PIL import Image, ImageFilter
before = Image.open("bridge.bmp")
after = before.filter(ImageFilter.BoxBlur(10))
after.save("out.bmp")

边缘检测(edges.py):

from PIL import Image, ImageFilter
before = Image.open("bridge.bmp")
after = before.filter(ImageFilter.FIND_EDGES)
after.save("out.bmp")
  • 理解:高级语言通过库将复杂算法封装成简单函数调用,大幅减少代码量
  • 思考:这也体现了“不要重复造轮子”的工程思想,但底层原理(如卷积、滤波)仍值得理解

六、变量与数据类型

1. 变量声明

C:int counter = 0; Python:counter = 0 (无类型、无分号)

2. 数据类型

  • Python有:bool, float, int, str(字符串)
  • 没有指针、没有char*,也没有单独的char类型
  • 没有指针意味着无法直接操作内存地址,避免了大量内存错误,但也失去了一些底层控制

3. 类型转换

x = int(input("x: "))
y = int(input("y: "))
print(x + y)
  • input 返回字符串,必须用 int() 转换(convert),而非C中的“强制转换(cast)”,因为字符串底层是多个字符,需要解析

易错点:如果输入非数字(如“cat”),int() 会抛出 ValueError 异常,程序崩溃。需用异常处理或验证。

七、条件语句

C:

if (x < y)
{
    printf("x is less than y\n");
}
else if (x > y)
{
    printf("x is greater than y\n");
}
else
{
    printf("x is equal to y\n");
}

Python:

if x < y:
    print("x is less than y")
elif x > y:
    print("x is greater than y")
else:
    print("x is equal to y")
  • 缩进是Python的语法要求,替代花括号
  • 条件后需要冒号 :
  • else if 缩写为 elif

易错点:Python强制缩进,不能随意把代码靠左对齐。缩进不一致会导致IndentationError。这是语言设计上强制代码可读性的体现。

字符串比较

  • Python中 == 直接比较字符串内容,而非像C中比较指针地址
  • Python的 == 内部自动遍历字符,等价于C的 strcmp 循环

八、字符串操作与方法

1. 大小写转换

  • s.lower() 返回小写版本
  • s.upper() 返回大写版本
  • s.capitalize() 首字母大写

  • 理解:这些是字符串对象自带的方法(method),即“对象内部的函数”。这是面向对象编程(OOP)的体现——数据及其相关操作封装在一起。

2. 判断输入是否同意

s = input("Do you agree? ").lower()
if s in ["y", "yes"]:
    print("Agreed")
else:
    print("Not agreed")
  • in 关键字检查元素是否在列表(list)中,比C中列举多个 || 条件更简洁
  • 链式调用:input(...).lower() 直接对返回值调用方法

九、循环

1. while循环

i = 0
while i < 3:
    print("meow")
    i += 1
  • 无 ++ 运算符,用 i += 1
  • 无限循环:while True:

2. for循环

for i in range(3):
    print("meow")
  • range(n) 生成 0 到 n-1 的序列,且惰性求值,不一次性占用大量内存
  • 也可直接遍历列表:for name in names:
  • 如果循环变量未使用,可用 _ 作为变量名(Pythonic约定)

3. 无do-while循环

Python没有do-while,但可用 while True + break 模拟:

while True:
    n = int(input("Height: "))
    if n > 0:
        break
for i in range(n):
    print("#")
  • 理解:break 跳出循环,实现“至少执行一次”的效果

十、定义函数与main

1. 定义函数

def meow(n):
    for _ in range(n):
        print("meow")
  • 用 def 关键字,无需指定参数类型和返回类型
  • 函数定义必须在使用之前(解释器逐行执行)

2. main函数约定

def main():
    meow(3)

def meow(n):
    for _ in range(n):
        print("meow")

main()
  • Python没有自动调用main的机制,必须显式调用
  • 更规范的做法:
if __name__ == "__main__":
    main()
  • 理解:这个条件判断允许文件被导入为模块时不执行主逻辑,而直接运行时才执行。这使代码更模块化。

易错点:忘记调用main(),程序运行后不会有任何输出。这是初学者常犯错误。

3. 函数间空行

  • Python风格指南(PEP8)建议函数之间用两个空行分隔

十一、数值问题

1. 截断(Truncation)

  • Python中 7 / 3 返回 2.333...,自动进行浮点除法,不会像C中整数除法截断为2
  • 旧式整数除法用 //:7 // 3 得到 2

2. 浮点精度

  • Python仍存在浮点精度问题:1 / 3 显示 0.3333333333333333,不是无限精确
  • 原因:计算机用有限二进制位表示浮点数,无法精确表示某些十进制小数
  • 可通过第三方库(如decimal)获得更高精度

3. 整数溢出

  • Python的整数可以无限增长,自动分配更多位,不存在C中int溢出回绕的问题
  • 这是Python相比C的一个重大优势,无需担心溢出边界

十二、异常处理

try:
    n = int(input("x: "))
except ValueError:
    print("Not an integer")
  • 异常(exception)允许“带外”传递错误,而非依赖返回值
  • 常见异常类型:ValueError, NameError, ModuleNotFoundError 等
  • 可自定义异常,也可捕获多种异常
  • CS50库中的get_int内部就是用try/except实现重新提示

易错点:不要盲目捕获所有异常(except:),应捕获具体异常类型,否则可能隐藏严重错误。

十三、数据结构

1. 列表(list)

  • 类似C的数组,但动态增长,内部是链表或动态数组
  • 支持 len() 获取长度,append() 添加元素,sum() 求和
scores = []
scores.append(72)
scores.append(73)
scores.append(33)
print(f"Average: {sum(scores) / len(scores)}")

2. 元组(tuple)

  • 不可变列表,用于固定集合(如坐标)

3. 字典(dict)

  • 键值对集合,相当于哈希表
  • 用字符串索引,如 people["David"]
people = {
    "Carter": "+1-617-495-1000",
    "David": "+1-617-495-1000"
}
name = input("Name: ")
if name in people:
    print(f"Number: {people[name]}")
  • 理解:字典的 in 操作检查的是键是否存在,时间复杂度平均O(1)

4. 集合(set)

  • 无重复元素的无序集合,用于成员检查

5. 列表与字典组合

  • 列表的元素可以是字典,模拟表格数据(如电子表格中的行)
people = [
    {"name": "Carter", "number": "+1-617-495-1000"},
    {"name": "David", "number": "+1-617-495-1000"},
    {"name": "John", "number": "+1-949-468-2750"}
]
  • 遍历时,person 是每个字典,用 person["name"] 访问字段
  • 思考:这正是数据库表中行(记录)的抽象,后续学数据库时会用到

十四、文件I/O与CSV

1. 基础文件写入

file = open("phonebook.csv", "a")
name = input("Name: ")
number = input("Number: ")
writer = csv.writer(file)
writer.writerow([name, number])
file.close()
  • 用 open 替代C的 fopen,模式 "a" 表示追加
  • csv.writer 处理CSV格式,避免手动拼接逗号

2. with 语句

with open("phonebook.csv", "a") as file:
    writer = csv.writer(file)
    writer.writerow([name, number])
  • with 自动关闭文件,即使发生异常也会关闭,避免内存泄漏

3. DictWriter

with open("phonebook.csv", "a") as file:
    writer = csv.DictWriter(file, fieldnames=["name", "number"])
    writer.writerow({"name": name, "number": number})
  • 使用字典写入,不依赖列顺序,更健壮

十五、命令行参数与退出状态

1. sys.argv

from sys import argv

if len(argv) == 2:
    print(f"hello, {argv[1]}")
else:
    print("hello, world")
  • argv 是包含命令行参数的列表,argv[0] 是程序名,argv[1] 是第一个参数
  • 注意:argv 不包含 python 解释器本身,只包含脚本名及其后参数

2. sys.exit

import sys

if len(sys.argv) != 2:
    print("Missing command-line argument")
    sys.exit(1)
print(f"hello, {sys.argv[1]}")
sys.exit(0)
  • sys.exit(1) 表示错误退出,sys.exit(0) 表示成功,便于自动化测试

十六、第三方库安装与使用

1. pip

  • pip install cowsay 安装第三方库
  • 示例:cowsay.cow("This is CS50") 在终端显示奶牛说话

2. 语音合成(pyttsx3)

import pyttsx3
engine = pyttsx3.init()
engine.say("This is CS50")
engine.runAndWait()
  • 需在本地电脑运行,因为云端环境无法播放音频

3. 生成二维码(qrcode)

import qrcode
image = qrcode.make("https://youtu.be/xvFZjo5PgG0")
image.save("qr.png")
  • 自建QR码生成器,无需在线服务

十七、总结

  • Python用更少的代码实现相同功能,开发效率高
  • 性能略低于C,但通常足够;Python社区通过字节码、JIT等技术优化
  • 数据结构、异常处理、库生态让Python成为“瑞士军刀”
  • 后续课程会继续探索更多语言和问题

关键标签:Python CS50 编程语言对比 解释型语言 数据结构 异常处理 文件I/O 第三方库 f-string 列表 字典


生成时间:2026-09-02 16:32:29 生成平台:VideoNote