30天,从零到能干活
这是一份为零基础新手设计的Python 30天实战路线图,每天1.5小时,从安装环境到编写命令行计算器、待办管理器,再到NumPy数据处理,手把手带你掌握Python核心语法与真实项目技能。无需数学功底与编程背景,跟着敲代码、完成练习,30天后你就能独立写出能用的程序,为AI、数据分析、Web开发打下坚实基础。
说句大实话:Python绝对是2026年最值得学的一门编程语言,没有之一。不管你以后想搞AI、做数据分析、写自动化脚本,还是单纯想多一门技能傍身,Python都是上手最快、用处最广的选择。而且你放心,真不需要什么数学天才或者计算机背景,会打字就能学。这份30天计划,每天花一个半小时到两个小时,跟着走就行,别想太多,干就完了。
第一阶段:先把摊子支起来(第1~7天)
这七天任务很简单:装好Python,搞明白最基础的东西。你不需要写出什么惊天动地的程序,先把"看得懂代码"这个小目标搞定。
第1天:安装Python和VS Code
今天要做什么: 在电脑上装好Python和编辑器VS Code,跑通你的第一行代码。
具体操作
打开浏览器,搜索"Python官网",进到 python.org,点 Downloads,它会自动识别你的系统,下载最新版本(2026年应该是3.13或更高)。下载完双击安装,最重要的一步:安装界面底部有个"Add Python to PATH"的选项,一定要勾上,不然之后命令行里会找不到Python。然后一路点下一步就行。
搜索"VS Code官网",下载安装。装好之后打开,左边侧边栏有个方块拼图一样的图标,点进去搜"Python",找到微软官方出的那个Python扩展,点安装。再搜"Chinese",装个中文语言包,界面就变成中文了。
在电脑上随便找个地方新建一个文件夹,比如叫 python-learning。打开VS Code,点"文件→打开文件夹",选你刚建的那个文件夹。然后在左侧文件列表里右键,新建一个文件,叫 day01.py。注意后缀名是 .py,不是 .txt。
在 day01.py 里敲下面这行代码:
print("我正式开始学Python了!")
按快捷键 Ctrl + F5(Mac上是 Cmd + F5),或者右键选"在终端中运行Python文件"。如果看到屏幕下方弹出一个窗口,里面显示"我正式开始学Python了!",恭喜你,环境搞定了!
容易踩的坑
如果提示"python不是内部或外部命令",说明安装时没勾那个PATH选项,重新安装一遍,或者搜索"python添加到环境变量"照着教程改一下。
文件名别用中文,也别带空格,养成用英文命名的习惯。
今天的练习
改一下 print 括号里的文字,输出你自己的名字、今天的日期、一句你想说的话,多试几次,确保每次都能跑成功。
第2天:变量和数据类型
今天要做什么: 认识Python里几种最常见的数据类型,学会用变量存东西,学会用 type() 和 print() 查看到底是什么类型。
敲这些代码试试
# 数字类型
age = 25
height = 1.75
print(age)
print(height)
print(type(age)) # 输出:<class 'int'> 整数
print(type(height)) # 输出:<class 'float'> 小数
# 字符串类型
name = "小明"
message = '你好,Python!'
print(name)
print(message)
print(type(name)) # 输出:<class 'str'> 字符串
# 布尔类型
is_student = True
has_job = False
print(is_student)
print(type(is_student)) # 输出:<class 'bool'> 布尔值
# 字符串可以拼接
greeting = "你好," + name
print(greeting) # 输出:你好,小明
# f-string:字符串和变量拼在一起输出
print(f"我叫{name},今年{age}岁") # 输出:我叫小明,今年25岁
重点理解
变量就像一个贴了标签的盒子,age = 25 就是把25这个数字放进贴了"age"标签的盒子里,以后喊"age"就能拿到25。
字符串可以用单引号也可以用双引号,但别混着用。
f"我叫{name}" 叫f-string,特别好用,记住这个写法,以后会经常用。
今天的练习
创建三个变量,分别存你的名字、年龄、所在城市,然后用 print 和f-string输出一句话,比如"我叫张三,今年28岁,住在北京"。
第3天:列表、字典、元组、集合
今天要做什么: 认识四种"容器",重点学会用列表和字典,另外两个先混个眼熟。
敲这些代码试试
# ===== 列表:可以随便增删改 =====
fruits = ["苹果", "香蕉", "橘子"]
print(fruits[0]) # 输出:苹果 (索引从0开始,不是从1开始)
print(fruits[1]) # 输出:香蕉
print(fruits[–1]) # 输出:橘子 (-1表示最后一个)
fruits.append("葡萄") # 在末尾加一个
print(fruits) # 输出:['苹果', '香蕉', '橘子', '葡萄']
fruits.remove("香蕉") # 删掉指定的
print(fruits) # 输出:['苹果', '橘子', '葡萄']
print(len(fruits)) # 输出:3 (列表里现在有几个东西)
# 切片:取列表的一部分
print(fruits[0:2]) # 输出:['苹果', '橘子'] (取索引0到1,不包含2)
# ===== 字典:靠"名字"找东西 =====
student = {
"name": "小明",
"age": 20,
"city": "上海"
}
print(student["name"]) # 输出:小明
print(student["age"]) # 输出:20
student["score"] = 95 # 加一个键值对
print(student) # 输出:{'name': '小明', 'age': 20, 'city': '上海', 'score': 95}
# ===== 元组:跟列表差不多,但不能改 =====
colors = ("红", "黄", "蓝")
print(colors[0]) # 输出:红
# colors[0] = "绿" # 这行会报错!元组不能修改
# ===== 集合:自动去重 =====
numbers = {1, 2, 2, 3, 3, 3}
print(numbers) # 输出:{1, 2, 3} 自动把重复的去掉了
重点理解
列表用方括号 [],字典用花括号 {} 里面是键值对,元组用圆括号 (),集合用花括号 {} 但里面只有值没有键。
列表的索引从0开始,这个一定要记住,几乎所有编程语言都是这样的。
字典是以后最常用的数据类型之一,靠"键"找"值"这个思路要理解透。
今天的练习
创建一个列表,存5个你喜欢的电影名字,然后分别打印出第一个和最后一个。
创建一个字典,存你自己的信息(姓名、年龄、爱好、职业),然后打印出其中两项。
试着把列表里的某个元素改掉,看看能不能成功。
第4天:条件判断if语句
今天要做什么: 让程序能根据不同的条件做不同的事,再学一个 input() 函数让用户能输入东西。
敲这些代码试试
# ===== 基础if-else =====
score = 85
if score >= 90:
print("优秀")
elif score >= 80:
print("良好")
elif score >= 60:
print("及格")
else:
print("不及格")
# 输出:良好
# ===== 结合input()做交互 =====
age = input("请输入你的年龄:")
age = int(age) # input拿到的默认是字符串,要转成数字
if age >= 18:
print("你已经是成年人了")
else:
print("你还是未成年人")
# ===== 逻辑运算符 and / or / not =====
temperature = 28
is_raining = False
if temperature > 25 and not is_raining:
print("天气不错,适合出去玩")
elif temperature > 25 and is_raining:
print("又热又下雨,还是在家吧")
else:
print("温度还可以")
重点理解
if 后面跟条件,条件后面一定要有冒号 :,下一行要缩进(按Tab键或者四个空格)。
elif 是"否则如果"的意思,可以写多个。
else 是"以上条件都不满足的时候"执行的。
input() 拿到的永远是字符串,如果要当数字用,必须用 int() 或 float() 转一下。
判断等于用 ==,赋值用 =,这个千万别搞混!
今天的练习
写一个程序,让用户输入他的年龄,然后判断他是儿童(0-12)、青少年(13-17)、成年人(18-59)还是老年人(60+)。
写一个程序,让用户输入一个数字,判断这个数字是正数、负数还是零。
写一个简单的登录验证:预设一个用户名和密码,让用户输入,判断是否正确。
第5天:循环for和while
今天要做什么: 让程序自动重复做一件事,不用你手动写一百遍。
敲这些代码试试
# ===== for循环:知道要循环多少次的时候用 =====
# 打印1到5
for i in range(1, 6): # range(1, 6) 生成1,2,3,4,5 (不包含6)
print(i)
# 遍历列表
names = ["张三", "李四", "王五"]
for name in names:
print(f"你好,{name}")
# 用range做累加:计算1到100的和
total = 0
for i in range(1, 101):
total = total + i
print(f"1到100的和是:{total}") # 输出:5050
# ===== while循环:不知道要循环多少次的时候用 =====
user_input = ""
while user_input != "quit":
user_input = input("请输入内容(输入quit退出):")
print(f"你输入了:{user_input}")
print("循环结束")
# ===== break:直接跳出循环 =====
for i in range(1, 10):
if i == 5:
break # 遇到5就停,后面的不打印了
print(i)
# 输出:1 2 3 4
# ===== continue:跳过本次循环,继续下一次 =====
for i in range(1, 6):
if i == 3:
continue # 跳过3,不打印
print(i)
# 输出:1 2 4 5
重点理解
for 循环一般搭配 range() 或者列表来用,你知道要循环多少次的时候用 for。
while 循环是"只要条件满足就一直跑",用在不知道具体循环次数、靠某个条件来终止的场景。
break 是直接跳出循环不干了,continue 是跳过当前这一轮、继续下一轮。
小心死循环!写 while 的时候一定要确保循环条件最终会变成 False,不然程序会一直跑停不下来。
今天的练习
用 for 循环打印九九乘法表。
用 for 循环找出1到100之间所有的偶数。
写一个猜数字游戏:程序预设一个数字(比如42),让用户不断猜,每次告诉用户猜大了还是猜小了,直到猜对为止。
第6天:函数
今天要做什么: 把常用的代码打包成函数,以后想用就直接调用,不用重写。
敲这些代码试试
# ===== 定义一个最简单的函数 =====
def say_hello():
print("你好!")
say_hello() # 调用函数,输出:你好!
say_hello() # 可以调用多次
# ===== 带参数的函数 =====
def greet(name):
print(f"你好,{name}!")
greet("小明") # 输出:你好,小明!
greet("小红") # 输出:你好,小红!
# ===== 带返回值的函数 =====
def add(a, b):
result = a + b
return result # 把结果还回去
sum_value = add(3, 5)
print(sum_value) # 输出:8
# ===== 默认参数 =====
def introduce(name, city="北京"):
print(f"我叫{name},来自{city}")
introduce("张三") # 输出:我叫张三,来自北京
introduce("李四", "上海") # 输出:我叫李四,来自上海
# ===== return之后函数就结束了 =====
def check_age(age):
if age < 0:
return "年龄不能为负数" # 这里直接返回,后面的代码不执行了
if age >= 18:
return "成年人"
else:
return "未成年人"
print(check_age(20)) # 输出:成年人
print(check_age(–5)) # 输出:年龄不能为负数
重点理解
def 是定义函数的关键字,函数名后面一定要有括号和冒号,函数体要缩进。
参数就是函数接收的输入,return 是函数的输出。
有默认值的参数叫"默认参数",调用的时候可以不传,不传就用默认值。
return 执行之后函数就结束了,后面的代码不会再跑。
今天的练习
写一个函数,接收一个数字,返回这个数字是奇数还是偶数。
写一个函数,接收三个数字,返回其中最大的那个。
写一个函数,接收一个列表,返回列表里所有数字的平均值。
第7天:阶段小测——命令行计算器
今天要做什么: 把前6天学的东西全部用上,写一个能用的命令行计算器。这是你第一个真正意义上的"完整程序"。
功能要求
用户依次输入第一个数字、运算符(+、-、*、/)、第二个数字。
程序输出计算结果。
如果用户输入的不是数字,给出友好提示而不是直接报错。
如果运算符是除法且第二个数字是0,提示"除数不能为零"。
参考代码(先自己写,实在写不出来再看)
print("===== 简易计算器 =====")
print("支持的运算:+、-、*、/")
try:
num1 = float(input("请输入第一个数字:"))
operator = input("请输入运算符(+ – * /):")
num2 = float(input("请输入第二个数字:"))
if operator == "+":
result = num1 + num2
elif operator == "-":
result = num1 – num2
elif operator == "*":
result = num1 * num2
elif operator == "/":
if num2 == 0:
result = "错误:除数不能为零"
else:
result = num1 / num2
else:
result = "错误:不支持的运算符,请使用 + – * /"
print(f"计算结果:{result}")
except ValueError:
print("错误:请输入有效的数字")
检验标准
输入 5 + 3,输出 8.0。
输入 10 / 0,输出"除数不能为零"的提示。
输入 abc,不会直接崩溃报红字,而是输出友好的提示。
如果这些都做到了,第一阶段完美通关!你已经可以写一个能用的程序了,后面学的所有东西都是在这个基础上添砖加瓦。
第二阶段:开始写点像样的代码(第8~14天)
第一阶段你学会了写零散的代码,这个阶段咱们要学着写更规范、更有组织的代码,向"正经程序员"靠拢。
第8天:面向对象入门——类和对象
今天要做什么: 理解面向对象最核心的两个概念:类和对象。说白了就是把"东西"和"它能干的事"打包在一起。
敲这些代码试试
# ===== 定义一个最简单的类 =====
class Dog:
def __init__(self, name, age): # __init__ 是初始化方法,创建对象时自动调用
self.name = name # self.name 是对象的属性
self.age = age
def bark(self): # 这是类的方法,就是狗能做的事
print(f"{self.name}在汪汪叫!")
def introduce(self):
print(f"我叫{self.name},今年{self.age}岁")
# 创建对象(也叫实例化)
my_dog = Dog("旺财", 3)
your_dog = Dog("小白", 1)
# 调用对象的方法
my_dog.bark() # 输出:旺财在汪汪叫!
my_dog.introduce() # 输出:我叫旺财,今年3岁
your_dog.bark() # 输出:小白在汪汪叫!
your_dog.introduce() # 输出:我叫小白,今年1岁
# 直接访问对象的属性
print(my_dog.name) # 输出:旺财
print(your_dog.age) # 输出:1
重点理解
class 是定义类的关键字,类名一般首字母大写。
__init__ 方法(注意前后各有两个下划线)是初始化方法,创建对象的时候自动调用,用来给对象设置初始属性。
self 代表对象本身,必须写在方法的第一个参数位置,调用的时候不用手动传,Python会自动处理。
类就像一个"模板",对象是用这个模板"印"出来的具体实例。Dog 是模板,my_dog 和 your_dog 是用模板印出来的两条具体的狗。
今天的练习
定义一个 Student 类,包含姓名、学号、成绩三个属性,再写一个 show_info 方法打印出所有信息。
创建三个学生对象,分别调用 show_info 方法。
给 Student 类加一个 is_passed 方法,判断成绩是否及格(>=60),返回True或False。
第9天:继承和多态——代码复用的利器
今天要做什么: 学会用继承来复用代码,理解多态的基本概念。
敲这些代码试试
# ===== 父类:银行账户 =====
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
def deposit(self, amount):
"""存钱"""
self.balance += amount
print(f"{self.owner}存入{amount}元,当前余额{self.balance}元")
def withdraw(self, amount):
"""取钱"""
if amount > self.balance:
print(f"余额不足!当前余额{self.balance}元")
else:
self.balance -= amount
print(f"{self.owner}取出{amount}元,当前余额{self.balance}元")
def show_balance(self):
print(f"{self.owner}的账户余额:{self.balance}元")
# ===== 子类:信用卡账户 继承自银行账户 =====
class CreditAccount(BankAccount):
def __init__(self, owner, balance=0, credit_limit=5000):
super().__init__(owner, balance) # 调用父类的__init__
self.credit_limit = credit_limit # 子类独有的属性
def withdraw(self, amount):
"""重写父类的取钱方法——信用卡可以透支"""
if amount > self.balance + self.credit_limit:
print(f"超出信用额度!可用额度{self.balance + self.credit_limit}元")
else:
self.balance -= amount
print(f"{self.owner}取出{amount}元,当前余额{self.balance}元")
def show_credit_limit(self):
print(f"{self.owner}的信用额度:{self.credit_limit}元")
# 测试
normal = BankAccount("张三", 1000)
normal.deposit(500) # 输出:张三存入500元,当前余额1500元
normal.withdraw(2000) # 输出:余额不足!当前余额1500元
credit = CreditAccount("李四", 1000, 3000)
credit.withdraw(2000) # 输出:李四取出2000元,当前余额-1000元 (可以透支!)
credit.withdraw(3000) # 输出:超出信用额度!可用额度2000元
credit.show_credit_limit() # 输出:李四的信用额度:3000元
重点理解
继承用括号表示:class CreditAccount(BankAccount) 意思是CreditAccount继承BankAccount。
super().__init__() 用来调用父类的初始化方法,让子类继承父类的属性。
子类可以重写父类的方法(比如上面的 withdraw),这就是多态——同一个方法名,不同类有不同的行为。
子类可以加自己独有的属性和方法(比如 credit_limit 和 show_credit_limit)。
今天的练习
定义一个 Animal 父类,有 name 属性和 speak 方法(随便打印一句话)。
定义 Cat 和 Bird 两个子类,分别重写 speak 方法(猫叫和鸟叫)。
创建一只猫和一只鸟,分别调用 speak 方法,看看输出有什么不同。
第10天:文件读写——让程序会"记东西"
今天要做什么: 学会用Python读写电脑上的文件,让程序能把数据存下来,下次打开还能读到。
敲这些代码试试
# ===== 写入文件 =====
# "w"模式:写入模式,会覆盖原有内容
with open("test.txt", "w", encoding="utf-8") as f:
f.write("这是第一行内容\\n")
f.write("这是第二行内容\\n")
f.write("Python文件读写很简单!\\n")
print("文件写入完成!")
# ===== 读取文件 =====
# "r"模式:读取模式(默认,可以不写)
with open("test.txt", "r", encoding="utf-8") as f:
content = f.read() # 一次性读取全部内容
print("全部内容:")
print(content)
# 逐行读取
with open("test.txt", "r", encoding="utf-8") as f:
print("逐行读取:")
for line in f:
print(line.strip()) # strip()去掉行尾的换行符
# ===== 追加写入 =====
# "a"模式:追加模式,不会覆盖原有内容
with open("test.txt", "a", encoding="utf-8") as f:
f.write("这是追加的一行\\n")
f.write("再追加一行\\n")
print("追加完成!")
# 验证一下
with open("test.txt", "r", encoding="utf-8") as f:
print("追加后的内容:")
print(f.read())
重点理解
with open(…) as f: 是最推荐的写法,它会自动帮你关文件,不用担心忘记关导致资源占用。
三种常用模式:"r" 读、"w" 写(覆盖)、"a" 追加。
encoding="utf-8" 一定要加,不然中文可能会乱码。
read() 是一次性读全部,readlines() 是读成列表,for line in f 是逐行读,数据量大的时候用逐行读更省内存。
今天的练习
写一个程序,让用户输入5句话,每句保存到文件里,一行一句。
写一个程序,读取刚才那个文件,统计里面一共有多少个字。
写一个简单的日记本程序:每次运行让用户输入一段日记,以追加模式存到文件里,并在内容前面自动加上日期时间。
第11天:异常处理——让程序别动不动就崩
今天要做什么: 学会用 try-except 处理程序运行中可能出现的错误,让程序更健壮。
敲这些代码试试
# ===== 基础try-except =====
try:
num = int(input("请输入一个数字:"))
print(f"你输入的数字是:{num}")
except ValueError:
print("错误:请输入有效的数字,别输入字母啊!")
# ===== 捕获多种异常 =====
try:
a = int(input("请输入被除数:"))
b = int(input("请输入除数:"))
result = a / b
print(f"结果:{result}")
except ValueError:
print("输入的不是数字!")
except ZeroDivisionError:
print("除数不能为零!初中数学还记得吧?")
except Exception as e:
print(f"发生了其他错误:{e}")
# ===== try-except-else-finally =====
try:
file = open("data.txt", "r", encoding="utf-8")
content = file.read()
except FileNotFoundError:
print("文件不存在,将创建一个新文件")
with open("data.txt", "w", encoding="utf-8") as f:
f.write("这是默认内容\\n")
else:
print("文件读取成功!")
print(content)
finally:
print("不管有没有出错,这行都会执行")
# 如果有打开的文件,在这里关闭
try:
file.close()
except:
pass
重点理解
try 块里放可能出错的代码,except 块里放出了错怎么处理。
可以指定捕获特定类型的异常(如 ValueError、ZeroDivisionError),也可以用一个通用的 Exception 兜底。
else 块:try 里的代码没出错的时候执行。
finally 块:不管出没出错,一定会执行,一般用来做清理工作(关文件、关数据库连接等)。
养成写 try-except 的习惯,以后写项目会少很多麻烦,用户也不会看到一个报错红字就懵了。
今天的练习
改进第7天写的计算器,加上完整的异常处理,确保用户输入任何奇怪的东西都不会崩溃。
写一个程序,读取用户指定的文件,如果文件不存在则提示并让用户重新输入,直到输入正确的文件名为止。
模拟一个银行转账场景:用户输入转账金额,如果金额大于余额则抛出一个自定义的提示,用 try-except 捕获并友好提示。
第12天:用Python帮你整理电脑
今天要做什么: 学几个内置模块,用Python操作你的操作系统,写一个自动整理文件的脚本。
敲这些代码试试
import os
import shutil
from datetime import datetime
# ===== os模块:操作文件和文件夹 =====
# 获取当前工作目录
current_dir = os.getcwd()
print(f"当前目录:{current_dir}")
# 列出当前目录下所有文件
files = os.listdir(".")
print("当前目录下的文件:")
for file in files:
print(f" {file}")
# 判断是文件还是文件夹
for item in files:
full_path = os.path.join(current_dir, item)
if os.path.isfile(full_path):
print(f"{item} 是文件")
elif os.path.isdir(full_path):
print(f"{item} 是文件夹")
# 创建文件夹
if not os.path.exists("test_folder"):
os.makedirs("test_folder")
print("创建了 test_folder 文件夹")
# 获取文件信息
if os.path.exists("test.txt"):
size = os.path.getsize("test.txt") # 文件大小(字节)
mtime = os.path.getmtime("test.txt") # 修改时间(时间戳)
mtime_str = datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M:%S")
print(f"test.txt 大小:{size}字节,修改时间:{mtime_str}")
# ===== 实战:自动整理下载文件夹 =====
def organize_downloads(folder_path):
"""把文件按类型分到不同文件夹"""
# 定义文件类型和对应文件夹
categories = {
"图片": [".jpg", ".jpeg", ".png", ".gif", ".bmp"],
"文档": [".pdf", ".docx", ".doc", ".txt", ".xlsx", ".pptx"],
"视频": [".mp4", ".avi", ".mov", ".mkv"],
"压缩包": [".zip", ".rar", ".7z", ".tar"],
"代码": [".py", ".js", ".html", ".css", ".java"],
}
for filename in os.listdir(folder_path):
file_path = os.path.join(folder_path, filename)
if os.path.isfile(file_path):
# 获取文件扩展名
ext = os.path.splitext(filename).lower()
# 判断属于哪个类别
moved = False
for category, extensions in categories.items():
if ext in extensions:
category_folder = os.path.join(folder_path, category)
if not os.path.exists(category_folder):
os.makedirs(category_folder)
shutil.move(file_path, os.path.join(category_folder, filename))
print(f"移动 {filename} → {category}/")
moved = True
break
if not moved:
print(f"跳过 {filename}(未分类)")
# 试试看(改成你自己的文件夹路径)
# organize_downloads("C:/Users/你的用户名/Downloads")
print("提示:把上面那行注释去掉,改成你自己的下载文件夹路径就能用了")
重点理解
os 模块是操作系统的接口,os.path 下面有一堆处理路径的方法。
os.path.join() 用来拼接路径,比直接用 + 拼接更安全,能自动处理不同系统的路径分隔符。
shutil.move() 可以移动文件,shutil.copy() 可以复制文件。
os.path.splitext() 可以把文件名和扩展名分开,拿到文件后缀。
今天的练习
写一个脚本,遍历你电脑上的某个文件夹,打印出里面所有文件的名字和大小。
在上面的基础上,只打印出大于1MB的文件。
把上面那个自动整理文件的脚本改一改,适配你自己的下载文件夹,真的跑一次,感受一下Python帮你干活的爽感。
第13天:生成器和迭代器——处理海量数据
今天要做什么: 理解生成器和迭代器的概念,学会在数据量大的时候节省内存。
敲这些代码试试
import sys
# ===== 迭代器基础 =====
# 列表是可迭代对象,但不是迭代器
my_list = [1, 2, 3, 4, 5]
# 用iter()把可迭代对象变成迭代器
my_iterator = iter(my_list)
print(next(my_iterator)) # 输出:1
print(next(my_iterator)) # 输出:2
print(next(my_iterator)) # 输出:3
print(next(my_iterator)) # 输出:4
print(next(my_iterator)) # 输出:5
# print(next(my_iterator)) # 这行会报StopIteration错误,因为取完了
# ===== 生成器:用yield关键字 =====
def count_up_to(n):
"""生成器函数:从1数到n"""
i = 1
while i <= n:
yield i # yield跟return不同,它会"暂停"函数,下次调用时继续
i += 1
# 生成器不会一次性把所有值算出来,而是"用一点算一点"
counter = count_up_to(1000000)
print(next(counter)) # 输出:1
print(next(counter)) # 输出:2
print(next(counter)) # 输出:3
# 只取了3个值,剩下的999997个还没算,内存里只存了当前这一个
# ===== 对比:列表 vs 生成器 内存占用 =====
size = 1000000
# 列表:一次性把所有数据存到内存里
big_list = [i for i in range(size)]
print(f"列表内存占用:{sys.getsizeof(big_list)} 字节")
# 生成器:不存数据,只是知道怎么生成数据
big_generator = (i for i in range(size)) # 注意是圆括号,这是生成器表达式
print(f"生成器内存占用:{sys.getsizeof(big_generator)} 字节")
# 你会看到生成器的内存占用小得多!
# ===== 生成器表达式 =====
# 跟列表推导式很像,但是用圆括号而不是方括号
squares_gen = (x * x for x in range(10))
print(list(squares_gen)) # 输出:[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 注意:生成器只能遍历一次!
squares_gen = (x * x for x in range(5))
print(list(squares_gen)) # 输出:[0, 1, 4, 9, 16]
print(list(squares_gen)) # 输出:[] 第二次就空了!
重点理解
迭代器是一个"能记住自己走到哪了"的对象,每次调用 next() 就吐出一个值。
生成器是一种特殊的迭代器,用 yield 关键字定义,特点是"懒"——用到的时候才计算,不用就不算。
用生成器处理百万级以上的数据,内存占用比列表小几十倍甚至上百倍。
生成器只能遍历一次,用完就没了,如果需要重复使用,就重新创建一个。
今天的练习
写一个生成器函数,生成斐波那契数列的前n项(斐波那契数列:1, 1, 2, 3, 5, 8, 13…)。
对比用列表和用生成器分别生成前100万个偶数,看看内存占用差多少。
写一个生成器,逐行读取一个大文件,返回包含某个关键词的行(模拟日志分析)。
第14天:阶段小测——待办事项管理器
今天要做什么: 把前两周学的东西综合起来,写一个完整的待办事项管理器。这个做完,你已经能独立完成一个完整的小项目了。
功能要求
添加待办事项
查看所有待办事项
标记某条为已完成
删除某条待办
所有数据保存到文件,关闭程序再打开数据还在
参考代码(先自己写,实在写不出来再看)
import json
import os
from datetime import datetime
class TodoList:
def __init__(self, filename="todos.json"):
self.filename = filename
self.todos = []
self.load()
def load(self):
"""从文件加载数据"""
if os.path.exists(self.filename):
try:
with open(self.filename, "r", encoding="utf-8") as f:
self.todos = json.load(f)
print(f"已加载{len(self.todos)}条待办事项")
except:
self.todos = []
def save(self):
"""保存数据到文件"""
with open(self.filename, "w", encoding="utf-8") as f:
json.dump(self.todos, f, ensure_ascii=False, indent=2)
def add(self, title):
"""添加待办事项"""
todo = {
"id": len(self.todos) + 1,
"title": title,
"done": False,
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
self.todos.append(todo)
self.save()
print(f"已添加:{title}")
def show(self):
"""显示所有待办事项"""
if not self.todos:
print("暂无待办事项,快去添加一个吧!")
return
print("\\n===== 待办事项列表 =====")
for todo in self.todos:
status = "✓" if todo["done"] else "□"
print(f"{status} [{todo['id']}] {todo['title']} ({todo['created_at']})")
print("========================\\n")
def done(self, todo_id):
"""标记为已完成"""
for todo in self.todos:
if todo["id"] == todo_id:
todo["done"] = True
self.save()
print(f"已完成:{todo['title']}")
return
print(f"找不到编号为{todo_id}的待办事项")
def delete(self, todo_id):
"""删除待办事项"""
for i, todo in enumerate(self.todos):
if todo["id"] == todo_id:
removed = self.todos.pop(i)
self.save()
print(f"已删除:{removed['title']}")
return
print(f"找不到编号为{todo_id}的待办事项")
def main():
app = TodoList()
while True:
print("\\n请选择操作:")
print("1. 查看所有待办")
print("2. 添加待办")
print("3. 标记完成")
print("4. 删除待办")
print("5. 退出")
choice = input("请输入选项(1-5):")
if choice == "1":
app.show()
elif choice == "2":
title = input("请输入待办事项:")
app.add(title)
elif choice == "3":
try:
todo_id = int(input("请输入要标记完成的编号:"))
app.done(todo_id)
except ValueError:
print("请输入有效的数字编号")
elif choice == "4":
try:
todo_id = int(input("请输入要删除的编号:"))
app.delete(todo_id)
except ValueError:
print("请输入有效的数字编号")
elif choice == "5":
print("再见!")
break
else:
print("无效选项,请重新输入")
if __name__ == "__main__":
main()
检验标准
添加几条待办,关掉程序再打开,数据还在。
标记完成和删除功能正常。
输入错误的编号不会崩溃。
完成这个项目,第二阶段通关!你用了类、文件读写、异常处理、JSON数据格式,这些都是真实项目中天天用的东西。
第三阶段:见识Python真正厉害的地方(第15~21天)
前两周打好了基础,这一周来体验一下Python为什么这么火——它有一大堆现成的库,别人已经帮你写好了各种功能,你拿过来直接用就行。
第15天:NumPy——数据处理神器
今天要做什么
学会用NumPy创建、操作多维数组,掌握数组向量化运算、切片筛选、随机数与统计分析,直观感受NumPy比原生Python列表运算快百倍的优势,为后续Pandas数据分析、机器学习打下底层基础。
安装命令
打开VS Code终端(快捷键 `Ctrl+``)执行:
pip install numpy
验证安装:import numpy as np 无报错即安装成功。
完整实操代码
import numpy as np
import time
# ===================== 一、创建数组 =====================
# 1. 从普通列表转为一维数组
arr1 = np.array([1, 2, 3, 4, 5])
print("一维数组:", arr1)
print("数组类型:", type(arr1)) # <class 'numpy.ndarray'>
print("数组维度:", arr1.ndim) # 1维
print("数组形状:", arr1.shape) # (5,) 5个元素一维数组
# 2. 特殊填充数组
zeros = np.zeros(5) # 长度为5,全0一维数组
print("全零数组:", zeros)
ones = np.ones((3, 3)) # 3行3列全零二维矩阵
print("3*3全一矩阵:\\n", ones)
# 3. 等差数列、均匀等分数列
seq = np.arange(0, 10, 2) # 起点0,终点10(不含),步长2
print("等差数组:", seq)
lin = np.linspace(0, 1, 5) # 0~1之间均匀分成5个点
print("等间距数组:", lin)
# 4. 二维数组(矩阵,本节课重点拓展)
arr2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print("\\n3*3二维矩阵:\\n", arr2d)
print("矩阵形状:", arr2d.shape) # (3,3) 3行3列
# ===================== 二、数组向量化运算(核心优势) =====================
# 数组运算无需循环,直接逐元素计算,底层C实现,速度极快
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
print("a + b =", a + b) # 逐元素相加 [6, 8, 10, 12]
print("a * b =", a * b) # 逐元素相乘 [5, 12, 21, 32]
print("a ** 2 =", a ** 2) # 逐元素平方 [1, 4, 9, 16]
print("开平方:", np.sqrt(a)) # 逐元素开方
# ===================== 三、索引、切片与布尔筛选 =====================
arr = np.array([10, 20, 30, 40, 50, 60])
print("前3个元素:", arr[0:3])
print("大于30的元素:", arr[arr > 30]) # 布尔索引,数据筛选神器
# 二维矩阵取行、取列
print("矩阵第1行:", arr2d[0, :])
print("矩阵第2列:", arr2d[:, 1])
print("右下角2*2子矩阵:\\n", arr2d[1:, 1:])
# ===================== 四、随机数生成(数据分析必备) =====================
np.random.seed(42) # 设置随机种子,保证每次运行随机数一致
rand_arr = np.random.rand(1000000) # 生成100万个0~1均匀随机数
print("\\n百万随机数平均值:", np.mean(rand_arr))
print("百万随机数标准差:", np.std(rand_arr))
print("最大值:", np.max(rand_arr))
print("最小值:", np.min(rand_arr))
# ===================== 五、速度对比:原生列表 VS NumPy数组 =====================
size = 1000000
# 原生列表循环运算
start = time.time()
list1 = [i for i in range(size)]
list2 = [i for i in range(size)]
list_result = [a + b for a, b in zip(list1, list2)]
print(f"\\nPython列表运算耗时:{time.time() – start:.4f} 秒")
# NumPy向量化运算
start = time.time()
arr1 = np.arange(size)
arr2 = np.arange(size)
arr_result = arr1 + arr2
print(f"NumPy数组运算耗时:{time.time() – start:.4f} 秒")
核心重点理解
ndarray 多维数组:NumPy唯一核心数据结构,要求所有元素数据类型统一,内存连续存储,速度远超Python动态列表。
向量化运算:摒弃Python低效for循环,直接对整组数组批量计算,底层C语言实现,百万级数据运算速度提升几十~上百倍。
维度与形状
-
ndim:数组维度(1维/2维矩阵/多维张量)
-
shape:数组尺寸,二维矩阵格式 (行数, 列数)
-
切片规则:arr[行切片, 列切片],: 代表全部行/列
布尔索引筛选:arr[arr > 数值] 一键过滤符合条件的数据,数据分析高频用法。
内置统计函数:np.mean()均值、np.std()标准差、np.max()最大值、np.min()最小值,支持按行/列聚合(添加 axis=0 列、axis=1 行)。
随机种子 ****np.random.seed():固定种子后随机数结果可复现,实验、建模必备。
今日三道实操练习
二维矩阵操作:用NumPy创建3×3二维数组,分别完整打印每一行、每一列、右上角2×2子矩阵。
随机数统计:生成100万个0~100区间随机整数,用NumPy一次性算出平均值、标准差、最大值、最小值。
性能对比:用原生Python列表实现第2题全部统计计算,对比代码行数、运行耗时,直观感受NumPy优势。
拓展补充(选学,衔接后续Pandas)
reshape() 重塑数组维度:一维转矩阵 np.arange(9).reshape(3,3)
矩阵乘法:区分逐元素乘 * 和数学矩阵乘法 @ / np.dot()
缺失值处理:np.nan 空值、np.nanmean() 忽略空值计算均值
总结与展望
到这里,你已经完成了 Python 30 天学习计划中从第 1 天到第 15 天的核心内容。从开发环境搭建、基础语法、数据容器、流程控制、函数编程,到面向对象、文件读写、异常处理、系统自动化脚本、生成器迭代器,再到 NumPy 数值计算——你夯实了全套 Python 底层核心基础。
这些知识不仅能让你独立编写计算器、待办管理器、文件整理工具等完整本地程序,还能借助 NumPy 高效处理海量数值数据,为后续数据分析、爬虫、Web 开发等高阶内容打下不可缺少的底层根基。
接下来的第 16 天到第 22 天,你将继续学习 Pandas 数据处理、Matplotlib 可视化、爬虫、Flask 网站、SQLite 数据库与 Git 版本管理等实战技能,把基础语法真正落地到真实业务项目中。
坚持每天动手敲代码、完成配套练习,30 天后你一定会惊讶于自己的成长——加油,你已经走在正确的路上了!






