纯新手先看这一框
Python 适合做什么? 数据分析、人工智能/机器学习、自动化脚本(批量重命名/爬虫/办公自动化)、Web 后端(Django/FastAPI)、运维工具。它几乎是「想用代码解决日常问题」的第一选择。
为什么学它? 语法最接近自然语言、不用管内存和类型声明,写同样的功能代码量通常只有 C/Java 的 1/3~1/5,新手最容易「写出东西、获得正反馈」。
怎么跑起来? 三个方式:① 本页每个示例点 ▶ 运行 直接看结果(不用装任何东西);② 装官方解释器(python.org 下 3.12+),命令行敲 python hello.py;③ VS Code + Python 插件,F5 运行。
新手最常踩的坑? 缩进就是语法(用空格别用 Tab 混用)、== 和 is 不是一回事、可变默认参数会「串味」、range 是左闭右开。下面每节都会标红这些坑。
1. Hello World 与运行环境先跑通第一个程序,建立信心
Python 是解释型语言:代码写完由「解释器」逐行翻译给 CPU 执行,不用像 C 那样先编译。好处是改完就能跑;代价是运行速度比编译型语言慢(日常脚本无感)。
# 这是注释,井号开头,解释器会忽略
print("Hello, World!") # 打印一行文字,自带换行
print("2 + 3 =", 2 + 3) # 可以一次打印多个值,自动用空格隔开
print。调试时它是最快的工具——不确定变量是什么,就 print(type(x), x) 看一眼。但生产代码要用 logging,别把 print 留着(后面干货会讲)。装环境(想本地跑的话)
- Windows:python.org 下载 3.12+ 安装包,勾选 “Add to PATH”。
- 验证:命令行输入
python --version,看到版本号即成功。 - 交互式 REPL:命令行敲
python进去做计算器/试语法,exit()退出。
2. 变量与动态类型「赋值」本质是给对象贴标签
Python 里 变量不需要声明类型,第一次赋值就创建了它;同一个变量还能再指向别类型的值。
name = "小明" # 字符串
age = 18 # 整数
height = 1.75 # 浮点数
is_student = True # 布尔值
print(name, age, height, is_student)
age = "十八" # 同一个变量可以换类型(动态类型)
print(age)
print(type(name), type(age)) # type() 看变量的真实类型
a = b = [] 会让 a 和 b 指向同一个列表——改 a 会连 b 一起改。想要两份独立的,写 a = []; b = [] 或 import copy; b = copy.deepcopy(a)。命名规则
- 字母/数字/下划线,不能以数字开头;区分大小写(
Age和age是两个变量)。 - 约定:变量/函数用
snake_case(小写加下划线),常量全大写MAX_SIZE。 - 不能用关键字(
iffordef等)做变量名。
3. 数字与运算整数不会溢出,但浮点有坑
Python 的 int 是任意精度的——再大的数都不会溢出(只受内存限制),这是它做密码学/大数运算的先天优势。
a = 10
b = 3
print(a + b) # 13
print(a - b) # 7
print(a * b) # 30
print(a / b) # 3.3333... 注意:/ 永远返回浮点数
print(a // b) # 3 整除(向下取整)
print(a % b) # 1 取余
print(a ** b) # 1000 乘方
# 大整数随便来
big = 2 ** 1000
print(big) # 一个 300 多位的整数,不报错
0.1 + 0.2 == 0.3 的结果是 False!因为二进制浮点无法精确表示 0.1。涉及钱的场合用 decimal.Decimal,不要用 float。比较浮点用 abs(a-b) < 1e-9 而不是直接 ==。// 是「向下取整」不是「截断」:-7 // 2 == -4(不是 -3)。负数取整除法要小心。还有海象运算符 :=(3.8+)能在表达式里顺便赋值:if (n := len(x)) > 10: 避免算两次。4. 字符串不可变、可切片、能套公式
字符串用单引号或双引号都行(推荐一致)。它不可变:不能改其中某个字符,只能生成新字符串。
s = "hello"
print(s[0]) # h 索引从 0 开始
print(s[-1]) # o 负数从末尾往前数
print(s[1:4]) # ell 切片 [起:止),左闭右开
print(s[::2]) # hlo 步长 2
print(s[::-1]) # olleh 反转字符串的惯用法!
# f-string:最推荐的格式化方式(3.6+)
name, score = "小红", 95
print(f"{name} 考了 {score} 分") # 小红 考了 95 分
print(f"百分数:{score / 100:.1%}") # 百分数:95.0%
print(f"对齐:{name:>5}") # 右对齐占 5 位
s[0] = "H" 会报错。想改就切片拼新串:s = "H" + s[1:]。还有:用 + 在循环里拼几万次字符串极慢,应该用 "".join(parts)。pathlib.Path 而不是字符串拼 "/";处理正则用 re 模块;多行文本用三引号 """..."""。5. 列表 list最常用容器,能装任何东西
列表是可变的有序序列,元素类型可以混搭。增删改查 + 切片 + 推导式是必会四项。
nums = [3, 1, 2]
nums.append(4) # 末尾加 → [3,1,2,4]
nums.insert(0, 9) # 下标0插入 → [9,3,1,2,4]
nums.remove(1) # 删第一个值为1的 → [9,3,2,4]
popped = nums.pop() # 弹出末尾4,列表变[9,3,2]
nums[0] = 100 # 改 → [100,3,2]
print(nums[1:]) # [3,2] 切片
print(len(nums)) # 3 长度
print(2 in nums) # True 是否包含
# 列表推导式:一行生成新列表(极常用)
squares = [x*x for x in range(1, 6)] # [1,4,9,16,25]
evens = [x for x in range(10) if x % 2 == 0] # [0,2,4,6,8]
list.sort() 原地排序、返回 None;想要「排序后返回新列表」用 sorted(list)。新手常写 x = nums.sort() 然后 x 是 None。另外 copy = nums 是浅拷贝(共享同一对象),要 copy = nums.copy() 或 nums[:]。for 快不少。但别嵌套超过两层、别写复杂条件,否则可读性崩塌——那种情况老老实实写 for 循环。6. 元组 / 集合 / 字典三种容器各管一摊事
元组 tuple —— 不可变的有序序列
point = (3, 4)
x, y = point # 解包!一次性取出
print(x, y) # 3 4
# point[0] = 5 # 报错:元组不可变
# 用途:函数返回多个值、作字典键(列表不行)
集合 set —— 去重 + 成员判断飞快
s = {1, 2, 2, 3} # {1, 2, 3} 自动去重
print(2 in s) # True 判断极快 O(1)
a, b = {1,2,3}, {2,3,4}
print(a & b) # {2, 3} 交集
print(a | b) # {1,2,3,4} 并集
print(a - b) # {1} 差集
字典 dict —— 键值对,查表神器
d = {"name": "小明", "age": 18}
print(d["name"]) # 小明
d["age"] = 19 # 改
d["city"] = "北京" # 增
print(d.get("tel", "无")) # 无 取不到给默认值,不会报错
for k, v in d.items(): # 同时取键和值
print(k, "=", v)
d["不存在的键"] 会抛 KeyError。安全取用 d.get(key, 默认值)。遍历时不要边遍历边删字典元素,先收集要删的键再删,或用字典推导重建。collections 模块里 Counter(计数)、defaultdict(缺键自动给默认值)、deque(两端高效增删队列)是日常利器,比手写循环优雅且快。7. 流程控制if / for / while 的 Python 写法
# if:注意冒号 + 缩进
x = 7
if x > 10:
print("大")
elif x > 5:
print("中") # 会走这里
else:
print("小")
# for:直接遍历元素(不用下标)
for i in range(5): # range(5) = 0,1,2,3,4
print(i)
for i, v in enumerate(["a","b","c"]): # 同时拿下标和值
print(i, v)
# while
n = 3
while n > 0:
print(n)
n -= 1
# break 跳出 / continue 跳过本次
range(a, b) 是左闭右开,不含 b。想遍历 1~10 写 range(1, 11)。还有:判断「是否为空」直接写 if lst: 而非 if len(lst) > 0:,更 Pythonic。for ... in 可迭代对象 而不是 for i in range(len(x))。要下标就用 enumerate。8. 函数def、参数、作用域、lambda
def greet(name, greeting="你好"): # greeting 是默认参数
return f"{greeting},{name}!"
print(greet("小明")) # 你好,小明!
print(greet("小红", "早安")) # 早安,小红!
# *args 收位置参数,**kwargs 收关键字参数
def show(*args, **kwargs):
print("位置:", args)
print("关键字:", kwargs)
show(1, 2, a=3, b=4)
# lambda:一行小函数
square = lambda x: x * x
print(square(5)) # 25
def bad(x=[]): # 错误写法!
x.append(1)
return x
print(bad()) # [1]
print(bad()) # [1, 1] 同一个列表被反复用!
def good(x=None): # 正确写法
if x is None:
x = []
x.append(1)
return xglobal x;嵌套函数改外层要写 nonlocal x。新手经常忘了这俩关键字导致「改不动」。9. 文件 I/O用 with 自动关文件
# 写文件
with open("note.txt", "w", encoding="utf-8") as f:
f.write("第一行\n")
f.write("第二行\n")
# 离开 with 块,文件自动关闭(即使中间报错)
# 读文件
with open("note.txt", "r", encoding="utf-8") as f:
content = f.read() # 全读
print(content)
# 逐行读(大文件省内存)
with open("note.txt", "r", encoding="utf-8") as f:
for line in f:
print(line.strip())
encoding="utf-8",否则 Windows 下默认 GBK,读中文文件会 UnicodeDecodeError。模式 "w" 会清空原文件,追加用 "a"。永远用 with,别手动 f.close()——忘了关或中途报错都会漏关。import json; json.dump(obj, f) / json.load(f),比自己拼字符串靠谱一万倍。10. 异常处理让程序崩得优雅
try:
n = int(input("输入数字:"))
print(10 / n)
except ValueError: # 转不成整数
print("那不是数字")
except ZeroDivisionError: # 除以 0
print("不能除以 0")
else:
print("没有异常才走这里")
finally:
print("无论怎样都执行(常用于释放资源)")
# 主动抛异常
def check_age(a):
if a < 0:
raise ValueError("年龄不能为负")
return a
except:(不写异常类型)——它会连 KeyboardInterrupt、SystemExit 一起吞掉,Ctrl+C 都停不了程序,调试时想死。永远捕获具体异常。Exception(别继承 BaseException)。捕获后用 raise ... from e 保留原始堆栈链,排查问题更快。11. 迭代器与生成器惰性求值,省内存又优雅
生成器用 yield 替代 return,每次调用「暂停」并返回一个值,下次从暂停处继续——适合处理大数据流。
def count_up(n):
i = 0
while i < n:
yield i # 暂停并返回 i
i += 1
for x in count_up(3):
print(x) # 0 1 2
# 生成器表达式:比列表推导省内存(不一次性生成全部)
nums = (x*x for x in range(1000000)) # 此刻几乎不占内存
print(next(nums)) # 0
print(next(nums)) # 1
itertools(chain/islice/groupby)能写出极简洁的流式处理。12. 装饰器不改动原函数的「外挂」
装饰器本质是「接收一个函数、返回一个新函数」的高阶函数。靠闭包记住原函数的引用。
import time, functools
def timer(func): # 计时装饰器
@functools.wraps(func) # 保留原函数名/文档
def wrapper(*args, **kwargs):
t = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__} 用时 {time.time()-t:.3f}s")
return result
return wrapper
@timer # 等价于 do = timer(do)
def do():
time.sleep(0.5)
do()
@functools.wraps(func) 会导致被装饰函数的 __name__、__doc__ 变成 wrapper 的,调试日志和文档全乱。装饰器还能带参数、能叠加多个(执行顺序从下往上),进阶时再展开。13. 面向对象class、self、继承、魔术方法
class Person:
def __init__(self, name, age): # 构造方法,self 是实例自身
self.name = name
self.age = age
def greet(self):
return f"我是{self.name},{self.age}岁"
@property # 把方法当属性用
def is_adult(self):
return self.age >= 18
class Student(Person): # 继承
def __init__(self, name, age, sid):
super().__init__(name, age) # 调父类构造
self.sid = sid
p = Student("小明", 16, "001")
print(p.greet(), p.is_adult) # 我是小明,16岁 False
self(名字可改但约定如此),调用时不用传。继承方法要扩展父类逻辑,用 super().方法() 调父类,别直接写父类名——多继承时 MRO(方法解析顺序)会乱。@dataclass 装饰器,自动生成 __init__/__repr__/__eq__,写数据类极省事:
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
还有 __slots__ 能锁死属性、大幅省内存(创建几百万个对象时关键)。14. 模块与包 / 虚拟环境组织代码、隔离依赖
# math_utils.py
def add(a, b): return a + b
# main.py
import math_utils # 导入整个模块
from math_utils import add # 只导入一个函数
print(add(1, 2))
# __name__ 技巧:直接运行做测试,被导入时不执行
if __name__ == "__main__":
print("只有直接运行本文件才会打印")
python -m venv venv 创建,激活后 pip install 包名 装的包只在这个项目里,不会污染全局。pip freeze > requirements.txt 记录依赖,别人 pip install -r requirements.txt 一键复现。15. 并发GIL 真相:什么时候该用哪个
multiprocessing(多进程,真并行);IO 密集任务(网络/文件)用 threading 或 asyncio 能明显提速(等待时让出 GIL)。import concurrent.futures, time
def work(n):
time.sleep(0.5)
return n * n
# 线程池:适合 IO 密集
with concurrent.futures.ThreadPoolExecutor(4) as ex:
results = list(ex.map(work, range(8)))
print(results)
# 进程池:适合 CPU 密集
with concurrent.futures.ProcessPoolExecutor() as ex:
results = list(ex.map(work, range(8)))
print(results)
asyncio 是单线程内的「协程并发」,用 async def + await,适合高并发网络服务(几万连接)。但它不是多线程,遇到 time.sleep 这种阻塞调用照样卡住——要用 asyncio.sleep。新手最容易在这里踩坑。16. 标准库与工程化别重复造轮子
Python 自带「电池」(batteries included),这些标准库几乎天天用:
| 模块 | 干啥 |
|---|---|
collections | Counter/defaultdict/deque/namedtuple |
pathlib | 面向对象的文件路径处理 |
datetime | 日期时间(时区用 zoneinfo) |
logging | 分级日志(别用 print 上生产) |
typing | 类型注解(大项目配合 mypy) |
itertools/re | 迭代器工具 / 正则 |
argparse | 命令行参数解析 |
def add(a: int, b: int) -> int:,再用 mypy 静态检查,能在运行前抓出一半以上的类型 bug。测试用 pytest,比自带 unittest 简洁得多。🧪 动手实验室代码已写好,点 ▶ 直接看结果(手机/电脑通用)
下面每个程序都已预填好。想验证就点 ▶ 运行;想改就在框里改再点运行;右上角可切语言、↺ 重置回到初始代码。
① 基础:打印与运算
② 列表:推导式 + 切片
③ 函数:默认参数 + 可变参数
④ 字典:词频统计
⑤ 生成器:惰性求和
💡 连老师都不会教你的干货(工业级技巧)这些学校不教,但公司里天天用
- int 任意精度:Python 整数不会溢出,适合大数/密码学,但比 C 的定长 int 慢,别拿它做超高频数值循环。
- is vs == 的坑:
a is b比的是「是不是同一个对象」;小整数(-5~256)和短字符串会被解释器缓存复用,所以256 is 256为 True 但257 is 257可能为 False。永远用==比值。 - 可变默认参数串味:
def f(x=[])会在多次调用间共享同一个列表,正确写法是x=None后再初始化。 - 浅拷贝 vs 深拷贝:
list.copy()/dict.copy()只复制第一层,嵌套的可变对象仍是共享;要完全独立用copy.deepcopy。 - GIL 真相:CPU 密集用
multiprocessing,IO 密集用threading/asyncio。别指望多线程加速纯计算。 - f-string 的隐藏能力:支持表达式
{x*2}、转换符{s!r}(repr)、{x:>5}对齐、{pi:.2f}精度,比 % 和 .format() 都强。 - 海象运算符 :=(3.8+):在表达式内赋值,避免重复计算,如
while (line := f.readline()):。 - 推导式别过度:嵌套超过两层或条件复杂就改回 for 循环,可读性优先。
- 用 pathlib 别用 os.path:
Path("a/b.txt").read_text(encoding="utf-8")一行读完,跨平台路径分隔符自动处理。 - 用 with 管理一切资源:文件、锁、数据库连接都用上下文管理器,异常也能正确释放。
- enumerate 优于 range(len):
for i, v in enumerate(lst)更清晰,还避免下标越界。 - collections 四件套:Counter(计数)、defaultdict(缺键给默认)、deque(双端队列)、namedtuple(轻量结构化),取代大量手写逻辑。
- __slots__ 省内存:类里定义
__slots__ = ("x","y")禁止动态加属性,创建百万实例时内存可降数倍。 - 类型注解 + mypy:大项目必备,运行前静态抓类型错误,相当于给 Python 加了半套编译期检查。
- join 优于 + 拼接:循环里
"".join(parts)比反复+快得多(后者每次都新建字符串)。 - logging 别用 print:生产环境用
logging分级(DEBUG/INFO/WARNING/ERROR)并输出到文件,print 无法关、无级别、难定位。 - 虚拟环境隔离依赖:每个项目独立 venv + requirements.txt,避免 A 项目升级包把 B 项目搞崩。
- 别捕获裸 except:写
except ValueError:这类具体异常,裸except:连 Ctrl+C 都吞掉,调试噩梦。
📝 自测题点「查看答案」核对,先自己想
1. 0.1 + 0.2 == 0.3 的结果是?
查看答案
False。二进制浮点无法精确表示 0.1,存在舍入误差。比较浮点应用 abs(a-b) < 1e-9;涉及金额用 decimal.Decimal。
2. 以下代码连续调用两次会输出什么?
def f(x=[]):
x.append(1)
return x
f(); f()
查看答案
第一次 [1],第二次 [1, 1]。因为默认列表在函数定义时创建一次、被所有调用共享。解决:默认参数用 None。
3. 反转字符串 "hello" 最 Pythonic 的写法是?
查看答案
s[::-1]。切片步进为 -1 即从尾到头取全部字符。
4. CPU 密集任务想提速,该用多线程还是多进程?
查看答案
多进程(multiprocessing)。因为 GIL 限制同一进程内只有一个线程在跑字节码,多线程对纯计算无效;多进程各有独立解释器和 GIL,能真并行。
5. 判断列表为空,哪种更好:if len(lst) == 0: 还是 if not lst:?
查看答案
if not lst: 更好。空容器在布尔上下文就是 False,更简洁、更 Pythonic,且对字符串/字典/集合同样适用。