简介:零基础入门Python的完整学习资料包,涵盖课程课件与配套源代码,适合自学入门及教师备课使用。内容从用Python设计第一个游戏讲起,逐步深入数据类型、分支循环、函数与递归,再扩展到文件操作、永久存储、类和对象、魔法方法、模块与包,循序渐进地建立编程思维。后续还包含网络爬虫实战、Scrapy框架安装与使用,以及Tkinter GUI编程和Pygame游戏开发,直至完成飞机大战系列项目,覆盖基础语法到项目实战的完整路径。压缩包整体约133.22MB,内部以课件文档和.py源代码文件为主,每个章节均配有对应课件与可直接运行的示例代码,方便对照学习与动手实践。目前已有1287人学习下载,适合希望从零系统掌握Python语法、面向对象编程及常用库应用的学习者,通过边看边练快速提升编程能力。
1. 零基础入门学习Python:一套课件源码覆盖的完整路径
给刚转 Python 的同事整理入门资料时,我绕了不少弯路。市面上的教程要么只讲语法概念,要么直接扔一个完整项目,中间缺了“每学一个知识点就有一小段能跑的代码”的过渡。这套按课时编号组织的 Python 源码课件,从 002 课的猜数字游戏一路走到 096 课的飞机大战,每一讲都配了可直接运行的源码和对应课件,最大的价值是学习路径完整:语法、函数、递归、面向对象、魔法方法、文件、模块、爬虫、GUI、游戏,一个不落。
资源适合两类人:零基础自学者,以及需要给学员准备练习代码的讲师。拿到压缩包后,建议先别急着整包导入 IDE,按编号从 002 开始逐个跑通,同时确认 python 安装 没问题、VSCode 里选中的解释器是当前版本。课件里标注的重点和源码文件名建议保留,后面很多课会复用前面的模块。
2. 从猜数字游戏看分支循环与数据类型:零基础的第一行代码
2.1 input() 返回的是字符串,python 类型转换在这埋了第一个坑
002 课“用 Python 设计第一个游戏”的核心是把用户输入转成整数再比较。常见写法:
temp = input("猜一下我现在心里想的是哪个数字:") guess = int(temp) if guess == 8: print("猜中了,但没有奖励") else: print(f"没猜中,正确答案是 8")最关键的一行是int(temp)。input() 在 Python3 里永远返回字符串,如果你直接拿 temp 和整数 8 比较,条件永远为 False,游戏会变成“怎么猜都错”。很多零基础学员在这儿卡住,根因不是 if 语法,而是没理解 input 返回 str、比较前必须做 python 类型转换。
转换函数的行为也要说清楚:int("8")可以,int("8.8")会抛 ValueError,想解析小数得先float()再int()。另外bool("")是 False,bool(" ")是 True,这是空串和非空字符串的差异,后期做输入校验时会频繁遇到。这套课件里 005 课专门讲数据类型,对照 int、float、str、bool 四个内置类型和它们的转换函数过一遍,基础语法这块就算立住了。
2.2 分支循环:改进版猜数字怎么控制游戏流程
004 课“改进我们的小游戏”把固定答案改成随机数,同时引入 while 循环,这是第一个完整的交互式程序。我给学员演示时会再补一层异常处理:
import random secret = random.randint(1, 10) guess = 0 while guess != secret: try: guess = int(input("请输入你猜的数字(1-10):")) except ValueError: print("输入的不是整数,请重新来") continue if guess > secret: print("大了") elif guess < secret: print("小了") else: print("恭喜,猜中了")random.randint(1, 10)生成的随机数包含 1 和 10 两个端点,这和range(1, 10)左闭右开的习惯不同,初学者很容易混。while 条件是退出循环的唯一开关,guess 等于 secret 时条件为 False,循环自然结束。continue 的作用是跳过本次循环剩余代码,直接进入下一轮,它在这里保证非法输入不会走到后面的 if 判断。
try/except 是原课程之外我建议补的防御性写法。没有它,用户输入一个字母,int() 抛异常,程序直接崩掉。学分支循环的时候顺手把异常处理的基本形态看懂,后面调试所有课件的源码都会轻松很多。
2.3 分支循环语法对照表
007 到 009 课三讲分支和循环,重点其实只有一个:用缩进表达代码块。Python 没有 begin/end 也没有花括号,同一个 if 下的语句必须保持相同缩进。几个易混点整理如下:
| 语句 | 作用 | 常见误用 |
|---|---|---|
| if / elif / else | 多分支选择 | elif 写成 else if |
| while | 条件循环 | 循环体内忘记改变条件,死循环 |
| for ... in range(n) | 固定次数循环 | 以为 range(1, n) 包含 n |
| break | 立即跳出本层循环 | 多层循环里只跳出最内层 |
| continue | 跳过本次循环剩余代码 | 在异常处理尾部误用,吞掉错误 |
建议练习方式:把猜数字改造成“最多猜 5 次,超了提示失败”。用for _ in range(5)写一版,再用count += 1配合 while 写一版,对比控制变量的差异。for 版本少一个计数变量,可读性更好,这也是后面学函数时“减少状态变量”习惯的起点。
3. 函数与递归:从斐波那契到汉诺塔的代码推演
3.1 函数是积木:默认参数与作用域边界
017 课把函数比作乐高积木,核心是封装和复用。先看一个带默认参数的函数:
def power(x, n=2): """计算 x 的 n 次方,n 不传时默认平方""" s = 1 while n > 0: n -= 1 s *= x return s print(power(5)) # 25 print(power(5, 3)) # 125默认参数 n=2 让同一个函数能适应两种调用方式,这是 Python 函数很实用的特性。注意 while 循环内部修改的是局部变量 n,不会影响外部任何同名变量,因为函数内的赋值操作默认只在局部作用域生效。如果想在函数里修改全局变量,必须显式声明global x,019 课“我的地盘听我的”专门讲这个问题,初学者最常见的错误就是不声明 global 直接赋值,结果函数内外变量各改各的。
学到函数这里,建议做一件事:把 len、range、enumerate、zip、map、filter 这些内置函数过一遍,不用背参数,但要记住它们存在。后面写爬虫和 GUI 时这些函数会频繁出现,到用的时候再查文档就慢了。
3.2 递归的两个经典实现:斐波那契与汉诺塔
022 到 024 课连续三讲递归。斐波那契数列是标准入门案例:
def fib(n): if n < 2: return n return fib(n - 1) + fib(n - 2)这个写法正确,但性能极差,fib(n) 会重复计算大量子问题,跑到 fib(35) 就能感觉到明显卡顿。递归成立有两个前提:一是有明确的终止条件(这里 n < 2),二是每层递归都在向终止条件逼近(n 递减)。缺任何一个都会无限递归直到 RecursionError。
汉诺塔是理解“递归不一定需要显式循环”的最佳案例,代码短但信息密度高:
def hanoi(n, a, b, c): if n == 1: print(a, "->", c) else: hanoi(n - 1, a, c, b) print(a, "->", c) hanoi(n - 1, b, a, c) hanoi(3, "A", "B", "C")参数 a 是源柱,b 是辅助柱,c 是目标柱。n=1 时直接移动;n>1 时先把上面 n-1 个盘子从 a 借助 c 移到 b,再把最底下的盘子从 a 移到 c,最后把 b 上的 n-1 个盘子借助 a 移到 c。整个移动过程是自相似的结构,递归代码和问题本身的数学结构完全对应,这也是递归最优雅的地方。
3.3 递归的性能边界与优化方向
| 方案 | 代码量 | 时间复杂度 | 适用场景 |
|---|---|---|---|
| 朴素递归 | 最短 | 指数级 | 只用来理解递归概念 |
| 带记忆化递归 | 中等 | O(n) | 想保留递归结构,n 较大 |
| 迭代 | 短 | O(n) | 生产环境首选 |
带记忆化的写法只需一个装饰器:
from functools import lru_cache @lru_cache(maxsize=None) def fib_memo(n): if n < 2: return n return fib_memo(n - 1) + fib_memo(n - 2)lru_cache 是 functools 自带的缓存装饰器,递归函数的同参数结果会被缓存,第一次计算后不再重复展开整棵调用树。maxsize=None 表示不限制缓存大小,对于 n 只有几百的斐波那契足够用。递归课学完,建议顺手看 049 课的生成器,把斐波那契改成 yield 版本,那其实是 python 协程 的前置知识点,asyncio 里的很多习惯用法都能从生成器里找到影子。
4. 面向对象与魔法方法:继承、描述符与迭代器
4.1 类与继承:从“对象”到“面向对象编程”
036 到 038 课把类和对象讲透。基础部分其实不复杂:
class Fish: def __init__(self, name): self.name = name def swim(self): print(f"{self.name} 在游泳") class Trout(Fish): def __init__(self, name): super().__init__(name) t = Trout("虹鳟") t.swim()这就是继承的全部核心:子类 Trout 没有定义 swim 方法,但通过继承链拿到父类方法;super().init(name) 显式调用父类构造函数,确保 name 属性被正确初始化。很多人会漏掉 super() 那一行,结果访问 self.name 时抛出 AttributeError,报错位置还在父类里,排查起来很费劲。
039 课拾遗部分讲了 issubclass、isinstance、hasattr 这些内建函数。一个容易混淆的点:isinstance(t, Fish) 是 True,因为 Trout 是 Fish 的子类;但 type(t) == Fish 是 False,因为 type 做的是精确类型判断。日常代码优先用 isinstance,它对继承关系更宽容。
4.2 魔法方法与属性访问拦截
045 课讲魔法方法,重点是getattr和setattr:
class Foo: def __getattr__(self, attr): return f"属性 {attr} 不存在" def __setattr__(self, name, value): print(f"设置 {name} = {value}") object.__setattr__(self, name, value)getattr只在正常属性查找失败后才被调用,适合做动态属性兜底;而getattribute是所有属性访问都会触发,写不好会把正常访问也拦掉,不建议初学者碰。setattr内部如果写self.name = value会再次触发setattr,形成无限递归,必须用 object.setattr绕过,这是高频踩坑点。三个方法的触发时机:
| 魔法方法 | 触发时机 | 常见用法 |
|---|---|---|
| getattr | 正常属性查找失败后 | 动态属性兜底 |
| getattribute | 每次属性访问都触发 | 访问日志,需谨慎 |
| setattr | 每次属性赋值都触发 | 属性校验,防递归 |
4.3 描述符协议与 property 的原理
046 课是整套资料里含金量最高的一讲。property 之所以能用,底层就是描述符协议:
class Celsius: def __init__(self, value=26.0): self._temperature = value def __get__(self, instance, owner): return self._temperature def __set__(self, instance, value): if value < -273.15: raise ValueError("温度不能低于绝对零度") self._temperature = value class Temperature: celsius = Celsius() t = Temperature() t.celsius = 30.0 print(t.celsius)描述符的三个方法分别对应读取、赋值、删除。get的 instance 参数是访问该描述符的实例对象,owner 是宿主类;set的 value 是被赋的值。当类属性是描述符对象时,实例的读取和赋值都会路由到描述符方法上,property 的 getter/setter 只是这个机制的语法糖。这一套机制是 Web 框架里很多字段校验功能的地基,读懂之后再去看框架源码,遇到__get__就不会懵。
4.4 定制序列、迭代器与生成器
047 课定制序列只要实现len和getitem,对象就能支持 len() 和下标访问;再补上setitem和delitem,就支持切片和删除。迭代器协议是iter返回自身、next产出下一个元素:
class Fibs: def __init__(self, limit): self.a, self.b = 0, 1 self.limit = limit def __iter__(self): return self def __next__(self): if self.a > self.limit: raise StopIteration self.a, self.b = self.b, self.a + self.b return self.a for n in Fibs(100): print(n, end=" ")注意next用 StopIteration 信号通知 for 循环结束,这是迭代器协议的一部分,忘记抛异常会导致死循环。049 课的生成器用 yield 把同样的逻辑压缩成几行,但它和迭代器在内存占用上有本质差别:迭代器要自己维护状态,生成器由解释器在 yield 处保存执行现场,后者写管道式数据处理时更省内存。yield 关键字在手,再往 async 方向走就有基础了。
5. 文件持久化与模块机制:pickle、name与爬虫扩展
5.1 文件读写:mode 参数和编码最容易出错
028 课进入文件操作,open() 的 mode 参数决定行为:
| mode | 含义 | 文件不存在时 |
|---|---|---|
| r | 只读,默认值 | 抛 FileNotFoundError |
| w | 写入,覆盖原内容 | 自动创建 |
| a | 追加写入 | 自动创建 |
| rb / wb | 二进制读写 | 同对应文本模式 |
| r+ | 读写,不截断 | 抛错 |
| w+ | 读写,会截断 | 自动创建 |
推荐写法配合 with 使用:
with open("notes.txt", "w", encoding="utf-8") as f: f.write("Python 零基础学习笔记")with 语句在 034 课展开讲过,本质是上下文管理器协议:进入时调用enter,退出时调用exit,即使中途抛异常也会保证文件关闭。手动调用 f.close() 的写法在出异常时会跳过关闭步骤,文件句柄泄漏在长时间运行的脚本里是真实会发生的问题。encoding="utf-8" 在 Windows 上尤其重要,不指定会用系统默认编码(GBK),和 UTF-8 源码文件互相读写时会出现乱码。
5.2 pickle:把对象“腌制”起来
031 课的标题叫“腌制一缸美味的泡菜”,用 pickle 实现对象序列化:
import pickle data = {"course": "零基础入门学习Python", "source": "课件+源码"} with open("course.pkl", "wb") as f: pickle.dump(data, f) with open("course.pkl", "rb") as f: loaded = pickle.load(f) print(loaded["course"])pickle.dump 把 Python 对象转成二进制字节流写入文件,pickle.load 反向还原。有个使用边界要清楚:pickle 加载不可信数据存在安全风险,反序列化过程中可能执行恶意构造的代码,网上下载的 pkl 文件不要直接 load。另外 pickle 在 Python2 和 Python3 之间不互通,跨版本传数据建议用 JSON。
5.3name的作用与模块搜索路径
051 课讲了模块机制里最重要的约定。每个模块文件被解释器执行时,Python 都会内置一个name变量,直接运行该脚本时值为 "main",被 import 导入时值为模块名:
print(f"__name__ 的值是:{__name__}") if __name__ == "__main__": print("作为脚本直接运行") else: print("被导入为模块")这个 if 判断的实际作用:把测试代码和演示代码包在里面,防止别人 import 你的模块时意外执行这些代码。常见错误是初学者在模块底部直接写 print 调试,别人导入时控制台一片噪声。更规范的做法是把可执行逻辑封装成函数,在 ifname== "main" 里只做函数调用。
模块搜索路径用 sys.path 查看,它是一个列表,包含当前目录、标准库路径和 site-packages。sys.path.append("/your/path") 可以临时添加搜索目录,适合开发期还没把模块安装成包的阶段;生产环境应该用 pyproject.toml 或 setup.py 做正式安装。
5.4 从 urllib 到 Scrapy:python 爬虫的实战路径
054 课从理论进入实战,055 课讲隐藏身份,056 课处理反爬应对,062 到 063 课开始切 Scrapy。最早的 urllib 写法值得留个底子:
import urllib.request url = "https://example.com" req = urllib.request.Request( url, headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" }, ) with urllib.request.urlopen(req, timeout=10) as resp: html = resp.read().decode("utf-8")urllib.request.Request 第一个参数是目标 URL,headers 用于携带 User-Agent 伪装浏览器标识,timeout=10 防止请求长时间挂起。拿到 resp 后先 read() 再 decode,网络传输的是字节流,解码字符集要和页面声明的 charset 一致,否则就是乱码。抓取数据前先看目标站点的 robots.txt,控制请求频率,这是 python 爬虫开发的基本工程习惯。
到了 Scrapy 阶段,先安装再建项目:
pip install scrapy scrapy startproject tutorial cd tutorial scrapy genspider example example.comscrapy startproject 生成项目目录骨架,genspider 创建继承 Spider 的爬虫模板。Scrapy 和 urllib 的差别核心是:请求调度、去重、并发、解析全由框架接管,你只需要在 items.py 定义字段、在 spider 里写 parse 方法、在 pipelines.py 里做入库。入门阶段建议先用 urllib 手写一个完整流程再切 Scrapy,这样才能分清哪些是框架替你做的,哪些是网络请求本身必须要处理的。
6. 事件驱动实战:Tkinter 界面与 Pygame 碰撞检测
6.1 从 mainloop 到事件循环
064 到 077 课全部围绕 Tkinter 展开,核心就一个概念:事件驱动。窗口程序启动后调用 mainloop(),程序进入等待事件的循环,按钮点击、键盘输入都被转成事件分派给对应回调。Pygame 把这个循环暴露得更直接:
while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 更新状态 # 绘制画面 pygame.display.flip() clock.tick(60)三件事顺序不要颠倒:先处理事件,再更新游戏状态,最后绘制。pygame.event.get() 取出当前帧所有事件,每帧只取一次,连续调用两次第二次可能拿到空列表。clock.tick(60) 把帧率限制在 60 FPS,同时防止 CPU 空转。
6.2 碰撞检测的可视化调试技巧
086 课讲碰撞检测,spritecollide 适合精灵组,但手动调试时我更喜欢用矩形对象:
player_rect = pygame.Rect(200, 500, 50, 50) enemy_rect = pygame.Rect(210, 490, 50, 50) if player_rect.colliderect(enemy_rect): print(f"碰撞坐标:{enemy_rect.topleft}") # 把边界画出来,肉眼确认判定区域 pygame.draw.rect(screen, (255, 0, 0), player_rect, 2) pygame.draw.rect(screen, (0, 255, 0), enemy_rect, 2)pygame.Rect 的 colliderect 判断两个矩形是否相交,不是像素级碰撞,而是矩形区域交集判断。绘制时传入 width=2 只画边框不填充,红绿边框让判定区域在画面上直接可见。很多“没撞上却判定碰撞”的 Bug,其实是图片比 Rect 大一圈,视觉机身超出边界框导致误判,把边框画出来一眼就能定位问题。
进阶验证做法:把敌机速度调大,制造高速穿越场景,观察碰撞是否漏判。Rect 只做静态相交判断,高速移动下两帧之间可能直接跳过相交区域,产生“穿透”。要解决穿透,用射线检测或把两帧之间的移动路径拆成子步长逐次判断,这是飞机大战后期值得自己动手的扩展点,也是这套 Pygame 课程收尾后最适合用来检验学习成果的练习题。
本文还有配套的精品资源,点击获取