01 · Python 基础语法

数据科学⭐⭐⭐⭐⭐核心必修|约 14 分钟15 节1 图
本章目标

能独立编写带注释、可复用的脚本,理解变量、流程控制、函数与面向对象的基本思想,为 NumPy / Pandas 打地基。

本章路线
01 · Python 基础语法

0. 小白导读:Python 到底是什么?

Python 是一种"编程语言"——语言是人和人交流的工具,编程语言就是人和计算机交流的工具。你写一句"人话",计算机按照规则执行,帮你干活。

  • 你不需要懂计算机内部原理,就像你不需要懂发动机原理也能开车。
  • 你只需要学会"用 Python 的语法表达你想做的事",剩下的交给计算机。
🍚生活化比喻

一个贯穿本章的比喻——做菜

  • 变量 = 贴了标签的保鲜盒(把食材放进盒子,贴上名字,随时取用)

  • 数据类型 = 不同的食材(整数像"3 个鸡蛋",字符串像"写着'鸡蛋'的标签",列表像"一篮食材")

  • 函数 = 一道菜谱(把固定的步骤打包,想用就调用,还能换食材)

  • 循环 = 流水线(同一个动作重复做 N 遍)

  • 异常处理 = 安全气囊(出问题时兜底,不让程序直接崩溃)

每学一个概念,回想一下这个比喻,会容易很多。


1. 环境搭建

1.1 安装 Anaconda

Anaconda 是一个 Python 发行版,自带 Python 解释器、Jupyter Notebook、conda 包管理器,以及大量预装的数据科学库。强烈建议新手直接用 Anaconda,省去逐个安装依赖的麻烦。

  1. 到 Anaconda 官网下载安装包(Windows 选 64-Bit Graphical Installer)。
  2. 安装时勾选 Add Anaconda3 to my PATH(可选,方便命令行使用)。
  3. 安装完成后,打开 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 第一个程序

python
print("Hello, 南方医科大学!")

运行输出:

text
Hello, 南方医科大学!
💡提示

print() 是 Python 内置的输出函数,后面会反复用到。


2. 变量与数据类型

Python 数据类型导图
图 01-1Python 数据类型导图

2.1 变量

🍚生活化比喻

先听懂:变量就像贴了标签的保鲜盒。你把一个值(比如数字 25)放进盒子里,在盒子上贴个名字(age),以后只要喊 age,就能拿到盒子里的 25。而且盒子里的东西随时可以换——再放一个 30 进去,age 就变成 30 了。

Python 变量不需要声明类型,直接赋值即可。变量名规则:字母、数字、下划线组成,不能以数字开头,不能是关键字(如 ifforclass)。

python
# 变量赋值
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 六大基本数据类型

🍚生活化比喻

先听懂:数据类型就是不同种类的"食材容器"

  • 列表(list) = 一篮食材,有顺序、能随时加菜/换菜(可增删改)。

  • 元组(tuple) = 一盒固定搭配的套餐,定死了不能换(不可修改)。

  • 字典(dict) = 一本"名字 → 信息"的电话簿,按名字查信息(键值对)。

  • 集合(set) = 一个自动去重的收纳袋,重复的东西放进去只剩一个。

类型 说明 示例 是否可变
int 整数 没有小数部分 42 不可变
float 浮点数 有小数部分 3.14 不可变
str 字符串 文本 "中药" 不可变
bool 布尔 真/假 True / False 不可变
list 列表 有序、可修改 [1, 2, 3] 可变
tuple 元组 有序、不可修改 (1, 2, 3) 不可变
dict 字典 键值对 {"name": "小明"} 可变
set 集合 无序、不重复 {1, 2, 3} 可变
python
# 列表(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 类型转换与类型判断

python
# 类型转换
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 > 2True
逻辑 and or not True and FalseFalse
赋值 = += -= *= /= x += 1 等价于 x = x + 1
成员 in not in "丹参" in drugsTrue
python
# 算术
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 输出

python
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() 返回的永远是字符串,需要数字时记得转换。

python
age_str = input("请输入年龄: ")     # 输入 25
age = int(age_str)                  # 转成整数
print(f"明年你 {age + 1} 岁")

5. 流程控制

5.1 条件判断 if-elif-else

python
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 循环

🍚生活化比喻

先听懂for 循环就像流水线传送带——传送带上每个物品(列表里的每个元素)经过你面前时,你就对它做一遍同样的动作,直到传送带走完。

python
# 遍历列表
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 循环

python
# 猜数字游戏逻辑
count = 0
while count < 3:
    print(f"第 {count + 1} 次尝试")
    count += 1

5.4 break / continue

python
# 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 定义与调用

🍚生活化比喻

先听懂:函数就像一道菜谱。你把一组固定的操作步骤打包成一个"菜谱"(函数),起个名字(函数名),以后只要喊这个名字就能执行整套餐步骤,不用每次都重新写一遍。还能"换食材"(传不同参数)做出不同口味。

python
def greet(name):
    """返回问候语(docstring,函数说明)"""
    return f"你好, {name}!"

msg = greet("小明")
print(msg)   # 你好, 小明!

6.2 参数类型(重点)

python
# 位置参数:按顺序传
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 返回值

python
def calc(a, b):
    return a + b, a - b     # 返回元组

s, d = calc(10, 3)          # 解包
print(s, d)                 # 13 7

6.4 作用域:局部变量与全局变量

python
x = 10          # 全局变量

def change():
    global x    # 声明使用全局变量
    x = 20

change()
print(x)        # 20(被修改了)
💡提示

函数内默认只能全局变量,要修改必须用 global。但科研代码中尽量少用全局变量,多用参数和返回值传递数据,代码更清晰。


7. 模块与包

7.1 import 的三种写法

python
# 方式一:导入整个模块
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)
python
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 写法)

python
# 方式一: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 写入文件

python
# 写入(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 科研场景:批量处理文件

python
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. 异常处理

🍚生活化比喻

先听懂:程序运行中出错,就像开车遇到突发状况。没有异常处理 = 没有安全气囊,一出事整个程序直接"撞毁"(崩溃退出);有了 try-except = 装了安全气囊,出事先兜住,程序还能继续跑。

9.1 try-except 基本结构

python
try:
    num = int("abc")        # 这里会报错
except ValueError as e:
    print(f"转换失败: {e}")

9.2 完整结构:try-except-else-finally

python
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. 面向对象基础

🍚生活化比喻

先听懂:面向对象就像"模具"和"产品"的关系class(类)是模具——规定了产品长什么样、有哪些属性;对象是用模具做出来的一个个具体产品。比如"Sample 类"是模具,s1s2 就是两个具体的样本对象,各有各的数据。

科研中面向对象主要用于封装实验流程自定义数据结构。理解基本概念即可,不需要精通。

10.1 类与对象

python
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 继承(了解)

python
class Animal:
    def speak(self):
        return "..."

class Dog(Animal):          # 继承 Animal
    def speak(self):
        return "汪汪"

print(Dog().speak())        # 汪汪(覆盖父类方法)

10.3 封装与多态(了解)

  • 封装:把数据和操作数据的方法放在类里,外部通过方法访问(如 s1.describe())。
  • 多态:不同对象对同一方法名给出不同实现(如 Animal.speakDog.speak)。
📌要点

面向对象在科研中不要求精通,但必须能读懂——因为很多库(如 scikit-learn 的模型、matplotlib 的 Figure)都是类。


11. 补充:科研高频语法

笔记

以下内容为清单之外的补充,但都是科研代码中每天都会遇到的写法。

11.1 常用内置函数

python
# 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 推导式(重点,科研高频)

python
# 列表推导式:快速生成新列表
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 匿名函数

python
# 普通函数
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 迭代器与生成器

python
# 生成器:用 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 字符串常用方法

python
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 浅拷贝

python
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 模块(科研文本清洗)

python
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 装饰器(了解即可)

python
# 装饰器:在不改原函数的情况下增加功能(如计时)
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. 变量与数据类型:定义一个样本字典(含样本名、分组、浓度),打印所有键值。
  2. 流程控制:遍历 1–100,把能被 3 整除的数存入列表。
  3. 函数:写一个 calc_stats(numbers) 函数,返回均值、最大值、最小值(用 sum / max / min)。
  4. 文件读写:把上面的统计结果写入 stats.txt
  5. 异常处理:尝试把用户输入的字符串转成数字,捕获 ValueError
  6. 简单类:定义 Sample 类(含 __init__describe 方法),创建 3 个对象并打印。

进阶练习(检验补充知识点)

  1. 列表推导式生成 10 个随机数(random.randint),筛选出大于 5 的。
  2. lambda + sorted 按浓度降序排列样本列表。
  3. 生成器计算 1 到 100 的平方和(sum(x**2 for x in range(101)))。
  4. 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 数值计算基础

配套学习资源

共 7 条 · 点击跳转