Python 入门篇:前端开发者的 AI 预备课
本篇按前端开发者的习惯对照 JavaScript,把 Python 环境、核心语法、模块、文件和日志过一遍,为后面写训练脚本、调 NumPy、PyTorch 打底。读完应能独立跑通脚本、管好虚拟环境、读写文件。
Python 是一种高级、解释型、面向对象的编程语言,由 Guido van Rossum 于 1991 年首次发布。语法靠缩进划分代码块,标准库和科学计算生态完整,是机器学习与 AI 工程的主流语言。
学习准备
Anaconda 安装
Anaconda 是面向数据分析、机器学习、科学计算的 Python、R 发行版,自带解释器、常用库和环境管理。
两大块:
- 预装科学计算库:NumPy、SciPy、Pandas、Matplotlib、Seaborn、Scikit-learn,以及 Jupyter Notebook。
- conda:包管理 + 环境管理。除了 Python 包,还能装 C、C++ 库、CUDA、MKL 等非 Python 依赖,并创建互相隔离的虚拟环境。
只想要轻量环境时,可以用 Miniconda(只有 conda 和 Python,库按需再装),或标准库自带的 venv。本篇以 Anaconda 为例。
安装步骤(以 macOS Apple silicon 为例;Windows、Linux 在同一页选对应安装包):
- 打开 https://www.anaconda.com/download
- 在 Download Now 里点 Skip Registration,下载当前系统的 Anaconda Distribution 图形安装包(不要死记某个日期的文件名,以官网当时提供的为准)
- 按安装向导完成
- 打开终端,能打出版本号即成功:
conda --version
python --version
Jupyter Notebook、Lab
Jupyter Notebook 是网页里的交互式编辑器:代码、说明文字、运行结果写在同一份 .ipynb 里,按单元格分段执行。适合数据分析、画图、调模型、写带结果的笔记。
JupyterLab 是其后继界面,带文件树、多标签和终端,现在更常用。Anaconda 两者都带:
jupyter notebook # 经典 Notebook
jupyter lab # JupyterLab(推荐)
第一个 Python 程序
在 Jupyter 中运行
- 打开 Anaconda Navigator,在 JupyterLab 上点 Launch。
- 浏览器打开
http://localhost:8888/lab(端口以终端提示为准)。 - 在 Notebook → Python(conda env:base)新建
Untitled.ipynb。 - 在单元格里写代码,点 Run,结果出现在单元格下方。
print("Hello,Jupyter Lab!")
name = "AI!"
print(f"I am {name}。")
在 Notebook 里跑同目录的 .py 文件:
# hello.py 需与当前 Notebook 在同一目录
%run hello.py
在终端运行 .py 文件
python hello.py
核心语法
变量与数据类型
Python 是动态类型:赋值即创建变量,不必先声明类型。
| 类型 | Python | JS 对照 | 示例 |
|---|---|---|---|
| 字符串 | str |
string |
"hello" |
| 整数 | int |
number |
42 |
| 浮点数 | float |
number |
3.14 |
| 布尔值 | bool |
boolean |
True、False |
| 空值 | NoneType |
null、undefined |
None |
| 列表 | list |
array |
[1, 2, 3] |
| 字典 | dict |
object、Map |
{"key": "value"} |
| 元组 | tuple |
无直接对应(不可变序列) | (1, 2, 3) |
| 集合 | set |
Set |
{1, 2, 3} |
与 JavaScript 的关键差异:
- 没有
let、const、var,直接name = "…"。 - 布尔是
True、False,空值是None,首字母都大写。 - 语句末尾不必写分号(写了也不报错)。
- 用缩进表示代码块,不用
{}。
name = "前端开发者" # str
age = 28 # int
height = 1.75 # float
is_learning = True # bool
result = None # 空值
ls = [10, 20, "hello", True, 3.14]
tup = (1, 2, "python", False)
d = {
"name": "张三",
"age": 25,
"score": 95.5,
}
类型查看与转换:
print(type(name)) # <class 'str'>
print(type(age)) # <class 'int'>
print(type(is_learning)) # <class 'bool'>
str_age = str(age) # "28"
int_height = int(height) # 1(向 0 截断,不是四舍五入)
float_age = float(age) # 28.0
bool_value = bool(age) # 非 0 → True;0、""、[]、None → False
字符串
单引号、双引号都可以(没有 JS 那种反引号字符串)。带变量用 f"…{expr}…",对应 JS 的模板字面量。
hello = "Hello"
word = "World"
message = f"{hello},{word}"
print(message)
multi_line = """
这是第一行
这是第二行
这是第三行
"""
print(multi_line)
常用方法:
upper()、lower():全大写、全小写。capitalize():首字母大写,其余小写。title():每个单词首字母大写。swapcase():大小写对调。strip([chars])、lstrip、rstrip:去首尾(或左、右)字符,默认去空格、\t、\n。find、rfind:找子串,返回下标,找不到返回-1。index、rindex:同上,找不到抛ValueError。count(sub, start, end):统计出现次数。replace(old, new, count):替换,count限制次数。zfill(width):左侧补 0。center、ljust、rjust:对齐并填充。expandtabs(tabsize):把\t展开成空格。split、rsplit、splitlines:切割。partition、rpartition:按第一次、最后一次分隔符切成三元组。str.join(iterable):用当前字符串作分隔符拼接。startswith、endswith:前缀、后缀判断。isdigit、isalpha、isalnum、isspace、islower、isupper、istitle:字符类别判断。isdigit()只认0-9。
s = " Hello Python "
s.lower()
s.upper()
s.capitalize()
s.title()
s.swapcase()
s.strip()
s.lstrip()
s.rstrip()
s.isdigit()
s.isalpha()
s.isalnum()
s.isspace()
s.startswith("He")
s.endswith("on")
msg = "hello python hello"
msg.find("hello")
msg.index("hello")
msg.rfind("hello")
msg.rindex("hello")
msg.count("hello")
data = "lizhao,杭州,下雨"
lst = data.split(",")
",".join(lst)
"5".zfill(3) # "005"
"hello".center(10)
"hello".ljust(10)
"hello".rjust(10)
切片:s[start:end:step],左闭右开。越界不报错,自动截断。
s = "ABCDEFGHIJ"
print(s[1:4]) # BCD
print(s[0:3]) # ABC
print(s[:4]) # ABCD
print(s[4:]) # EFGHIJ
print(s[:]) # 复制整串
print(s[::2]) # ACEGI
print(s[1::2]) # BDFHJ
print(s[-3:]) # HIJ
print(s[-5:-2]) # FGH
print(s[::-1]) # 逆序
print(s[8:2:-1])
print(s[2:100]) # 越界截断
注意:字符串不可变,这些方法都返回新串,不改原串。
列表
列表可混放任意类型,用下标读写。负数下标从末尾数,JS 数组没有这种写法。
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", True, None]
print(numbers[0]) # 1
print(numbers[-1]) # 5
print(numbers[-2]) # 4
numbers[2] = 99
print(numbers)
常用方法:
append(item):末尾追加一个元素。insert(index, item):在下标处插入。extend(iterable):把另一个可迭代对象的元素逐个接上。pop(index):按下标删除并返回;默认删最后一个。remove(item):删第一个匹配值,没有则报错。clear():清空,留下[]。lst[index] = 值:按下标改值。index(item, start, end):查下标,找不到报错。count(item):计数。sort()、sort(reverse=True):原地排序。reverse():原地反转。copy():浅拷贝(等价lst[:])。lst[start:end]:切片,得到新列表。in、not in:是否包含。+、*:拼接、重复,返回新列表。len(lst):长度。
lst = [10, 20, 30]
lst.append(40)
lst.insert(1, 15)
lst.extend([50, 60])
lst.index(20)
lst.count(10)
lst.pop()
lst.remove(15)
lst.reverse()
lst.sort()
lst.sort(reverse=True)
lst.copy()
lst.clear()
lst[0] = 99
a = [1, 2]
b = [3, 4]
print(a + b)
print(a * 3)
print(2 in a)
注意:append、extend、insert、pop、remove、clear、sort、reverse 改原列表;index、count、copy 以及 +、切片不改原列表。
列表推导式:一行完成映射 + 过滤,对应 JS 的 map、filter。
numbers = [1, 2, 3, 4, 5, 6]
doubled = [n * 2 for n in numbers]
# JS: numbers.map(n => n * 2)
evens = [n for n in numbers if n % 2 == 0]
# JS: numbers.filter(n => n % 2 === 0)
result = [n * 2 for n in numbers if n % 2 == 0]
# JS: numbers.filter(n => n % 2 === 0).map(n => n * 2)
切片规则与字符串相同:
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numbers[2:6]) # [2, 3, 4, 5]
print(numbers[:4]) # [0, 1, 2, 3]
print(numbers[6:]) # [6, 7, 8, 9]
print(numbers[-4:]) # [6, 7, 8, 9]
print(numbers[::2]) # [0, 2, 4, 6, 8]
print(numbers[::-1]) # 反转
元组
元组是不可变序列,写成 (1, 2, 3)。能下标、切片、解包,不能 append 或按位赋值。适合做函数多返回值、字典的键(元素本身也必须不可变)。只有一个元素时必须写逗号:(1,),否则 (1) 只是加了括号的整数。
tup = (1, 2, "python")
a, b, c = tup
print(tup[0], len(tup))
字典
字典是键值对,接近 JS 对象。键必须是不可变类型:字符串、数字、元组。
常用方法:
dict[key] = value:没有则新增,有则覆盖。update(...):批量写入,可传入字典或关键字参数。setdefault(key, 默认值):已有键则不动;没有则写入默认值,并返回对应 value。dict[key]:取值,键不存在抛KeyError。get(key, 默认值):键不存在返回None或默认值,不抛错。keys()、values()、items():键、值、键值对视图。pop(key, [默认值]):删除并返回 value;无默认值且键不存在则报错。popitem():删除并返回最后插入的(key, value)(Python 3.7+ 字典保序)。clear():清空。copy():浅拷贝。
user = {
"name": "lizhao",
"city": "杭州",
"weather": "台风",
}
user["job"] = "前端开发"
user["name"]
user.get("weather")
user.get("job", "未知")
job = user.pop("job")
del user["city"]
user.update({"company": "ABC"})
user.setdefault("city", "杭州")
user.setdefault("name", "李兆") # 已有 name,不改
print(list(user.keys()))
print(list(user.values()))
print(list(user.items()))
print("name" in user)
print("phone" not in user)
user.clear()
遍历:
user = {
"name": "lizhao",
"city": "杭州",
"weather": "台风",
}
for key, value in user.items():
print(f"{key}: {value}")
for key in user.keys():
print(key)
for value in user.values():
print(value)
字典推导式:
squares = {x: x**2 for x in range(5)}
print(squares)
numbers = [1, 2, 3, 4, 5]
even_squares = {x: x**2 for x in numbers if x % 2 == 0}
print(even_squares)
集合
集合是不重复、无序的元素堆,写法 {1, 2, 3},对应 JS 的 Set。空集合必须写 set(),{} 是空字典。支持 | 并、& 交、- 差。
s = {1, 2, 2, 3}
s.add(4)
print(s) # {1, 2, 3, 4}
print(2 in s)
运算符
| 操作 | JavaScript | Python | ||
|---|---|---|---|---|
| 相等 | === |
==(值相等,不随便做 JS 那种类型强制转换) |
||
| 同一对象 | ===(引用类型) |
is |
||
| 不相等 | !== |
!=、is not |
||
| 比较 | > < >= <= |
相同 | ||
| 逻辑与或非 | && `\ |
\ | `! |
and、or、not |
| 成员 | arr.includes(x) |
x in list |
判断是否为 None 用 is None、is not None,不要写 == None。
a = 10
b = 20
if a > 5 and b < 30:
print("两个条件都成立")
if a > 15 or b > 15:
print("至少一个条件成立")
if not a > 15:
print("a 不大于 15")
fruits = ["苹果", "香蕉"]
if "苹果" in fruits:
print("有苹果")
if "西瓜" not in fruits:
print("没有西瓜")
条件判断
与 JS 相近,两处不同:分支关键字是 elif,代码块靠缩进而不是 {}。
score = 85
if score >= 90:
grade = "优秀"
elif score >= 80:
grade = "良好"
elif score >= 60:
grade = "及格"
else:
grade = "不及格"
print(grade)
循环
for 接近 JS 的 for...of,直接遍历元素。需要下标时用 enumerate。没有 count++,要写 count += 1。
ls = ["A", "B", "C"]
for e in ls:
print(e)
for i, e in enumerate(ls):
print(f"第{i + 1}个是{e}")
count = 0
while count < 5:
print(count)
count += 1
函数
用 def 定义。pass 表示空函数体,相当于 JS 的空 {}。return a, b, c 实际返回一个元组,可以解包。
def greet(name):
return f"你好,{name}!"
print(greet("前端"))
def placeholder():
pass
def get_user():
return "lizhao", "杭州", "下雨"
name, city, weather = get_user()
print(name, city, weather)
模块导入
模块(Module) 就是一个 .py 文件,可被其他文件 import。
执行 import xxx 时,解释器按 sys.path 查找,常见顺序是:
- 当前脚本所在目录(交互式则是启动目录)
- 环境变量
PYTHONPATH里的目录 - 标准库
site-packages(第三方包,相当于node_modules)
import math
print(math.pi)
print(math.sqrt(16))
print(math.pow(2, 3))
from math import sqrt, pi, pow
print(pi)
print(sqrt(16))
import numpy as np
import pandas as pd
data = np.array([1, 2, 3])
from math import sqrt as square_root
print(square_root(25))
from math import * 会把模块里的名字倒进当前命名空间,容易重名,只适合演示,正式代码用 import math 或按名导入。
内置模块
无需安装即可用:
os:操作系统、文件、环境变量sys:运行时、命令行参数math:数学函数json:JSON 读写datetime:日期时间random:随机数re:正则collections:Counter、deque、defaultdict等(JS 的Map、Set更接近内置dict、set)pathlib:面向对象的路径(见文件一节)
自定义模块
# utils.py
PI = 3.14159
def add(a, b):
return a + b
def multiply(a, b):
return a * b
def greet(name):
return f"你好,{name}!"
class Calculator:
@staticmethod
def subtract(a, b):
return a - b
import utils
print(utils.PI)
print(utils.add(3, 5))
print(utils.Calculator.subtract(10, 3))
包
包(Package) 是装着多个模块的目录。
Python 3.3 起,没有 __init__.py 的目录也可以当命名空间包导入。常规项目仍建议保留 __init__.py:标明这是一个包,并在里面写初始化、__all__(控制 from package import * 导出哪些名字)。
package/
├── __init__.py
├── math_utils.py
├── string_utils.py
└── data/
├── __init__.py
└── processor.py
import package.math_utils
result = package.math_utils.add(3, 5)
from package import math_utils
result = math_utils.add(3, 5)
from package.math_utils import add
result = add(3, 5)
from package.data.processor import process_data
安装第三方模块
pip 相当于 npm、yarn、pnpm。在 conda 环境里也可以用 conda install。
pip install requests
pip install numpy pandas
pip install flask
pip install pandas==1.5.0
pip freeze > requirements.txt
pip install -r requirements.txt
虚拟环境
pip install 默认装进当前解释器的全局(或当前已激活环境)。多个项目共用一套包会互相踩版本。虚拟环境给每个项目单独一份 Python 和依赖,作用接近每个前端项目自己的 node_modules。
用 conda:
conda create -n myenv python=3.11
conda activate myenv
conda install numpy pandas
conda deactivate
conda env list
不用 Anaconda 时,标准库写法是 python -m venv .venv,再激活后用 pip。
文件操作
读写文件用内置 open(),再配合 read()、write()。文本请显式写 encoding="utf-8",避免 Windows 默认编码把中文读乱。
打开文件
| 模式 | 说明 |
|---|---|
'r' |
只读(默认)。文件必须存在。 |
'w' |
写入。存在则清空覆盖,不存在则创建。 |
'a' |
追加。在末尾写,不存在则创建。 |
'x' |
独占创建。已存在则报错。 |
'r+' |
读写,文件必须存在。 |
'w+' |
读写,存在则覆盖,不存在则创建。 |
二进制再加 b,如 'rb'、'wb'。文本模式可写 'rt',t 可省略。
file = open("file.txt", "r", encoding="utf-8")
file = open("image.jpg", "rb")
文件读取
优先用 with,离开块时自动关闭,避免漏掉 close()。
with open("file.txt", "r", encoding="utf-8") as file:
content = file.read()
print(content)
# 不推荐:容易忘记 close
file = open("file.txt", "r", encoding="utf-8")
content = file.read()
print(content)
file.close()
逐行读:
# 遍历文件对象,适合大文件
with open("file.txt", "r", encoding="utf-8") as file:
for line in file:
print(line.strip())
# 一次读成列表
with open("file.txt", "r", encoding="utf-8") as file:
lines = file.readlines()
for line in lines:
print(line.strip())
# 自己控制何时读下一行
with open("file.txt", "r", encoding="utf-8") as file:
while True:
line = file.readline()
if not line:
break
print(line.strip())
读指定个数(文本模式下按字符,不是按字节):
with open("file.txt", "r", encoding="utf-8") as file:
chunk = file.read(100)
print(chunk)
文件写入
with open("output.txt", "w", encoding="utf-8") as file:
file.write("这是第一行内容\n")
file.write("这是第二行内容\n")
with open("output.txt", "a", encoding="utf-8") as file:
file.write("这是追加的第三行内容\n")
文件与路径
os.path 是函数式写法;pathlib.Path 更面向对象,新代码优先用它。
import os
from pathlib import Path
if os.path.exists("f.txt"):
print("文件存在")
print(os.path.isfile("file.txt"))
print(os.path.isdir("file.txt"))
file_path = Path("file.txt")
if file_path.exists():
print(file_path.is_file(), file_path.is_dir())
import os
from pathlib import Path
if not os.path.exists("data/output"):
os.makedirs("data/output")
Path("data/output").mkdir(parents=True, exist_ok=True)
import os
from pathlib import Path
print(os.path.join("data", "output", "result.txt"))
root = Path("data")
print(root / "output" / "result.txt")
异常处理(try、except)
运行期无法继续时,Python 会抛出异常。不捕获则程序中止并打印栈。
| 概念 | Python | JavaScript |
|---|---|---|
| 抛出 | raise |
throw |
| 捕获 | try、except |
try、catch |
| 总会执行 | finally |
finally |
| 无异常时 | else |
无直接对应 |
| 基类 | Exception |
Error |
| 自定义 | class MyError(Exception) |
class MyError extends Error |
try:
result = 10 / 0
except Exception as e:
print(f"发生了错误:{e}")
print(f"错误类型:{type(e).__name__}")
def divide_numbers(a, b):
try:
result = a / b
except ZeroDivisionError:
print("除零错误!")
return None
except TypeError:
print("类型错误!请传入数字")
return None
else:
print("除法计算成功")
return result
finally:
print("清理完成")
print(divide_numbers(10, 2))
print(divide_numbers(10, 0))
def validate_age(age):
if age < 0:
raise ValueError("年龄不能为负数!")
if age > 150:
raise ValueError("年龄不能超过150岁!")
return f"年龄:{age},有效"
try:
validate_age(-5)
except ValueError as e:
print(f"验证失败:{e}")
日志记录
logging 比 print 适合生产环境:能分级、同时打到控制台和文件,也能接到远程。
默认级别是 WARNING,DEBUG、INFO 不会出现。下面把级别设成 DEBUG 之后,五行都会输出。
import logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("app.log", encoding="utf-8"),
],
)
logging.debug("这是 DEBUG 级别")
logging.info("这是 INFO 级别")
logging.warning("这是 WARNING 级别")
logging.error("这是 ERROR 级别")
logging.critical("这是 CRITICAL 级别")
basicConfig 只在首次配置时生效;Notebook 里重复执行往往改不了已有配置,需要重启内核,或改用 logging.getLogger(name) 自己装 handler。
总结
Python 用缩进写块、动态类型,列表、字典、元组、集合对应前端最常用的几种结构。环境用 Anaconda 或 venv 隔离依赖,代码用 import 组织,文件用 with open 加 UTF-8,错误用 try、except,输出用 logging。这些够开始写 AI 相关的脚本;数值计算再接到 NumPy、Pandas、PyTorch。