01 · Python 基础语法
能独立编写带注释、可复用的脚本,理解变量、流程控制、函数与面向对象的基本思想,为 NumPy / Pandas 打地基。
0. 小白导读:Python 到底是什么?
Python 是一种"编程语言"——语言是人和人交流的工具,编程语言就是人和计算机交流的工具。你写一句"人话",计算机按照规则执行,帮你干活。
- 你不需要懂计算机内部原理,就像你不需要懂发动机原理也能开车。
- 你只需要学会"用 Python 的语法表达你想做的事",剩下的交给计算机。
1. 环境搭建
1.1 安装 Anaconda
Anaconda 是一个 Python 发行版,自带 Python 解释器、Jupyter Notebook、conda 包管理器,以及大量预装的数据科学库。强烈建议新手直接用 Anaconda,省去逐个安装依赖的麻烦。
- 到 Anaconda 官网下载安装包(Windows 选 64-Bit Graphical Installer)。
- 安装时勾选 Add Anaconda3 to my PATH(可选,方便命令行使用)。
- 安装完成后,打开 Anaconda Prompt,输入
python --version验证。
2026 年版本建议:Python 3.12 / 3.13(生态兼容性最好)。若 Anaconda 默认版本过旧,可用 conda create -n tcm_nano python=3.13 新建环境(详见第 04 章)。
1.2 Jupyter Notebook 与 VS Code
- Jupyter Notebook:交互式写代码,适合学习与探索。启动方式:Anaconda Navigator 里点 Launch,或命令行输入
jupyter notebook。 - VS Code:专业代码编辑器。安装 Python 插件后,可运行
.py脚本、调试、写 Jupyter。
学习建议:学语法阶段用 Jupyter(逐行运行、即时反馈);写正式脚本用 VS Code。
1.3 第一个程序
print("Hello, 南方医科大学!")
运行输出:
Hello, 南方医科大学!
print() 是 Python 内置的输出函数,后面会反复用到。
2. 变量与数据类型
2.1 变量
Python 变量不需要声明类型,直接赋值即可。变量名规则:字母、数字、下划线组成,不能以数字开头,不能是关键字(如 if、for、class)。
# 变量赋值 age = 25 name = "小明" height = 1.75 is_student = True # 同时给多个变量赋值 a, b, c = 1, 2, 3 # 交换两个变量的值(Python 特色写法) a, b = b, a print(a, b) # 2 1
2.2 六大基本数据类型
| 类型 | 说明 | 示例 | 是否可变 |
|---|---|---|---|
int 整数 |
没有小数部分 | 42 |
不可变 |
float 浮点数 |
有小数部分 | 3.14 |
不可变 |
str 字符串 |
文本 | "中药" |
不可变 |
bool 布尔 |
真/假 | True / False |
不可变 |
list 列表 |
有序、可修改 | [1, 2, 3] |
可变 |
tuple 元组 |
有序、不可修改 | (1, 2, 3) |
不可变 |
dict 字典 |
键值对 | {"name": "小明"} |
可变 |
set 集合 |
无序、不重复 | {1, 2, 3} |
可变 |
# 列表(list):有序、可增删改 drugs = ["丹参", "川芎", "黄芪"] drugs.append("三七") # 末尾追加 drugs[0] = "人参" # 修改 print(drugs) # ['人参', '川芎', '黄芪', '三七'] # 元组(tuple):不可修改,常用于固定结构 coords = (116.4, 39.9) # coords[0] = 1 # 报错:tuple 不可修改 # 字典(dict):键值对 target = {"name": "AKT1", "uniprot": "P31749"} print(target["name"]) # AKT1 # 集合(set):去重 genes = {"TP53", "AKT1", "TP53"} print(genes) # {'TP53', 'AKT1'} —— 自动去重
科研中最高频的是 list 和 dict:基因列表、样本列表用 list;样本-分组映射、参数配置用 dict。
2.3 类型转换与类型判断
# 类型转换 int("42") # 42 float("3.14") # 3.14 str(42) # "42" list("abc") # ['a', 'b', 'c'] # 类型判断 type(42) # <class 'int'> isinstance(42, int) # True
3. 运算符
| 类别 | 运算符 | 示例 |
|---|---|---|
| 算术 | + - * / // % ** |
7 // 2 = 3(整除)、7 % 2 = 1(取余)、2 ** 3 = 8(幂) |
| 比较 | == != > < >= <= |
3 > 2 → True |
| 逻辑 | and or not |
True and False → False |
| 赋值 | = += -= *= /= |
x += 1 等价于 x = x + 1 |
| 成员 | in not in |
"丹参" in drugs → True |
# 算术 print(7 // 2) # 3 整除 print(7 % 2) # 1 取余(判断奇偶常用) print(2 ** 3) # 8 幂 # 比较与逻辑 age = 20 print(age >= 18 and age < 60) # True print(not age < 18) # True # 成员判断 targets = ["AKT1", "TP53", "EGFR"] print("AKT1" in targets) # True
️ == 与 = 的区别:= 是赋值(把值存进变量),== 是比较(判断是否相等)。这是新手最高频错误之一。
4. 输入输出
4.1 print 输出
name = "小明" age = 25 # 逗号分隔(自动加空格) print("姓名:", name, "年龄:", age) # f-string(推荐,最常用) print(f"姓名: {name}, 年龄: {age}") # 控制格式:保留两位小数 pi = 3.14159 print(f"π ≈ {pi:.2f}") # π ≈ 3.14 # 不换行 / 指定分隔符 print("a", "b", "c", sep="-", end="!\n") # a-b-c!
4.2 input 输入
input() 返回的永远是字符串,需要数字时记得转换。
age_str = input("请输入年龄: ") # 输入 25 age = int(age_str) # 转成整数 print(f"明年你 {age + 1} 岁")
5. 流程控制
5.1 条件判断 if-elif-else
score = 85 if score >= 90: grade = "A" elif score >= 80: grade = "B" elif score >= 70: grade = "C" else: grade = "D" print(f"成绩等级: {grade}") # B
判断顺序很重要:从上往下,满足第一个条件就停止。范围判断时从小到大或从大到小写清楚。
5.2 for 循环
# 遍历列表 drugs = ["丹参", "川芎", "黄芪"] for d in drugs: print(d) # 配合 range 生成数字序列 for i in range(5): # 0,1,2,3,4 print(i) for i in range(1, 10, 2): # 1,3,5,7,9(起始、结束、步长) print(i) # enumerate:同时拿到索引和值(科研常用) for idx, d in enumerate(drugs): print(idx, d)
5.3 while 循环
# 猜数字游戏逻辑 count = 0 while count < 3: print(f"第 {count + 1} 次尝试") count += 1
5.4 break / continue
# break:跳出整个循环 for i in range(10): if i == 5: break print(i) # 0 1 2 3 4 # continue:跳过本次,继续下一次 for i in range(5): if i == 2: continue print(i) # 0 1 3 4
️ while 死循环:while True: 必须配合 break 使用,否则程序永远不结束。
6. 函数
6.1 定义与调用
def greet(name): """返回问候语(docstring,函数说明)""" return f"你好, {name}!" msg = greet("小明") print(msg) # 你好, 小明!
6.2 参数类型(重点)
# 位置参数:按顺序传 def add(a, b): return a + b add(1, 2) # 3 # 关键字参数:按名字传(顺序可乱) add(b=2, a=1) # 3 # 默认参数:不传时用默认值 def power(x, n=2): return x ** n power(3) # 9 power(3, 3) # 27 # 不定长参数 *args:接收任意多个位置参数(元组) def total(*args): return sum(args) total(1, 2, 3, 4) # 10 # 不定长关键字参数 **kwargs:接收任意多个关键字参数(字典) def show_info(**kwargs): for k, v in kwargs.items(): print(f"{k}: {v}") show_info(name="小明", age=25)
️ 默认参数的坑:默认参数不要用可变对象(如 def f(x=[])),否则多次调用会共享同一个列表。正确写法是 def f(x=None): if x is None: x = []。
6.3 返回值
def calc(a, b): return a + b, a - b # 返回元组 s, d = calc(10, 3) # 解包 print(s, d) # 13 7
6.4 作用域:局部变量与全局变量
x = 10 # 全局变量 def change(): global x # 声明使用全局变量 x = 20 change() print(x) # 20(被修改了)
函数内默认只能读全局变量,要修改必须用 global。但科研代码中尽量少用全局变量,多用参数和返回值传递数据,代码更清晰。
7. 模块与包
7.1 import 的三种写法
# 方式一:导入整个模块 import math print(math.sqrt(16)) # 4.0 # 方式二:导入特定函数 from math import sqrt, pi print(sqrt(16), pi) # 4.0 3.141592653589793 # 方式三:起别名(科研代码常用,如 numpy as np) import numpy as np import pandas as pd
7.2 常用标准库速览
| 模块 | 用途 | 示例 |
|---|---|---|
os |
操作系统路径、文件 | os.path.join("data", "a.csv") |
sys |
系统参数 | sys.version |
math |
数学函数 | math.sqrt, math.log |
random |
随机数 | random.randint(1, 10) |
datetime |
日期时间 | datetime.date.today() |
json |
JSON 读写 | json.dumps(data) |
re |
正则表达式 | re.findall(r"\d+", s) |
import os # 路径拼接(跨平台,不要用字符串 + "/") path = os.path.join("data", "raw", "exp.csv") print(path) # data\raw\exp.csv(Windows 下) # 判断文件是否存在 print(os.path.exists(path)) # False
8. 文件操作
8.1 读取文件(推荐 with 写法)
# 方式一:with 自动关闭文件(推荐) with open("data.txt", "r", encoding="utf-8") as f: content = f.read() # 一次性读全部 print(content) # 方式二:逐行读取(大文件用这个) with open("data.txt", "r", encoding="utf-8") as f: for line in f: print(line.strip()) # strip() 去掉行尾换行符 # 方式三:readlines 返回行列表 with open("data.txt", "r", encoding="utf-8") as f: lines = f.readlines()
️ 编码问题:读写中文文件务必指定 encoding="utf-8",否则 Windows 下默认 GBK 会乱码或报错。
8.2 写入文件
# 写入(w 覆盖) / 追加(a) with open("output.txt", "w", encoding="utf-8") as f: f.write("第一行\n") f.write("第二行\n") with open("output.txt", "a", encoding="utf-8") as f: f.write("追加的一行\n")
8.3 科研场景:批量处理文件
import os data_dir = "data" for filename in os.listdir(data_dir): if filename.endswith(".csv"): path = os.path.join(data_dir, filename) print(f"处理文件: {path}") # 后续用 pandas 读取(见第 03 章)
9. 异常处理
9.1 try-except 基本结构
try: num = int("abc") # 这里会报错 except ValueError as e: print(f"转换失败: {e}")
9.2 完整结构:try-except-else-finally
try: f = open("data.txt", "r") content = f.read() except FileNotFoundError: print("文件不存在,请检查路径") except Exception as e: print(f"其他错误: {e}") else: print("读取成功") # 无异常时执行 finally: print("无论是否出错都会执行") # 常用于关闭资源
9.3 常见异常类型速查
| 异常 | 触发场景 |
|---|---|
ValueError |
类型对但值不对,如 int("abc") |
TypeError |
类型不匹配,如 "1" + 2 |
IndexError |
索引越界,如 [1,2][5] |
KeyError |
字典键不存在 |
FileNotFoundError |
文件不存在 |
ZeroDivisionError |
除零 |
ImportError |
导入模块失败 |
科研代码中,不要用空 except: 吞掉所有错误——那样错误会被隐藏,很难调试。至少要 except Exception as e: print(e)。
10. 面向对象基础
10.1 类与对象
class Sample: """一个简单的样本类""" def __init__(self, name, group, value): self.name = name # 实例属性 self.group = group self.value = value def describe(self): # 实例方法 return f"{self.name} ({self.group}): {self.value}" # 创建对象 s1 = Sample("S001", "control", 3.2) s2 = Sample("S002", "model", 5.8) print(s1.describe()) # S001 (control): 3.2
10.2 继承(了解)
class Animal: def speak(self): return "..." class Dog(Animal): # 继承 Animal def speak(self): return "汪汪" print(Dog().speak()) # 汪汪(覆盖父类方法)
10.3 封装与多态(了解)
- 封装:把数据和操作数据的方法放在类里,外部通过方法访问(如
s1.describe())。 - 多态:不同对象对同一方法名给出不同实现(如
Animal.speak与Dog.speak)。
面向对象在科研中不要求精通,但必须能读懂——因为很多库(如 scikit-learn 的模型、matplotlib 的 Figure)都是类。
11. 补充:科研高频语法
以下内容为清单之外的补充,但都是科研代码中每天都会遇到的写法。
11.1 常用内置函数
# enumerate:带索引遍历 for i, g in enumerate(["TP53", "AKT1"]): print(i, g) # zip:并行遍历多个列表 names = ["丹参", "川芎"] vals = [3.2, 5.8] for n, v in zip(names, vals): print(n, v) # sorted:排序(返回新列表) nums = [3, 1, 2] print(sorted(nums)) # [1, 2, 3] print(sorted(nums, reverse=True)) # [3, 2, 1] # map:对每个元素应用函数 print(list(map(int, ["1", "2", "3"]))) # [1, 2, 3] # filter:按条件过滤 print(list(filter(lambda x: x > 2, [1, 2, 3, 4]))) # [3, 4] # len / type / isinstance / range
11.2 推导式(重点,科研高频)
# 列表推导式:快速生成新列表 squares = [x ** 2 for x in range(5)] print(squares) # [0, 1, 4, 9, 16] # 带条件的列表推导式 evens = [x for x in range(10) if x % 2 == 0] print(evens) # [0, 2, 4, 6, 8] # 字典推导式 square_dict = {x: x ** 2 for x in range(3)} print(square_dict) # {0: 0, 1: 1, 2: 4} # 集合推导式(自动去重) unique = {len(w) for w in ["ab", "abc", "ab"]} print(unique) # {2, 3} # 三元表达式(条件表达式) status = "成人" if age >= 18 else "未成年"
列表推导式是科研代码的"灵魂"——一行顶一个 for 循环,且运行更快。务必熟练掌握。
11.3 lambda 匿名函数
# 普通函数 def double(x): return x * 2 # 等价 lambda double = lambda x: x * 2 # 常配合 sorted 的 key 参数使用 data = [("丹参", 3.2), ("川芎", 5.8), ("黄芪", 1.5)] data_sorted = sorted(data, key=lambda t: t[1], reverse=True) print(data_sorted) # [('川芎', 5.8), ('丹参', 3.2), ('黄芪', 1.5)]
11.4 迭代器与生成器
# 生成器:用 yield,逐个产出,节省内存(处理大文件/大数据时关键) def gen_squares(n): for i in range(n): yield i ** 2 for s in gen_squares(3): print(s) # 0 1 4 # 生成器表达式(类似列表推导式,但用圆括号) total = sum(x ** 2 for x in range(5)) print(total) # 30
11.5 字符串常用方法
s = " gene_symbol " s.strip() # "gene_symbol"(去首尾空白) s.lstrip() # 去左边 s.rstrip() # 去右边 s.replace("gene", "protein") # 替换 s.split("_") # ['gene', 'symbol'](按分隔符拆分) "_".join(["a", "b"]) # "a_b"(拼接) s.upper() / s.lower() # 大小写 s.startswith("g") / s.endswith("l") # 判断开头/结尾
科研中清洗基因名、样本名时,strip / replace / split / join 是最高频的四个方法。
11.6 深拷贝 vs 浅拷贝
import copy a = [1, [2, 3]] b = a.copy() # 浅拷贝:只复制外层,内层列表仍共享 b[1][0] = 99 print(a) # [1, [99, 3]] —— a 也被改了! c = copy.deepcopy(a) # 深拷贝:完全独立 c[1][0] = 0 print(a) # [1, [99, 3]] —— a 不受影响
️ 这是非常隐蔽的坑:list.copy()、dict.copy()、切片 a[:] 都是浅拷贝。嵌套结构要修改时务必用 copy.deepcopy。
11.7 正则表达式 re 模块(科研文本清洗)
import re text = "样本 S001 浓度 3.2 mg/mL,样本 S002 浓度 5.8 mg/mL" # 提取所有数字 nums = re.findall(r"\d+\.?\d*", text) print(nums) # ['001', '3.2', '002', '5.8'] # 提取样本编号 ids = re.findall(r"S\d+", text) print(ids) # ['S001', 'S002'] # 替换 clean = re.sub(r"\s+", " ", text) # 多个空格合并为一个
正则表达式入门只需掌握:\d 数字、\w 字母数字下划线、\s 空白、+ 一个或多个、* 零个或多个、? 零个或一个、[] 字符集、. 任意字符。
11.8 装饰器(了解即可)
# 装饰器:在不改原函数的情况下增加功能(如计时) import time def timer(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) print(f"{func.__name__} 耗时 {time.time() - start:.4f}s") return result return wrapper @timer def slow_add(a, b): time.sleep(0.1) return a + b print(slow_add(1, 2))
读别人代码时遇到 @xxx 就是装饰器。科研中常用 @timer 计时、@staticmethod / @classmethod 定义类方法。了解即可,不要求会写。
12. 常见坑
| 坑 | 说明 | 正确做法 |
|---|---|---|
= 与 == 混淆 |
赋值 vs 比较 | 判断相等用 == |
| 缩进错误 | Python 用缩进表示代码块 | 统一用 4 个空格 |
| 默认参数用可变对象 | def f(x=[]) 共享列表 |
用 x=None |
| 浅拷贝改嵌套 | a.copy() 内层仍共享 |
用 copy.deepcopy |
| 中文乱码 | 未指定编码 | encoding="utf-8" |
| 索引从 0 开始 | [1,2,3][0] 是 1 |
记住 0-based |
| 字符串不可变 | s[0] = "x" 报错 |
用 s.replace 或拼接 |
空 except: |
吞掉错误难调试 | 至少 except Exception as e |
13. 练习与交付物
交付物(必做)
综合练习脚本:新建 practice_basics.py,包含以下内容,全部加注释:
- 变量与数据类型:定义一个样本字典(含样本名、分组、浓度),打印所有键值。
- 流程控制:遍历 1–100,把能被 3 整除的数存入列表。
- 函数:写一个
calc_stats(numbers)函数,返回均值、最大值、最小值(用sum/max/min)。 - 文件读写:把上面的统计结果写入
stats.txt。 - 异常处理:尝试把用户输入的字符串转成数字,捕获
ValueError。 - 简单类:定义
Sample类(含__init__和describe方法),创建 3 个对象并打印。
进阶练习(检验补充知识点)
- 用列表推导式生成 10 个随机数(
random.randint),筛选出大于 5 的。 - 用 lambda + sorted 按浓度降序排列样本列表。
- 用生成器计算 1 到 100 的平方和(
sum(x**2 for x in range(101)))。 - 用 re 从一段文本中提取所有邮箱地址。
14. 速查表
| 需求 | 代码 |
|---|---|
| 输出 | print(f"{x:.2f}") |
| 输入 | x = int(input("...")) |
| 条件 | if a > b: ... elif: ... else: ... |
| 循环 | for i in range(n): / for x in lst: |
| 带索引循环 | for i, x in enumerate(lst): |
| 并行循环 | for a, b in zip(list1, list2): |
| 列表推导 | [f(x) for x in lst if cond] |
| 匿名函数 | lambda x: x * 2 |
| 排序 | sorted(lst, key=lambda x: x[1], reverse=True) |
| 读文件 | with open(p, "r", encoding="utf-8") as f: |
| 写文件 | with open(p, "w", encoding="utf-8") as f: f.write(...) |
| 异常 | try: ... except ValueError as e: print(e) |
| 导入 | import numpy as np |
上一章:00 · 学习路径与总览 | 下一章:02 · NumPy 数值计算基础