模块
绝对导入和相对导入为 Python 包的组织提供了清晰的规则。明确的导入方式让代码的依赖关系更加清晰,避免了命名冲突。
# 绝对导入
from mypackage.submodule import function
# 相对导入
from .submodule import function
from ..parentmodule import other_function
模块
模块表示一个或多个具有相关功能的 Python 代码的集合。
最小的模块是单个 Python 可执行文件。最常见的是 .py
后缀结尾的文件。
较大的模块往往是一个文件夹,内含多个 Python 可执行文件、文件夹与资源等等。
Python 的 import
不止能导入 .py
后缀结尾的文件
.pyd
是 Windows 特有的文件格式。它的作用和 .dll
相似,可直接导入而不需要 ctypes
模块。
.pyc
是由 py 文件经过编译后生成的二进制文件,py 文件变成 .pyc
文件后,加载的速度有所提高,并且可以实现源码隐藏。
Python 的 import
通过 importlib
模块自动处理这些不同格式的文件。
.so
和 .dll
分别是 Linux 和 Windows 的动态链接库,可使用 ctypes
模块导入。
.pyx
是 Cython 的源代码文件,支持 Python 与 C 代码混合编程。可以将 .pyx
文件编译为纯 C 文件,但需要注意的是,这个 C 文件通常是用来创建一个 Python 扩展模块的,它不能独立运行。需要进一步编译为 .so
或 .dll
或 .pyd
文件。
.pyi
文件是 Python 接口文件(Python Interface file),也称为存根文件(stub file)。它包含模块的类型提示,但不包含实际的实现代码。当一个模块是用 C 或 Cython 编写的(例如,编译成 .pyd
或 .so
文件),它的源代码不是 Python 格式,因此无法直接被 MyPy、Pyright 等类型检查器或 IDE 识别。这时,开发者会创建一个对应的 .pyi
文件来提供类型信息。
from,import,as
在导入模块时,可以使用from
、import
、as
关键字来导入模块中的指定内容。
通过import
关键字导入指定的模块。
通过from
关键字从模块中导入指定内容。
通过as
关键字给函数别名。
def foo():
print('goodbye, world!')
import module # 导入整个模块文件
module.foo()# 输出goodbye, world!
from module import foo # 从模块中导入foo函数
foo() # 输出goodbye, world!
from module import foo as foo2 # 导入模块中的foo函数,并别名为foo2
foo2() # 输出goodbye, world!
__name__
属性
__name__
属性是一个特殊的属性,用于获取模块的名称。
print(__name__)
"""
当模块被直接运行时,__name__的值为__main__
当模块被导入时,__name__的值为模块的名称,即module
如果你希望在模块被直接运行时执行一些代码,被导入时则不执行,可以这样写:
"""
if __name__ == "__main__":
print("This is the main module")
__init__.py
与-m
通常情况下,当使用 import 语句导入模块后,Python 会按照以下顺序查找指定的模块文件:
当前目录,即当前执行的程序文件所在目录下查找;
到 PYTHONPATH(环境变量)下的每个目录中查找;
到 Python 默认的安装目录下查找。
以上所有涉及到的目录,都保存在标准模块 sys 的 sys.path 变量中,通过此变量我们可以看到指定程序文件支持查找的所有目录。换句话说,如果要导入的模块没有存储在 sys.path 显示的目录中,那么导入该模块并运行程序时,Python 解释器就会抛出 ModuleNotFoundError(未找到模块)异常。
解决“Python 找不到指定模块”的方法有 3 种,分别是:
向 sys.path 中临时添加模块文件存储位置的完整路径;
将模块放在 sys.path 变量中已包含的模块加载路径中;
设置 path 系统环境变量。
具体区别可以创建下面的文件结构来了解:
top/
├── __init__.py
├── second.py
└── second_copy.py
在 Python 中,__init__.py
文件是一个特殊的文件,它标志着一个目录是一个 Python 包(package)。当 Python 解释器在导入一个模块时,如果发现一个目录里包含了 init.py 文件,它就会将这个目录当作一个包来处理。
__init__.py
文件本身可以为空,但它也可以包含初始化包的代码。例如,你可以在里面定义变量、函数,或者导入子模块,以便在包被导入时自动执行这些代码。这使得你可以控制包的初始化行为,比如设置包级别的配置或简化子模块的导入路径。
import sys
print(sys.argv)
import sys
print(sys.argv)
from .second_copy import *
两种运行脚本的方式,以及对应的输出:
-
使用
python -m top.second
运行:PS C:\Users\jiang\Desktop> python -m top.second
['C:\\Users\\jiang\\Desktop\\top\\second.py']
['C:\\Users\\jiang\\Desktop\\top\\second.py']- 输出两次相同的
sys.argv
,显示脚本的完整路径。 - 没有错误,脚本正常运行。
- 输出两次相同的
-
直接运行
python top\second.py
:PS C:\Users\jiang\Desktop> python top\second.py
['top\\second.py']
Traceback (most recent call last):
File "C:\Users\jiang\Desktop\top\second.py", line 3, in <module>
from .second_copy import *
ImportError: attempted relative import with no known parent package- 仅输出了
sys.argv
一次,显示的是相对路径top\second.py
。 - 然后抛出了
ImportError
,提示“尝试进行相对导入,但没有已知的父包”。
- 仅输出了
为什么会有这样的差异?
问题的核心在于 Python 如何处理这两种运行方式,以及它们对模块结构和相对导入的影响。
1. python -m top.second
的行为
- 运行方式:使用
-m
标志告诉 Python 将top.second
作为一个模块运行。这里,top
被识别为一个包,second
是该包中的一个模块。 - 包上下文:Python 会正确设置包的层次结构。由于当前工作目录是
C:\Users\jiang\Desktop
,Python 知道top
是一个包,并且second.py
是其中的模块。 - 相对导入:在
second.py
中,from .second_copy import *
是一个相对导入,.
表示当前包(即top
)。因为 Python 已经建立了包上下文,它能找到同一目录下的second_copy.py
,导入成功。 sys.argv
的值:在这种模式下,sys.argv[0]
被设置为脚本的完整路径,即C:\\Users\\jiang\\Desktop\\top\\second.py
。- 首先,
second.py
打印这个值。 - 然后,导入
second_copy.py
时,second_copy.py
也打印sys.argv
,因为sys.argv
是全局的,不会因模块不同而改变,所以输出两次相同的结果。
- 首先,
2. python top\second.py
的行为
- 运行方式:直接通过文件路径运行
second.py
,即将其作为独立的脚本执行,而不是作为一个包中的模块。 - 包上下文缺失:在这种情况下,Python 不会将
top
视为一个包,而是直接运行second.py
作为主模块(__main__
)。因此,没有定义任何“父包”。 - 相对导入失败:
second.py
中的from .second_copy import *
依赖于包 结构,但由于缺少包上下文,Python 不知道.
代表什么,导致抛出ImportError: attempted relative import with no known parent package
。 sys.argv
的值:在这里,sys.argv[0]
是命令行中提供的路径,即top\second.py
(相对于当前工作目录C:\Users\jiang\Desktop
)。second.py
打印这个值后,尝试执行相对导入时失败,因此程序终止,second_copy.py
的代码未被执行。
垃圾回收机制
如果持续不断加载数据,调用函数模块,计算机的内存会溢出,Python 的垃圾回收机制采用引用计数机制,当一个对象的引用数为 0 时,它就会被垃圾回收机制回收。
import sys
# 生以下四种情况的时候,该对象的引用计数器+1
a= "hello" # 对象被创建
b=a # 对象被引用
def func(a):
return
func(a) # 对象被作为参数,传到函数中
List=[a,"a","b",2] # 对象作为一个元素,存储在容器中
sys.getrefcount(a)
# python系统内部很多地方都在使用一些常用的对象,这些对象在python解释器启动时就被创建出来。
#发生以下四种情况时,该对象的引用计数器**-1**
#该对象的别名被显式销毁时
del a
#该对象的引别名被赋予新的对象,
a = "world"
#个对象离开它的作用域,例如 func函数执行完毕时,函数里面的局部变量的引用计数器就会减一(但是全局变量不会)
#该元素从容器中删除时,或者容器被销毁时。
b = a # 当前计数器为2
del b # 删除变量b:b对应的对象的引用计数器-1 (此时计数器为1)
del a # 删除变量a:a对应的对象的引用计数器-1 (此时引用计数器为0)
# 当引用计数器为0 时,意味着没有人再使用这个对象,这个对象就变成垃圾,垃圾回收。
# 回收:1. 对象从refchain的链表移除。
# 2. 将对象进行销毁,内存归还给操作系统,可用内存就增加。
sys.getrefcount(a)
内置函数
__import__
函数
函数签名:__import__(name, globals=None, locals=None, fromlist=(), level=0) -> module
参数说明:
name
:要导入的模块名globals
:全局命名空间locals
:局部命名空间fromlist
:要导入的属性列表level
:导入级别
返回值:
- 返回导入的模块
math = __import__('math')
print(math.sqrt(16)) # 4.0
第三方库
优秀的第三方库有很多,例如awesome-python中列举了大量优秀的第三方库。
安装
这里第三方模块使用的基本流程以 OpenCV 为例
- 下载
pip install opencv-python
- 导入
import cv2
- 使用
模块名.方法名
示例:cv2.imread('cat.jpg')
这里需要注意的是:OpenCV 模块的下载名、导入名均不是 opencv,OpenCV 共有 4 种不同的下载名:opencv-python
、opencv-python-headless
、opencv-contrib-python
、opencv-contrib-python-headless
。
事实上,模块名、下载名与导入名更多是开发者出于习惯会将名称统一,并非一种强制的规则。建议在下载模块之前先通过搜索引擎搜索。
对于复杂 的模块来说,使用 help() 方法、dir() 方法不能很好的满足我们的需求。需要搭配官方文档,查阅使用实例。
第三方模块与系统模块一样,都是自定义好的一系列模块,这些模块也自然存在一些版本差异。
在使用的过程之中很可能因为版本的不匹配、方法的弃用导致示例的代码失效。
我们可以通过升级至最新版本或安装指定的版本来解决:
# 安装最新版
python -m pip install SomePackage # latest version
# 安装指定版本
python -m pip install 'SomePackage==1.0.4' # specific version
# 安装最小版本
python -m pip install 'SomePackage>=1.0.4' # minimum version
# 升级至最新版本
python -m pip install --upgrade SomePackage
此外,有些包作者会仅在 GitHub 提供源码,需要我们自己编译安装。
uv pip install .
使用 uv 安装当前目录下的包。并以拷贝的方式将其添加到 site-packages
中。
uv pip install -e .
使用 uv 安装当前目录下的包,并以符号链接的方式将其添加到 site-packages
中。
两种方式都会搜索当前项目下的 pyproject.toml
和 setup.py
。如果同时存在只会使用 pyproject.toml
。
在开发过程中会频繁的修改代码并导入测试,传统的方式是每次修改代码后,都需要重新构建包、重新安装包。不优雅。
符号链接(Symbolic Link)是一种文件系统对象,它指向另一个文件或目录的位置。当你访问符号链接时,操作系统会自动将符号链接解析为实际的目标文件或目录(软链接)。
这让我们可以在实际的目录中修改代码,另一边立刻能看到修改后的效果。不需要每次修改都重新构建、安装包。
在 Linux 服务器中安装包安装到全局还是用户目录下需要根据实际情况选择。
-
如果需要安装到系统环境,可以使用
--break-system-packages
后缀,例如:pip install --break-system-packages requests
。 -
如果需要安装到用户目录下,可以使用
--user
后缀,例如:pip install --user requests
。
在现代 Linux 发行版中,系统 Python 标记为"外部管理环境"(externally managed environment)。
由操作系统的包管理器(如 apt)统一维护,禁止用户通过 pip
(即使是 --user
)直接安装包,以防破坏系统依赖。
如果你要安装的是 uv
、ruff
这样的可执行应用程序,推荐使用 pipx
独立安装。
sudo apt install pipx
pipx install uv
pipx install ruff
如果你要安装的是 requests
、opencv
这样的Python 包,推荐使用虚拟环境结合 pip
独立安装。
python3 -m venv .venv
source .venv/bin/activate
pip install requests
对于更加特殊的一些包,如ros2
会使用到特定的Python包(rclpy
等),ros2
会将其下载到本地目录后,将这个目录添加到PYTHONPATH
环境变量中。让所有Python解释器找包时都能加载这个目录内的模块。
PYTHONPATH
是一个操作系统级别的环境变量,用于指定Python解释器的搜索路径。PYTHONPATH
的值是一个或多个目录路径,优先级高于标准库和site-packages
PYTHONPATH
只是额外添加的搜索路径,为空时不会影响默认的搜索路径。- 除了
PYTHONPATH
环境变量,还有PATH
等环境变量会影响Python解释器的搜索路径。你可以通过env
命令查看当前所有设置的环境变量。
不过这种方式也增加了包冲突的风险,我们个人开发时使用虚拟环境管理包。
更多子命令与参数可以查看pip官方文档。
命令 | 作用说明 |
---|---|
pip install | 安装 Python 包(来自 PyPI、本地文件、VCS 等) |
pip uninstall | 卸载已安装的包 |
pip inspect | 以结构化 JSON 格式输出当前环境的包信息(实验性,较新版本支持) |
pip list | 列出当前环境中所有已安装的包及其版本 |
pip show | 显示某个已安装包的详细信息(如版本、作者、依赖、位置等) |
pip freeze | 以 requirements.txt 兼容格式输出已安装包列表(常用于锁定依赖) |
pip check | 验证已安装包的依赖是否满足,检测版本冲突 |
pip lock | 生成可复现的依赖锁文件(截至 pip v25.2,此命令尚未正式实现,处于规划/实验阶段) |
pip download | 下载包及其依赖(不安装),用于离线环境或缓存 |
pip wheel | 将包构建为 wheel 格式的分发文件(.whl ) |
pip hash | 计算并输出文件的哈希值(用于验证包完整性) |
pip search | 已弃用:原用于在 PyPI 上搜索包,因 API 关闭已不可用 |
pip index | 查询包在索引(如 PyPI)中的元数据(如版本、兼容性等) |
pip cache | 管理 pip 的本地缓存(查看、清空等) |
pip config | 管理 pip 的配置文件(设置镜像源、超时等) |
pip debug | 显示 pip 的调试信息(如兼容标签、环境路径、版本等) |
运行时与安装时视图
从一次 import 开始 ,你写了一行代码:
import requests
这行代码背后,Python 解释器做了三件事:
- 找:在
sys.path
列出的路径中搜索requests
。 - 读:找到后,读取模块内容,执行其中的代码。
- 存:把执行结果存入
sys.modules
,避免重复导入。
这个过程,就是 Python 包管理的运行时视图。
那么,包是怎么出现在 sys.path
里的?
答案是:pip install requests
。pip 做了:
- 下载:从 PyPI 下载
requests
的 wheel 包。 - 解压:把包内容解压到
site-packages
目录。 - 记录:在
site-packages
下生成一个requests-X.X.X.dist-info
目录,里面存着元数据。
这个过程,就是 Python 包管理的安装时视图。
当我们在命令行执行 pip install requests
时,pip 实际上会向 PyPI(Python Package Index)发送一 系列 HTTP 请求来查找、下载和验证包。
pip 首先会向 PyPI 发送一个 GET 请求,以获取包的元数据信息,例如版本号、依赖关系等。
# 下载请求示例
GET /pypi/requests/json HTTP/1.1
Host: pypi.org
User-Agent: pip/21.0.1
Accept: application/json
Accept-Encoding: gzip, deflate
Connection: keep-alive
一旦 pip 获取了包的元数据,它会根据元数据中的下载链接发送另一个 GET 请求来下载包文件(通常是 wheel 文件或源码包)。
# 下载请求示例
GET /packages/66/49/7b6c842e994f9353296cb2865fc206635286b27415336348f39/requests-2.25.1-py2.py3-none-any.whl HTTP/1.1
Host: files.pythonhosted.org
User-Agent: pip/21.0.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
下载完成后,pip 会使用元数据中提供的哈希值来验证包的完整性,确保包在传输过程中没有被篡改。
你可以提前获得包的wheel压缩文件,例如requests-2.32.5-py3-none-any.whl
PYPI
每个包的侧边栏Download files
中可能会有多个版本的压缩文件,记得选择适配你Python与计算平台版本的压缩文件。
那么你就可以通过pip install requests-2.32.5-py3-none-any.whl
安装它,等价于跳过了前面的网络通信下载时间。
验证通过后,pip 会将包文件解压并安装到 Python 的 site-packages 目录中,并记录安装信息以便后续管理。
如果包有依赖关系,pip 会递归地对每个依赖包重复上述过程,直到所有依赖都被满足。
为了提高效率,pip 还会使用本地缓存和索引,避免重复下载相同的包文件。
在整个过程中,pip 会处理各种网络错误,如超时、连接失败等,并根据配置进行重试。
对于私有包或需要认证的包,pip 会使用适当的认证机制,如 API 密钥或 OAuth,来发送请求。
pip 会记录详细的日志信息,包括请求的 URL、响应状态码、下载速度等,以便用户排查问题。
用户可以通过 pip 的配置文件或命令行选项来定制请求行为,例如使用代理、指定镜像源、设置超时时间等。
通过这些步骤,pip 确保了 Python 包的可靠、高效和安全的安装。
元数据
很多人以为,pip install 就是“把文件拷过去”。但真正关键的是那个 .dist-info
目录。
它就像包的"身份证",记录了:
- 包名、版本、作者、描述 ——
METADATA
文件 - 所有安装的文件列表 ——
RECORD
文件(卸载时用) - 依赖哪些其他包 ——
METADATA
中的Requires-Dist
字段 - 顶级模块名称 ——
top_level.txt
文件(import 时用) - 安装工具 ——
INSTALLER
文件(通常写着pip
)
没有这个身份证,pip 就不知道这个包是谁、从哪来、依赖谁、能不能卸载。
当你执行 pip list
或 pip show requests
,看到的信息,全都来自这个 .dist-info
。
离线安装
如果你完全没有网络,能不能不 pip,直接把模块文件复制到 site-packages
?
可以。但你复制的只是“身体”,没有“灵魂”。
你复制了 .py
文件,Python 能 import,没问题。
但 pip 不知道它的存在,无法卸载,也无法管理依赖。
更严重的是:如果这个包有依赖,比如 requests
依赖 urllib3
, 你手动复制 requests
,却忘了 urllib3
,运行时就会报错。
pip 的价值,不在于“复制文件”,而在于“解析依赖 + 安装 + 记录元数据”这一整套自动化流程。
你必须把代码和元数据文件,以及相关的依赖库与依赖库元数据文件都复制过去,才能成功。
Python 包管理的本质,是三个要素的闭环:
- 代码(Code):真正的
.py
文件,实现功能。 - 结构(Structure):包目录、
__init__.py
、子模块,构成可导入的层级。 - 元数据(Metadata):
.dist-info
,让包可发现、可管理、可依赖。
只有三者齐全,一个包才算真正“安装”完成。
看到 ImportError
,你能立刻判断是“没安装”、“路径不对”还是“依赖缺失”。
看到 .dist-info
,你知道它不是垃圾,而是包的“灵魂”。
Python 包管理,管的是模块与解释器的关系,是包与依赖的关系,是开发者与生态的关系。
发布第三方库
我们安装的模块来自于pypi.org
,我们也可以分享我们的模块到pypi.org
。
截至2025年10月,UV 已经成为最现代化和高效的 Python 包管理工具,它集成了包管理、虚拟环境和打包发布功能,比传统的 pip + setuptools 方案更快更简单。
目录结构
your_project/
├── .github/(可选)
│ └── workflows/(可选)
│ └── python-publish.yml(可选)
│
├── src/(推荐结构)
│ └── your_package/(包名)
│ ├── __init__.py
│ └── module.py
│
├── tests/(可选)
│ └── test_module.py
│
├── README.md(可选)
├── LICENSE(可选)
├── pyproject.toml
└── uv.lock(UV 生成的锁文件)
pyproject.toml
pyproject.toml为Python项目提供了统一的配置文件格式,取代了传统的setup.py。它让项目配置更加清晰和标准化。
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "my-package"
version = "1.0.0"
description = "A sample package"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "exboard"
version = "1.0.12"
authors = [
{ name="Allen", email="jiangyangcreate@gmail.com" },
]
description = "A exboard package for AIBOX"
readme = "README.md"
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
]
dependencies = [
"schedule>=1.1.0",
]
[project.urls]
Homepage = "https://github.com/jiangyangcreate/exboard"
Issues = "https://github.com/jiangyangcreate/exboard/issues"
# UV 特定配置
[tool.uv]
dev-dependencies = [
"pytest>=7.0",
"black>=23.0",
"ruff>=0.1.0",
]
构建包
# 构建包(生成 wheel 和 sdist)
uv build
# 只构建 wheel
uv build --wheel
# 只构建源码分发
uv build --sdist
该命令将在 dist/
目录下生成 .tar.gz
和 .whl
文件。
PyPI 密钥配置
在 PyPI 中获取 API Token:
- 访问:https://pypi.org/manage/account/token/
- 选择 "Add API token"
- 设置名称和权限范围
- 复制生成的 token
配置 PyPI 凭据
编辑用户目录下的 .pypirc
文件:
[pypi]
username = __token__
password = your_api_token
[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = your_test_api_token
发布到 PyPI
# 安装 twine(如果需要)
uv add --dev twine
# 上传到 PyPI
uv run twine upload dist/*
# 上传到测试 PyPI
uv run twine upload --repository testpypi dist/*
GitHub Actions 发布
name: Upload Python Package
on:
release:
types: [published]
permissions:
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install UV
uses: astral-sh/setup-uv@v3
with:
version: "latest"
- name: Set up Python
run: uv python install 3.11
- name: Install dependencies
run: |
uv sync --all-extras --dev
- name: Run tests
run: |
uv run pytest
- name: Build package
run: |
uv build
- name: Publish package
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
在 GitHub 项目中添加PyPI密钥:
Settings -> Secrets and variables -> Actions -> New repository secret
Name: PYPI_API_TOKEN
Value: 刚刚复制的 API token
这样当我们在 GitHub 上创建 release 时,会自动将包上传到 PyPI。注意不是 push 代码自动上传。
Python 导入 C 模块
Python社区的发展依赖于开放、透明的决策过程。每个PEP都经过充分讨论,这种民主化的开发方式保证了Python的长期健康发展。
当你遇到"为什么Python要这样设计"的问题时,PEP往往能给你答案。
Python 的底层是 C 写的(实际上大部分高级编程语言都是 C 写的)因此Python可以调用以下C/C++文件类型:
- C源代码文件(.c)
- C++源代码文件(.cpp、.cxx、.cc)
- 编译后的共享库(Linux/Unix的.so、Windows的.dll、macOS的.dylib)
- 编译后的静态库(Linux/Unix的.a、Windows的.lib)
因此互相调用的逻辑主要是:数据类型转换、编译库的链接、接收返回值。
python+c/c++混合编程如:
原生的 Python.h
cython
pybind11:pytorch 也采用该方法
ctypes、cffi、SWIG、Boost.Pytho 等
但不论是哪个方法,大致的流程都是:转换数据类型->编译代码->生成编译后的文件(.pyd .pyc .pyo .so .dll 等)
代码编写
一个求某个数可以分解为多少个质数之和代码,其中最核心的代码是判断一个数是否为质数。我们使用C语言实现这个被频繁调用的功能。
#include <math.h>
int is_prime(int n) {
if (n < 2) return 0;
for (int i = 2; i <= (int)sqrt(n); i++) {
if (n % i == 0) return 0;
}
return 1;
}
编译:gcc -shared -o prime.so prime.c
import ctypes
import timeit
# 加载C动态库
prime_lib = ctypes.CDLL('./prime.so')
is_prime = prime_lib.is_prime
is_prime.argtypes = [ctypes.c_int]
is_prime.restype = ctypes.c_int
def count_prime_pairs(n):
count = 0
for i in range(2, n // 2 + 1):
if is_prime(i) and is_prime(n - i):
count += 1
return count
def is_prime_py(n):
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True
def count_prime_pairs_py(n):
count = 0
for i in range(2, n // 2 + 1):
if is_prime_py(i) and is_prime_py(n - i):
count += 1
return count
if __name__ == "__main__":
n = int(input("输入一个正整数: "))
print("C混合版结果:", count_prime_pairs(n))
print("纯Python结果:", count_prime_pairs_py(n))
# 性能对比
py_time = timeit.timeit(lambda: count_prime_pairs_py(n), number=100)
c_time = timeit.timeit(lambda: count_prime_pairs(n), number=100)
print(f"Pure Python: {py_time:.4f} seconds")
print(f"Python+C: {c_time:.4f} seconds")
想把Python代码封装为可执行文件,可以使用PyInstaller。
官方文档:https://www.pyinstaller.org/
命令行:
# 直接封装
pyinstaller -F app.py
# 指定图标
pyinstaller -F -i app.ico app.py
# 指定图标 不展示终端框
pyinstaller -F -i app.ico app.py --noconsole
# 将数据文件添加到捆绑包中,中间使用分号分隔,前面是源目录地址,后面是目的目录地址
pyinstaller -F -i app.ico app.py --add-data="C:\mediapipe\modules;mediapipe/modules" --noconsole