工作台

书架 · Python 学习系列 · 01 · Python 基础下一章:02 Python 进阶 →

01 · Python 基础

本章目标:装好环境,掌握变量、流程控制、四大核心数据结构、函数、模块、异常处理。学完你能独立写出几百行的脚本。

前置知识:会任何一门编程语言(你写过 Java,这章会非常快)。


目录

  1. Python 是什么,为什么 AI 圈都用它
  2. 环境搭建(Windows 11)
  3. 第一个程序:REPL 与脚本
  4. 变量与基本数据类型
  5. 字符串
  6. 运算符
  7. 流程控制
  8. 四大核心数据结构:list / tuple / dict / set
  9. 函数
  10. 模块与包
  11. 文件读写初步
  12. 异常处理
  13. 自测清单
  14. 小练习

1. Python 是什么,为什么 AI 圈都用它

Python 是一门动态类型、解释执行的语言。它成为 AI 第一语言不是偶然:

Java vs Python 一句话:Java 是"编译成字节码跑在 JVM 上的静态类型语言",Python 是"解释器直接执行的动态类型语言"。Java 重工程规整,Python 重开发速度。两者不是替代关系——你以后大概率是 Java 写业务 + Python 写 AI 的双枪将。

维度 Java Python
类型 静态,编译期检查 动态,运行时才知道
语句结尾 ; 换行即结尾
代码块 { } 缩进(4 个空格)
注释 ///* */ #"""文档字符串"""
主流场景 企业后端、大数据 AI、数据科学、脚本、Web
运行方式 javac 编译再运行 python xxx.py 直接跑

最重要的一条:Python 用缩进表示代码块,缩进错了程序就错了(甚至逻辑变了但没报错)。请从今天起统一用 4 个空格,不要混用 Tab。


2. 环境搭建(Windows 11)

2.1 安装 Python

  1. python.org/downloads 下载 Python 3.12+ 的 Windows installer(64-bit)
  2. 安装第一屏务必勾选 "Add python.exe to PATH"(这是新手第一大坑)
  3. 验证:打开 PowerShell(或 Git Bash),执行:
python --version     # 输出 Python 3.12.x 即成功
pip --version        # pip 是 Python 的包管理器,类似 maven

⚠️ 本机如果装了多个 Python,python 命令可能指向老版本。用 where python(PowerShell 用 Get-Command python)排查。

2.2 安装 uv(现代工具链,强烈推荐)

uv 是 Astral 公司用 Rust 写的 Python 工具链,一个命令搞定 Python 版本管理、虚拟环境、依赖安装,速度比 pip 快 10 倍以上,2024 年后已是事实标准:

# PowerShell 安装
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

# 验证
uv --version

安装后你可以完全不用系统 Python,uv 能自己管理:

uv python install 3.12      # 安装某个版本的 Python
uv python list               # 查看可用版本

📌 本章你只需要记住 3 条 uv 命令(第 03 章系统讲): - uv init demo —— 新建项目 - uv add requests —— 加依赖(自动创建虚拟环境) - uv run main.py —— 在项目环境中运行

2.3 编辑器:VSCode

  1. 安装 VSCode
  2. 扩展市场搜 Python(Microsoft 官方)安装
  3. Pylance(语法提示,随 Python 扩展自带)

写大项目也可以用 PyCharm(社区版免费),但 VSCode + Pylance 对新手完全够用且更轻。


3. 第一个程序:REPL 与脚本

3.1 REPL(交互式环境)

终端输入 python 回车,进入交互模式(>>> 提示符),适合做"草稿纸":

>>> 1 + 1
2
>>> "hello" * 3
'hellohellohello'
>>> exit()   # 退出

新手建议:学本章时把所有示例都在 REPL 里敲一遍,肌肉记忆比看书强十倍。

3.2 脚本文件

新建 hello.py

# hello.py
print("Hello, Python!")

name = "macao"
age = 18
print(f"{name} 今年 {age} 岁")   # f-string,第 5 节细讲

运行:

python hello.py
# 或者用 uv(自动使用项目环境)
uv run hello.py

对照 Java:不需要 public class Hello { public static void main(String[] args) } 这套样板,顶层直接写语句。print() 就是 System.out.println()


4. 变量与基本数据类型

4.1 变量:没有类型声明

age = 18            # int
price = 9.99        # float
is_vip = True       # bool(注意大写开头:True / False)
name = "macao"      # str
nothing = None      # None 类似 Java 的 null

# 变量只是"名字标签",可以随时贴到别的类型上
x = 100
x = "换个类型也行"    # 合法!但工程里别这么干(有类型检查工具,见第 02 章)

查看类型:

age = 18
is_vip = True
type(age)     # <class 'int'>
type(is_vip)  # <class 'bool'>

Java vs Python: - Java:int age = 18; —— 类型属于变量 - Python:age = 18 —— 类型属于,变量只是名字。等价于所有变量都是 Object age,但用起来有各自类型的方法 - Python 的 None ≈ Java 的 null,判断写 if x is None:(不是 ==,见第 02 章的坑)

4.2 数字

# int 无限精度,不会像 long 一样溢出
2 ** 100        # 直接算,2 的 100 次方

# 除法有三种
7 / 2           # 3.5   —— 真除法,结果永远是 float
7 // 2          # 3     —— 地板除,扔掉小数
7 % 2           # 1     —— 取模
round(3.14159, 2)  # 3.14 —— 四舍五入到 2 位
abs(-5)         # 5
int("42")       # 42   —— 字符串转 int
float("3.14")   # 3.14
str(42)         # "42" —— 转字符串

☕ Java 里 7 / 2 是 3(整数除法),Python 里 / 永远给 float,// 才是整数除法。这跟 Java 正好相反,注意

4.3 bool 与真值

True and False   # False
True or False    # True
not True         # False

# Python 独有:任何值都有"真值性"
# 以下都算 False:0、0.0、""、空 list/dict/set、None
# 其余全是 True

items = []
if not items:            # 不用写 len(items) == 0
    print("空的")

5. 字符串

5.1 三种引号与 f-string

s1 = '单引号'
s2 = "双引号"           # 两者完全等价,习惯上外双内单
s3 = """三引号可以
跨越多行,
类似 Java 的文本块"""    # Java 15+ 的 """

# f-string:格式化字符串(必学,相当于 String.format 的语法糖)
name, score, age = "macao", 92.5, 18
print(f"{name} 的分数是 {score}")
print(f"明年 {age + 1} 岁")          # {} 里可以放表达式
print(f"{score:.1f}")                 # 92.5,保留 1 位小数
print(f"{1234567:,}")                 # 1,234,567 千分位
print(f"{'居中':^10}")                # 填充对齐

5.2 常用方法

s = "  Hello, Python  "

s.strip()              # "Hello, Python"   去两端空白
s.strip().lower()      # "hello, python"
s.replace("Python", "Java")
s.split(",")           # 按分隔符切 → list
"a,b,c".split(",")     # ['a', 'b', 'c']
"-".join(["2026", "09", "05"])   # "2026-09-05"  ← 注意 join 写在分隔符上
"abc123".startswith("abc")
"abc123".endswith("123")
"py" in "python"       # True —— 成员判断直接用 in
len("python")          # 6
"hello"[0]             # 'h' —— 索引取字符

Java vs Python:Java 字符串是不可变的,Python 同样不可变——所有方法都返回新串。但 Python 用 in 代替 contains(),用 len() 代替 .length(),切片代替 substring()

5.3 切片(slicing)—— 高频技巧

s = "python"

s[0]      # 'p'   正索引从 0 开始
s[-1]     # 'n'   负索引从末尾数,-1 是最后一个
s[0:3]    # 'pyt' 第 0~2 个(左闭右开,同 Java substring)
s[2:]     # 'thon' 省略结尾 = 到最后
s[:2]     # 'py'  省略开头 = 从头开始
s[::2]    # 'pto' 步长 2,隔一个取一个
s[::-1]   # 'nohtyp' 反转字符串的惯用写法

切片同样适用于 list/tuple,是 Python 的核心操作,务必练熟。


6. 运算符

# 算术:+ - * / // % **
2 ** 10        # 1024

# 比较:== != > < >= <=,产出 bool
# ⭐ Python 支持链式比较(Java 不支持)
age = 30
18 < age < 60       # 等价于 18 < age and age < 60

# 逻辑:and / or / not(不是 && || !)
a, b = True, False
a and b   # False

# 身份判断:is / is not(比较"是不是同一个对象")
x = None
x is None  # True —— 判断 None 的标准写法

# 成员判断:in / not in
3 in [1, 2, 3]      # True
"k" not in "abc"    # True

对照&&and||or!not。Python 没有 ++/--,自增写 i += 1。没有三元 ? :,写 x = a if cond else b


7. 流程控制

7.1 if / elif / else

score = 85

if score >= 90:
    grade = "A"
elif score >= 80:        # elif = else if
    grade = "B"
elif score >= 60:
    grade = "C"
else:
    grade = "D"

☕ 没有 switch(3.10+ 有 match-case,语法见下,但底层是模式匹配,比 switch 强大):

command = "start"

match command:
    case "start":
        print("启动")
    case "stop" | "exit":        # 或模式
        print("停止")
    case _:                      # default
        print("未知命令")

7.2 for 循环 —— 是"foreach",不是计数器

# Python 的 for 只有一种形态:遍历可迭代对象
for fruit in ["苹果", "香蕉", "橘子"]:
    print(fruit)

for ch in "abc":
    print(ch)             # 字符串也能遍历

# 要计数器时用 range
for i in range(5):        # 0 1 2 3 4(左闭右开)
    print(i)
for i in range(2, 10, 2): # 2 4 6 8(起、止、步长)
    print(i)

# 同时要下标和值:enumerate(Java 里 for-i 循环的替身)
for i, fruit in enumerate(["苹果", "香蕉"]):
    print(i, fruit)       # 0 苹果 / 1 香蕉

# 并行遍历多个序列:zip
names = ["Tom", "Jerry"]
ages = [3, 4]
for name, age in zip(names, ages):
    print(name, age)

Java vs Pythonfor (int i = 0; i < n; i++) 在 Python 里几乎不存在,等价物是 for i in range(n);遍历集合用 for x in list(= Java 的 for (var x : list))。需要下标时永远优先 enumerate,不要 range(len(list)) 再下标访问(暴露你是 Java 转来的 :))。

7.3 while、break、continue、循环 else

count = 0
while count < 3:
    print(count)
    count += 1

# break / continue 与 Java 一致

# Python 特色:循环可以带 else(循环没被 break 才执行)
for n in [2, 4, 6, 7]:
    if n % 2 == 1:
        print("发现奇数", n)
        break
else:
    print("全是偶数")     # 没有 break 时走这里

循环 else 在"查找类"逻辑里很好用:找到就 break,走完没找到进 else。


8. 四大核心数据结构:list / tuple / dict / set

总对照表(先记这个):

Python 近似 Java 可变 有序 典型用途
list ArrayList<Object> 通用有序集合
tuple List.of(...) 不可变 List 固定结构、函数多返回值
dict HashMap<K,V> ✅(3.7+) 键值映射,到处都是
set HashSet<Object> 去重、成员判断

8.1 list

# 创建
nums = [1, 2, 3]
mixed = [1, "two", 3.0, [4, 5]]   # 可混合类型(但工程里别混)
empty = []

# 增
nums.append(4)          # 尾部追加 → [1,2,3,4]
nums.insert(0, 0)       # 指定位置插入
nums.extend([5, 6])     # 拼接另一个 list

# 删
nums.remove(3)          # 按值删(第一个匹配)
last = nums.pop()       # 弹出尾部并返回
first = nums.pop(0)     # 弹出指定下标
del nums[0]             # 按下标删

# 改 / 查
nums[0] = 100
nums[1]                 # 读
nums[-1]                # 最后一个
nums[1:3]               # 切片,返回新 list
len(nums)               # 长度

# 判断
3 in nums               # True

# 排序
sorted([3, 1, 2])               # [1,2,3] 返回新列表
sorted([3, 1, 2], reverse=True) # [3,2,1]
nums.sort()                     # 原地排序,返回 None ⚠️
words = ["banana", "apple", "fig"]
sorted(words, key=len)          # ['fig', 'apple', 'banana'] 按长度排

# 常用聚合
sum([1, 2, 3])       # 6
max([1, 5, 3])       # 5
min([1, 5, 3])       # 1

⚠️ list.sort()原地排序返回 Nonesorted() 返回新列表。新手常写 nums = nums.sort(),结果 nums 变成 None。

8.2 list 推导式(comprehension)—— Python 灵魂语法

# 传统写法
squares = []
for x in range(10):
    squares.append(x ** 2)

# 推导式一行搞定
squares = [x ** 2 for x in range(10)]

# 带条件过滤
evens = [x for x in range(20) if x % 2 == 0]

# 带 if-else 转换
labels = ["偶" if x % 2 == 0 else "奇" for x in range(5)]

# 嵌套(少用,可读性差)
pairs = [(x, y) for x in range(3) for y in range(3)]

☕ 等价于 Java Stream 的 map/filter/collect[x**2 for x in xs if x>0]xs.stream().filter(x->x>0).map(x->x*x).toList()。Python 里能用推导式就不用 for 循环 append,这是地道写法。

8.3 tuple

point = (3, 5)              # 圆括号(可省略,但别省)
single = (42,)              # ⚠️ 单元素必须带逗号
x, y = point                # 解包(unpacking)——超常用
x, y = y, x                 # 交换变量不需要中间变量!

# 函数返回多个值其实就是返回 tuple
def min_max(nums):
    return min(nums), max(nums)

lo, hi = min_max([3, 1, 4])     # 解包接收

tuple 不可变:point[0] = 9 会抛 TypeError。它的意义:表达"固定结构的数据"(坐标、配置项),以及作为 dict 的 key。

8.4 dict

# 创建
user = {"name": "macao", "age": 18}
empty = {}

# 增改
user["city"] = "成都"           # 有则改,无则增

# 查
user["name"]                    # key 不存在 → KeyError ⚠️
user.get("name")                # 不存在 → None
user.get("phone", "未填写")      # 不存在 → 给默认值

# 删
del user["city"]
age = user.pop("age")           # 删除并返回值

# 判断 key
"name" in user                  # True(判断的是 key,不是 value!)

# 遍历
for key in user:                        # 默认遍历 key
    print(key, user[key])
for key, value in user.items():         # 同时要 key value(最常用)
    print(key, value)
for v in user.values():
    print(v)

# 常用
len(user)
user.update({"age": 20, "vip": True})   # 批量合并
new_user = {**user, "extra": 1}         # 合并出新 dict(不改动原 dict)

# 字典推导式
lengths = {name: len(name) for name in ["ab", "cde"]}

dict.get(k, default)map.getOrDefault(k, d)。JSON 与 dict 结构同构,AI 开发里 API 报文、模型消息全是 dict/list 嵌套,dict 是 Python 里最重要的结构,没有之一

8.5 set

s = {1, 2, 3}
empty_set = set()          # ⚠️ {} 是空 dict,不是空 set!

s.add(4)
s.remove(1)                # 元素不存在会抛 KeyError
s.discard(99)              # 不存在也不报错

# 核心价值 1:去重
nums = [1, 2, 2, 3, 3, 3]
unique = list(set(nums))   # [1, 2, 3](顺序不保证)

# 核心价值 2:集合运算
a = {1, 2, 3}
b = {3, 4, 5}
a & b      # {3}      交集
a | b      # {1,2,3,4,5} 并集
a - b      # {1,2}    差集
a ^ b      # {1,2,4,5} 对称差

# 核心价值 3:O(1) 成员判断(大 list 判断存在性前先转 set)
big_list = list(range(1_000_000))
big_set = set(big_list)
10000 in big_list   # O(n) 慢
10000 in big_set    # O(1) 快

8.6 解包进阶(很常用)

first, *rest = [1, 2, 3, 4]      # first=1, rest=[2,3,4]
*init, last = [1, 2, 3, 4]       # init=[1,2,3], last=4

def f(*args, **kwargs):          # 见函数一节
    pass

f(*[1, 2], **{"k": 3})           # 调用时解包展开

9. 函数

9.1 定义与调用

def add(a, b):
    """两数相加。这行叫 docstring,是 Python 的文档注释。"""
    return a + b

# 无返回值函数默认返回 None
def greet(name):
    print(f"hello {name}")

result = greet("macao")   # result 是 None

def ≈ 方法定义;docstring ≈ Javadoc;没有可见性关键字(没有 public/private,惯例用 _name 表示私有);没有方法重载(后定义覆盖先定义,同名不同参靠默认参数解决)。

9.2 参数的四种形态(重点)

# 1. 位置参数(最普通)
def power(x, n):
    return x ** n

# 2. 关键字参数:调用时指名道姓,可读性神器
power(n=2, x=3)                 # 不用管顺序

# 3. 默认参数
def greet(name, greeting="你好"):
    return f"{greeting}, {name}"

greet("Tom")                    # 你好, Tom
greet("Tom", greeting="Hello")  # Hello, Tom

# ⚠️ 头号新手坑:默认值绝不能是 list/dict 等可变对象!
def bad(items=[]):              # ❌ 默认值在函数定义时只创建一次,多次调用共享
    items.append(1)
    return items

bad()   # [1]
bad()   # [1, 1] ← 惊不惊喜

def good(items=None):           # ✅ 标准写法
    items = items if items is not None else []
    items.append(1)
    return items

# 4. 可变参数:*args 收集多余位置参数为 tuple,**kwargs 收集多余关键字参数为 dict
def summary(*args, **kwargs):
    print(args, kwargs)

summary(1, 2, 3, mode="fast", debug=True)
# (1, 2, 3) {'mode': 'fast', 'debug': True}

9.3 lambda

# lambda 只能写一个表达式
square = lambda x: x ** 2

# 主要用途:作为 key 函数传给 sorted / max / min
users = [{"name": "a", "age": 30}, {"name": "b", "age": 25}]
by_age = sorted(users, key=lambda u: u["age"])
oldest = max(users, key=lambda u: u["age"])

☕ ≈ Java 的 (x) -> x * x。Python 的 lambda 故意做得弱(只能一个表达式),复杂逻辑就老老实实 def

9.4 作用域

x = 10            # 全局

def outer():
    y = 20        # 局部
    def inner():
        nonlocal y    # 引用外层函数变量(少用)
        y += 1
    inner()

def read_global():
    print(x)      # 读全局 OK

def write_global():
    global x      # 想改全局要声明(工程里避免这种写法)
    x = 99

10. 模块与包

10.1 import

import math                      # 导入整个模块
math.sqrt(16)                    # 4.0

from math import sqrt, pi        # 导入具体名字(常用)
sqrt(16)                         # 直接用

import numpy as np               # 起别名(社区惯例,如 np / pd)
from datetime import datetime    # 标准库常用模块

# ⚠️ 不推荐 from math import *(污染命名空间)

importimport,一个 .py 文件就是一个模块(≈ Java 一个类文件),一个带 __init__.py 的目录就是一个包(≈ package)。区别:Python 导入的是整个模块对象from x import y 是把模块里的名字 y 拿到当前命名空间。

10.2 if __name__ == "__main__":

# utils.py
def add(a, b):
    return a + b

def main():
    print("自测:", add(1, 2))

# __name__ 是模块的内置名字:
# 直接运行本文件时它是 "__main__",被别人 import 时它是模块名 "utils"
if __name__ == "__main__":
    main()

这是 Python 版的"入口函数":让文件既能当库被导入、又能当脚本直接跑。每个可执行脚本都应带上它

10.3 用第三方库

# 传统方式(pip)
pip install requests

# uv 项目方式(推荐,见第 03 章)
uv add requests
import requests
resp = requests.get("https://api.github.com")
print(resp.status_code)

11. 文件读写初步

# with 语句自动关文件(≈ try-with-resources,第 02 章细讲原理)
with open("data.txt", "w", encoding="utf-8") as f:   # ⚠️ Windows 下必带 encoding
    f.write("第一行\n")
    f.write("第二行\n")

with open("data.txt", "r", encoding="utf-8") as f:
    content = f.read()               # 一次读全

with open("data.txt", encoding="utf-8") as f:
    for line in f:                   # 逐行读(大文件友好)
        print(line.strip())          # strip 去掉行尾换行符

# 追加模式 "a",二进制 "rb"/"wb"

模式:r 读(默认)/ w 清空写 / a 追加写 / x 新建写(存在则报错)。

JSON 读写(AI 开发天天用):

import json

user = {"name": "macao", "tags": ["py", "ai"], "vip": True}

with open("user.json", "w", encoding="utf-8") as f:
    json.dump(user, f, ensure_ascii=False, indent=2)   # ensure_ascii=False 才能存中文

with open("user.json", encoding="utf-8") as f:
    loaded = json.load(f)

# 串 ↔ 对象(更常用)
s = json.dumps(user, ensure_ascii=False)   # dict → str
d = json.loads(s)                          # str → dict

json.dumpsobjectMapper.writeValueAsStringjson.loadsreadValue。Python 的 dict/list 天生就是 JSON 结构,没有"实体类"这一层。


12. 异常处理

12.1 基本结构

try:
    num = int("abc")            # ValueError
except ValueError as e:
    print(f"转换失败: {e}")
except (KeyError, IndexError):  # 一个 except 接多种异常
    print("键或下标错了")
except Exception as e:          # 兜底(≈ catch (Exception e))
    print(f"未知错误: {e}")
else:
    print("没出异常才执行")       # Java 没有这个块
finally:
    print("无论如何都执行")

常见异常类型速查:

异常 场景
ValueError 值不对:int("abc")
TypeError 类型不对:"a" + 1
KeyError dict key 不存在
IndexError 下标越界
FileNotFoundError 文件不存在
ZeroDivisionError 除零
AttributeError 属性/方法不存在:None.xxx 高频!

12.2 抛出与自定义

def set_age(age):
    if age < 0:
        raise ValueError(f"年龄不能为负: {age}")   # raise ≈ throw
    return age

# 自定义异常:继承 Exception 即可
class BusinessError(Exception):
    def __init__(self, code: str, message: str):
        super().__init__(message)
        self.code = code

raise BusinessError("A0001", "余额不足")

对照 Java: - Python 没有 checked 异常——所有异常都相当于 RuntimeException,函数签名不声明也不强制捕获 - raise = throwexcept = catchfinally 同义 - Python 文化是 EAFP(It's Easier to Ask Forgiveness than Permission):先直接干,错了再 catch。Java 的 LBYL(先 if 判断再执行)风格在 Python 里常被认为不地道:

d = {"name": "macao"}       # 示例数据
default = "未填写"

# LBYL(Java 风)
if "key" in d:
    v = d["key"]

# EAFP(Python 风)
try:
    v = d["key"]
except KeyError:
    v = default
print(v)                     # 未填写(两条路都没进 if,最终由 except 兜住)

13. 自测清单

能不查资料回答以下问题,本章就算过关:

14. 小练习

练习 1(基础):写函数 fizzbuzz(n),打印 1~n:3 的倍数输出 "Fizz",5 的倍数输出 "Buzz",两者都是输出 "FizzBuzz",否则输出数字。

练习 2(数据结构):给定 words = ["apple", "banana", "avocado", "cherry", "blueberry"],用一行推导式筛出 a 开头且长度 > 5 的单词,再按长度倒序排序。

练习 3(dict 应用):统计一段英文里每个单词出现的次数(忽略大小写),输出出现最多的前 3 个单词。提示:str.split()、dict 计数、sorted(d.items(), key=lambda kv: kv[1], reverse=True)

练习 4(综合):读取一个 JSON 文件(自己造一个用户列表),找出 age > 18 的用户,把他们的 name 写入新的 JSON 文件。


⬅️ 返回目录 | ➡️ 下一章:02-Python进阶