Skip to content

介绍

类似 js 的 es module

导入模块

python
# [from 模块名称] import [模块|类|变量|常量|函数|*(所有内容)] as 别名

# 1.导入整个模块
import 模块名称

# 2.导入模块的某个功能
from 模块名称 import 变量|函数|类等

# 3.导入整个模块的所有内容
from 模块名称 import *

# 4. 导入整个模块并重新命名
import 模块名称 as 别名

# 5.导入某个模块的功能并重新命名
from 模块名称 import 功能名 as 别名
python
from random import randint as get_random_number

rand_num = get_random_number(1, 9)
print(rand_num)

标准库模块

所谓标准库模块, 就是 python 解释器自带的模块, 只要安装了 python 就可以直接导入使用的模块 查看python 编译器标准库文档

python
import random
print(random.randint())

内置模块 __builtins__

这是标准库模块中特殊的一个模块, 它存放了一些 特别常用的 函数和类, 比如: print range len 等等, 解释器会自动的导入这个模块

python
# 我并没有手动 import builtins
# 就可以直接使用 __builtins__ 变量和 dir print 方法
for item in dir(__builtins__):
    print(f"{item} 来自于 builtins 模块")
python
import __builtin__

for item in dir(__builtin__):
    print(f"{item} 来自于 __builtin__ 模块")

自定义模块

  1. 创建文件 mod1 并输入以下内容
python
def mod1_test():
    print("mod1_test excuted")
  1. main.py 中导入
python
# 这个 模块名就是文件名(不包含.py 后缀) [mod1.py]
import mod1

mod1.mod1_test()

__name__ 常量

当直接执行的时候, __name__ 的值为 __main__, 当导入的时候, __name__ 的值就不是 __main__

python
# mod1.py 内容如下:
def mod1_test():
    print("mod1_test excuted")

# 使用 python mod1.py 直接运行会进入 if 中的代码
# 当在 main.py 中导入的时候 mod1.py 的时候不会执行
if __name__ == '__main__':
  mod1_test()

自定义包(package)

什么是 python package

包的本质就是一个目录, 并且含有 __init__.py 这个文件, 这个目录下可以有多个模块, 并且通过 __init__.py 来管理导出行为, 那么这个目录就是 python package

sh
# 创建对应文件
mkdir mypkg
touch mypkg/__init__.py
touch mypkg/mod1.py
touch mypkg/mod2.py

文件中代码如下:

python
# 执行 python main.py 查看结果
from mypkg import mod1
from mypkg import mod2

mod1.f1()
mod2.f2()
python
# 这个文件可以为空, 但是文件必须存在
python
def f1():
    print("我是 mod1 中的 f1 方法")
python
def f2():
    print("我是 mod2 中的 f2 方法")

导包的底层逻辑

1.第一次导入

  1. 将自己当前的命名空间中的代码执行
  2. 创建模块对象, 并将模块内所有的变量绑定到模块对象上
  3. 在 import 语句的位置, 引入被导入的变量到当前的命名空间

2.第n次导入

只要导入过一次, 那么后面的导入语句就只会执行第三个步骤

python
.
├── README.md
├── main.py
├── pkg1
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-312.pyc
│   │   └── example.cpython-312.pyc
│   └── example.py
├── pyproject.toml
└── uv.lock

3 directories, 8 files
python
from pkg1 import example_inst
# 第一次导入,执行 __init__.py 控制台输出: "__init__ executed"

import pkg1 as pkg2
import pkg1 as pkg3
# 后续导入不再执行 __init__.py, 只是将包中的变量导入到当前命名空间

def main():
    example_inst.test()
    pkg2.example_inst.test()
    pkg3.example_inst.test()


if __name__ == "__main__":
    main()

# 执行 uv run ./main.py 的输出如下:
# __init__ executed             说明 __init__ 执行了
# Example class init            说明 __init__ 执行了
# 没有多次输出 "__init__ executed", 说明只执行了一次
# Example test method exteuted, 说明: example_inst.test() 执行了
# Example test method exteuted, 说明: pkg2.example_inst.test() 执行了
# Example test method exteuted, 说明: pkg3.example_inst.test() 执行了
python
from .example import Example

print("__init__ executed")

# example 类的实例对象
example_inst = Example()
python
class Example:
    def __init__(self):
        print("Example class init")

    def test(self):
        print("Example test method exteuted")

sys.path 对导包的影响

通过代码我们发现, Python 导包的语句并不是类似js那样直接基于操作系统路径的, 那么他是如何找到这些代码的呢?

  1. 首先去找 builtins 模块, 看是否有
  2. 然后去 sys.path 这个目录列表中去找
python
import sys

print(sys.path)
# 控制台输出如下:
# [
#     '/Users/secret/codes/py-fastapi-demo',
#     '/Users/secret/.local/share/uv/python/cpython-3.12.12-macos-x86_64-none/lib/python312.zip',
#     '/Users/secret/.local/share/uv/python/cpython-3.12.12-macos-x86_64-none/lib/python3.12',
#     '/Users/secret/.local/share/uv/python/cpython-3.12.12-macos-x86_64-none/lib/python3.12/lib-dynload',
#     '/Users/secret/codes/py-fastapi-demo/.venv/lib/python3.12/site-packages',
# ]

print(type(sys.path))
# 控制台输出如下:
# <class 'list'>
# 我发现 sys.path 是一个 list
# 也就说可以手动修改这个 sys.path 来控制导包的行为

修改 sys.path 控制导包语句的查找列表

假设现在的项目结构是这样的:

.
├── pyproject.toml
├── src
│   ├── main.py         # 主入口文件
│   └── shared
│       └── tools.py
├── test.py
└── uv.lock

3 directories, 7 files

如果想要在 src/main.py 中导入 /test.pysrc/shared/tools.py 可以这样做:

python
#### src/main.py
import sys
from os import path

# 将当前文件所在的目录添加到 sys.path 中
script_path = path.dirname(path.abspath(__file__));
sys.path.append(script_path)

# 将当前文件所在目录的父级目录添加到 sys.path 中
sys.path.append(path.dirname(script_path))

# 注: 这些导入语句必须写在修改 sys.path 的语句之后
from test import test_fn1 # 导入 test.py 中的内容
from src.shared.tools import tools_fn1 # 导入 src/shard/tools.py 中的内容

安装第三方包

类似 node.js 的安装第三方包, 只不过 node.js 包管理的工具叫 npm 而 python 的包管理工具 pip

sh
# 已经安装的包
pip list

# 安装包
pip install numpy

# 删除已经安装的包
pip uninstall numpy

# 去 https://pypi.org 搜索某个包
pip search numpy

# 查看帮助
pip --help

# 导出安装的包列表
pip freeze > requirments.txt

设置包管理器源

sh
pip install numpy -i https://pypi.tuni.tsinghua.edu.cn/simple
sh
# 修改 ~/.pip/pip.conf 文件(没有就创建), 输入以下内容
[global]
index-url = https://pypi.tuni.tsinghua.edu.cn/simple
sh
阿里云: http://mirrors.aliyun.com/pypi/simple/
中国科技大学: https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣: http://pypi.douban.com/simple/
清华大学: https://pypi.tuna.tsinghua.edu.cn/simple/
中国科学技术大学: http://pypi.mirrors.ustc.edu.cn/simple/

如何发布一个包到 pipy?

查看视频教程

Released under the MIT License.