📌 引言:不要重复发明轮子,但要学会怎么用轮子

想象一下:你想做个蛋糕,难道要从种小麦开始?当然不!你会去超市买现成的面粉、鸡蛋、糖。编程也一样——你想写个程序处理日期、发个网络请求、画个图表,难道都要自己从头写?Python 说:不用!别人早就写好了,打包成 模块,你直接拿来用就行。

Python 最强大的地方就是它庞大的生态系统。标准库自带“电池”,第三方库更是成千上万。学会导入和使用模块,你的编程能力将瞬间膨胀。

本章我们就来学习:

  • 如何导入和使用模块
  • 如何组织自己的模块成为包
  • Python 的模块搜索路径是怎么回事
  • 入口惯用法 if __name__ == '__main__' 是干嘛的
  • 标准库里那些常用模块的快速巡礼

准备好了吗?让我们一起站上巨人的肩膀!🚀


一、模块是什么?—— 一个 .py 文件就是一个模块

模块就是包含 Python 代码的 .py 文件。你可以把相关的函数、类、变量放在一个文件里,方便在其他地方导入使用。

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

PI = 3.14159

这个 mytools.py 就是一个模块。其他 Python 文件可以导入它,然后使用里面的函数和变量。

为什么用模块?

  • 代码复用:写一次,到处用
  • 命名空间:避免名字冲突(不同模块的同名函数互不影响)
  • 组织代码:把相关功能放在一起,清晰易维护

二、导入模块的四种姿势

Python 提供了多种导入语法,适应不同场景。


1️⃣ import module —— 最基础的方式

import math
print(math.sqrt(16))   # 4.0

用这种方式,你需要通过 模块名.函数名 来调用,避免了命名冲突。


2️⃣ from module import name —— 直接导入指定内容

from math import sqrt, pi
print(sqrt(16))   # 4.0
print(pi)         # 3.141592653589793

现在可以直接用函数名,不用加模块前缀。方便,但可能造成命名冲突。


3️⃣ import module as alias —— 给模块起个别名

import numpy as np
arr = np.array([1, 2, 3])

适合模块名很长或者你想避免命名冲突的情况。


4️⃣ from module import * —— 导入所有(谨慎使用!)

from math import *
print(sin(0))   # 0.0

这样会导入模块的所有公共内容,但不推荐,因为:

  • 你不知道导入了哪些名字,可能覆盖已有的变量
  • 代码可读性差,别人看不出函数来自哪个模块

🧠 冷知识:模块也是单例

一个模块无论被导入多少次,在整个解释器进程中只会被执行一次(第一次导入时)。后续的导入只是把已经加载的模块对象绑定到当前命名空间。所以不用担心重复导入的开销。


三、包:模块的集合

当模块越来越多,就需要用来组织。包就是一个包含 __init__.py 文件的目录(Python 3.3+ 可以没有 __init__.py,成为命名空间包,但传统包还是推荐保留)。

mypackage/
    __init__.py
    module1.py
    module2.py
    subpackage/
        __init__.py
        module3.py

导入包中的模块

import mypackage.module1
from mypackage import module2
from mypackage.subpackage import module3

__init__.py 的作用

  • 标识目录是 Python 包
  • 可以在里面写初始化代码,或者定义 __all__ 控制 from package import * 导入的内容

四、模块搜索路径:Python 去哪找模块?

当你执行 import xxx,Python 会按照以下顺序搜索:

  1. 当前目录(或者脚本所在目录)
  2. PYTHONPATH 环境变量中的路径
  3. 标准库目录
  4. 任何 .pth 文件定义的路径

所有这些路径存放在 sys.path 列表中:

import sys
print(sys.path)   # 打印模块搜索路径

你可以动态添加路径:

sys.path.append('/my/custom/path')

但更规范的做法是通过设置环境变量 PYTHONPATH,或者把模块安装到 site-packages。


五、入口惯用法:if __name__ == '__main__'

你肯定在很多 Python 脚本末尾看到过这个:

def main():
    print("程序入口")

if __name__ == '__main__':
    main()

这是干什么的?

每个 Python 模块都有一个内置属性 __name__

  • 当模块被直接运行时,__name__ 被设为 '__main__'
  • 当模块被导入时,__name__ 被设为模块名(如 'mytools'

所以这个判断可以让一段代码只在直接运行时执行,被导入时不执行。常用于:

  • 测试代码
  • 命令行入口
# test.py
def add(a, b):
    return a + b

if __name__ == '__main__':
    print("测试:2+3=", add(2, 3))

现在直接运行 python test.py 会执行测试;如果在其他文件导入 test,则不会打印测试信息。


🧠 冷知识:你也可以手动修改 __name__

虽然没人会这么干,但理论上你可以修改 __name__ 的值,影响它的行为。千万别在生产代码里这么玩。


六、常用内置模块巡礼

Python 标准库非常庞大,号称“内置电池”。这里精选一些最常用的,让你有个印象,用到时知道去哪查。


1️⃣ sys —— 系统相关

import sys
print(sys.version)       # Python 版本
print(sys.argv)          # 命令行参数列表
sys.exit(0)              # 退出程序

2️⃣ os —— 操作系统接口

import os
print(os.getcwd())       # 当前工作目录
os.mkdir('newdir')       # 创建目录
print(os.environ['PATH']) # 环境变量

3️⃣ math —— 数学函数

import math
print(math.pi)           # 3.141592653589793
print(math.sqrt(2))      # 1.4142135623730951
print(math.factorial(5)) # 120

4️⃣ random —— 随机数

import random
print(random.random())           # [0,1) 随机浮点数
print(random.randint(1, 10))     # 1-10 随机整数
print(random.choice(['a','b','c'])) # 随机选一个

5️⃣ datetime —— 日期时间

from datetime import datetime, timedelta
now = datetime.now()
print(now.strftime("%Y-%m-%d %H:%M:%S"))
tomorrow = now + timedelta(days=1)

6️⃣ json —— JSON 数据

import json
data = {'name': 'Alice', 'age': 25}
json_str = json.dumps(data)        # 序列化为 JSON 字符串
parsed = json.loads(json_str)      # 解析回 Python 对象

7️⃣ pickle —— Python 对象序列化(注意安全!)

import pickle
data = {'name': 'Alice', 'scores': [90, 85, 88]}
with open('data.pkl', 'wb') as f:
    pickle.dump(data, f)            # 存到文件
with open('data.pkl', 'rb') as f:
    loaded = pickle.load(f)         # 加载

⚠️ 安全警告:永远不要 pickle.load 不可信的数据,可能执行恶意代码。


8️⃣ collections —— 高级容器

from collections import Counter, defaultdict, deque

# Counter 计数
words = ['a','b','a','c','b','a']
print(Counter(words))        # Counter({'a':3, 'b':2, 'c':1})

# defaultdict 默认值
dd = defaultdict(int)        # 默认值 0
dd['count'] += 1

# deque 双端队列
dq = deque([1,2,3])
dq.appendleft(0)             # [0,1,2,3]

9️⃣ itertools —— 迭代器工具

from itertools import count, cycle, permutations

# count 无限计数
for i in count(10):    # 10,11,12...
    if i > 15: break

# cycle 无限循环
colors = cycle(['红','黄','蓝'])
for _ in range(5):
    print(next(colors))   # 红 黄 蓝 红 黄

# permutations 排列
print(list(permutations([1,2,3], 2)))  # [(1,2),(1,3),(2,1),(2,3),(3,1),(3,2)]

🔟 functools —— 高阶函数工具

from functools import partial, lru_cache

# partial 固定参数
def power(base, exp):
    return base ** exp
square = partial(power, exp=2)
print(square(5))   # 25

# lru_cache 缓存结果
@lru_cache(maxsize=128)
def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)

1️⃣1️⃣ argparse —— 命令行参数解析

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--name', default='world')
args = parser.parse_args()
print(f"Hello, {args.name}!")

1️⃣2️⃣ logging —— 日志

import logging
logging.basicConfig(level=logging.INFO)
logging.info("程序开始")
logging.error("出错了")

1️⃣3️⃣ pathlib —— 面向对象路径操作(3.4+)

from pathlib import Path
p = Path('/usr/bin/python3')
print(p.name)        # python3
print(p.parent)      # /usr/bin
print(p.exists())    # True

七、如何创建自己的模块和包

创建模块

随便写一个 .py 文件,比如 mymath.py

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

def multiply(a, b):
    return a * b

PI = 3.14

然后在同目录下的另一个文件中导入:

import mymath
print(mymath.add(5, 3))

创建包

创建目录结构:

mypkg/
    __init__.py
    stats.py
    utils.py

__init__.py 可以为空,也可以写包的初始化代码。

stats.py

def mean(data):
    return sum(data) / len(data)

utils.py

def echo(msg):
    return msg

在其他地方导入:

from mypkg import stats
print(stats.mean([1,2,3,4,5]))

八、第三方模块:pip install 大法

标准库虽好,但总有覆盖不到的需求。这时就需要第三方模块。用 pip 安装:

pip install requests

然后在代码里导入:

import requests
response = requests.get('https://api.github.com')
print(response.status_code)

常用的第三方库我们在第20章会详细介绍。


九、常见陷阱与冷知识

1️⃣ 模块重复导入只执行一次

如前所述,模块是单例。但如果你用 importlib.reload() 可以强制重新加载(开发调试用)。

2️⃣ __pycache__ 目录是什么?

为了提高加载速度,Python 会把编译后的字节码缓存到 __pycache__ 目录里,文件名如 module.cpython-39.pyc。可以安全删除,但会减慢下次启动速度。

3️⃣ 循环导入(circular import)

如果 a.py 导入 b.py,而 b.py 又导入 a.py,就会形成循环导入,可能导致错误。解决办法是重构代码,把公共部分抽到第三个模块,或者在函数内部导入。

4️⃣ from module import *__all__

模块可以定义 __all__ 列表,控制 from module import * 导入的内容。

# mymodule.py
__all__ = ['func1', 'CONST']

def func1(): pass
def func2(): pass   # 不会被 import * 导入
CONST = 100

✅ 本章总结

概念 说明
模块 一个 .py 文件
包含 __init__.py 的目录
导入方式 import, from ... import, as 别名
模块搜索路径 sys.path,可动态添加
if __name__ == '__main__' 判断是否直接运行
标准库常用模块 sys, os, math, random, datetime, json, pickle, collections, itertools, functools, argparse, logging, pathlib 等
第三方模块 pip install 安装
循环导入 避免,必要时重构

🚀 下集预告

掌握了模块和包,你已经能站在巨人的肩膀上写代码了。下一章我们将学习 文件操作——如何读写文件、操作路径、用 with 管理资源。这些是几乎所有程序都离不开的技能。

记得动手试试:自己写个小模块,包含几个函数,然后在另一个文件里导入使用。再试试用 argparse 写个带命令行参数的小工具。实践出真知!🔥


🔗 上一篇:函数(基础篇):写一次,用一辈子
🔗 下一篇:文件操作:与硬盘对话的艺术(待更新)


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐