Python编译脚本案例实操?

wen python案例 1

本文目录导读:

  1. 案例一:基础编译脚本(编译单个.py文件)
  2. 案例二:批量编译脚本(编译整个项目)
  3. 案例三:编译并打包为可执行文件(使用PyInstaller)
  4. 案例四:完整的编译与优化脚本
  5. 案例五:自动化构建脚本
  6. 使用示例

我来为你提供几个实用的Python编译脚本案例,从简单到复杂逐步展示。

基础编译脚本(编译单个.py文件)

# compile_single.py
import py_compile
import sys
import os
def compile_python_file(source_file):
    """
    编译单个Python文件到字节码
    """
    if not os.path.exists(source_file):
        print(f"错误:文件 {source_file} 不存在")
        return False
    try:
        # 编译并生成.pyc文件
        compiled_file = py_compile.compile(source_file, cfile=source_file + 'c')
        print(f"✅ 编译成功:{source_file}")
        print(f"   生成文件:{compiled_file}")
        return True
    except py_compile.PyCompileError as e:
        print(f"❌ 编译失败:{e}")
        return False
if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("用法:python compile_single.py <filename.py>")
        sys.exit(1)
    source = sys.argv[1]
    compile_python_file(source)

批量编译脚本(编译整个项目)

# compile_project.py
import compileall
import os
import sys
from pathlib import Path
def compile_project(project_path, force=False, quiet=False):
    """
    批量编译整个Python项目
    参数:
    - project_path: 项目根目录
    - force: 是否强制重新编译
    - quiet: 是否静默模式
    """
    project_path = Path(project_path)
    if not project_path.exists():
        print(f"错误:目录 {project_path} 不存在")
        return False
    print(f"开始编译项目:{project_path}")
    print(f"强制重新编译:{'是' if force else '否'}")
    print("-" * 50)
    # 使用compileall批量编译
    success = compileall.compile_dir(
        str(project_path),
        force=force,
        quiet=quiet,
        rx=re.compile(r'/.git/|/__pycache__/')  # 排除git和缓存目录
    )
    if success:
        print("-" * 50)
        print("✅ 项目编译完成!")
    else:
        print("-" * 50)
        print("❌ 编译过程中存在错误")
    return success
if __name__ == "__main__":
    import re
    # 示例用法
    project_dir = "." if len(sys.argv) < 2 else sys.argv[1]
    compile_project(
        project_dir,
        force=True,  # 强制重新编译
        quiet=False  # 显示详细信息
    )

编译并打包为可执行文件(使用PyInstaller)

# compile_and_package.py
import subprocess
import sys
import os
import shutil
from pathlib import Path
def build_executable(script_path, 
                     name=None, 
                     one_file=True, 
                     windowed=False,
                     icon_path=None,
                     extra_data=None):
    """
    使用PyInstaller将Python脚本编译为可执行文件
    参数:
    - script_path: Python脚本路径
    - name: 输出文件名
    - one_file: 是否生成单个文件
    - windowed: 是否隐藏控制台窗口
    - icon_path: 图标文件路径
    - extra_data: 额外数据文件列表
    """
    # 检查PyInstaller是否安装
    try:
        import PyInstaller
    except ImportError:
        print("❌ 请先安装PyInstaller:pip install pyinstaller")
        return False
    # 构建命令
    cmd = ["pyinstaller"]
    if one_file:
        cmd.append("--onefile")  # 单文件模式
    if windowed:
        cmd.append("--windowed")  # 无控制台窗口
    if name:
        cmd.extend(["--name", name])
    if icon_path:
        cmd.extend(["--icon", icon_path])
    # 添加额外数据文件
    if extra_data:
        for src, dst in extra_data:
            cmd.extend(["--add-data", f"{src}:{dst}"])
    cmd.append(script_path)
    print("开始构建可执行文件...")
    print(f"执行命令:{' '.join(cmd)}")
    print("-" * 50)
    try:
        # 执行PyInstaller
        result = subprocess.run(cmd, capture_output=True, text=True)
        if result.returncode == 0:
            print("✅ 构建成功!")
            print(f"输出位置:{Path.cwd() / 'dist'}")
            # 显示生成的文件
            dist_dir = Path.cwd() / "dist"
            if dist_dir.exists():
                for f in dist_dir.iterdir():
                    print(f"  - {f.name}")
        else:
            print("❌ 构建失败!")
            print("错误信息:")
            print(result.stderr)
    except Exception as e:
        print(f"❌ 发生异常:{e}")
        return False
    return True
if __name__ == "__main__":
    # 示例用法
    if len(sys.argv) < 2:
        print("用法:python compile_and_package.py <script.py> [options]")
        sys.exit(1)
    build_executable(
        script_path=sys.argv[1],
        name="MyApp",          # 输出文件名
        one_file=True,         # 单文件
        windowed=False,        # 显示控制台
        icon_path=None,        # 自定义图标
        extra_data=[           # 附加资源文件
            ("config.json", "."),
            ("assets/", "assets")
        ]
    )

完整的编译与优化脚本

# advanced_compile.py
import py_compile
import compileall
import dis
import marshal
import struct
import time
import os
import sys
from pathlib import Path
from datetime import datetime
class PythonCompiler:
    """Python编译与优化工具"""
    def __init__(self, source_dir, optimize_level=2):
        self.source_dir = Path(source_dir)
        self.optimize_level = optimize_level
        self.stats = {
            'total': 0,
            'success': 0,
            'failed': 0,
            'skipped': 0
        }
    def compile_with_optimization(self, file_path):
        """
        编译单个文件并应用优化
        优化级别:
        0 - 基础编译
        1 - 移除断言
        2 - 移除断言和docstring
        """
        file_path = Path(file_path)
        if not file_path.exists() or not file_path.suffix == '.py':
            return False
        try:
            # 读取源代码
            with open(file_path, 'r', encoding='utf-8') as f:
                source = f.read()
            # 编译代码(带优化)
            code = compile(
                source,
                str(file_path),
                'exec',
                optimize=self.optimize_level
            )
            # 生成.pyc文件路径
            cache_dir = file_path.parent / '__pycache__'
            cache_dir.mkdir(exist_ok=True)
            pyc_file = cache_dir / f"{file_path.stem}.cpython-{sys.version_info.minor}.pyc"
            # 写入编译后的字节码
            with open(pyc_file, 'wb') as f:
                # 写入魔数和时间戳
                f.write(importlib.util.MAGIC_NUMBER)
                f.write(struct.pack('<I', int(time.time())))
                f.write(b'\0' * 4)  # 源文件大小(可选)
                # 写入序列化的代码对象
                marshal.dump(code, f)
            # 显示优化信息
            if self.optimize_level > 0:
                original_size = len(source)
                compiled_size = os.path.getsize(pyc_file)
                print(f"  优化级别:{self.optimize_level}")
                print(f"  源文件大小:{original_size} 字节")
                print(f"  编译后大小:{compiled_size} 字节")
            return True
        except SyntaxError as e:
            print(f"  语法错误:{e}")
            return False
        except Exception as e:
            print(f"  编译错误:{e}")
            return False
    def analyze_bytecode(self, file_path):
        """分析字节码"""
        import importlib.util
        try:
            # 加载编译后的模块
            spec = importlib.util.spec_from_file_location("temp_module", file_path)
            module = importlib.util.module_from_spec(spec)
            # 反汇编函数的字节码
            print(f"\n📊 字节码分析:{file_path}")
            print("-" * 40)
            for name in dir(module):
                obj = getattr(module, name)
                if callable(obj) and not name.startswith('_'):
                    print(f"\n函数:{name}")
                    dis.dis(obj)
        except Exception as e:
            print(f"分析失败:{e}")
    def compile_all(self, recursive=True):
        """编译所有Python文件"""
        pattern = "**/*.py" if recursive else "*.py"
        print(f"🔧 Python编译工具 - 优化级别 {self.optimize_level}")
        print(f"📂 源目录:{self.source_dir}")
        print(f"⏰ 开始时间:{datetime.now().strftime('%H:%M:%S')}")
        print("=" * 50)
        # 收集所有Python文件
        py_files = list(self.source_dir.glob(pattern))
        print(f"📄 找到 {len(py_files)} 个Python文件\n")
        # 编译每个文件
        for py_file in py_files:
            self.stats['total'] += 1
            # 跳过__init__.py和缓存文件
            if py_file.name == '__init__.py' or '__pycache__' in str(py_file):
                self.stats['skipped'] += 1
                continue
            print(f"编译:{py_file.relative_to(self.source_dir)}")
            if self.compile_with_optimization(py_file):
                self.stats['success'] += 1
                print("  ✅ 成功")
            else:
                self.stats['failed'] += 1
                print("  ❌ 失败")
        # 显示统计信息
        print("\n" + "=" * 50)
        print("📊 编译统计:")
        print(f"  总文件数:{self.stats['total']}")
        print(f"  编译成功:{self.stats['success']}")
        print(f"  编译失败:{self.stats['failed']}")
        print(f"  跳过文件:{self.stats['skipped']}")
        print(f"  成功率:{self.stats['success']/max(self.stats['total'],1)*100:.1f}%")
if __name__ == "__main__":
    import importlib
    # 使用示例
    compiler = PythonCompiler(
        source_dir=".",          # 当前目录
        optimize_level=2         # 最高优化级别
    )
    # 编译所有文件
    compiler.compile_all(recursive=True)
    # 分析特定文件的字节码
    # compiler.analyze_bytecode("your_module.py")

自动化构建脚本

# build_system.py
import os
import sys
import json
import subprocess
from pathlib import Path
from datetime import datetime
class BuildSystem:
    """自动化构建系统"""
    def __init__(self, config_file="build_config.json"):
        self.config = self.load_config(config_file)
        self.start_time = None
        self.build_id = datetime.now().strftime("%Y%m%d_%H%M%S")
    def load_config(self, config_file):
        """加载构建配置"""
        default_config = {
            "name": "MyPythonProject",
            "version": "1.0.0",
            "entry_point": "main.py",
            "output_dir": "dist",
            "compile_options": {
                "optimize": 2,
                "bytes": True,
                "strip_comments": True,
                "include_packages": ["requests", "numpy"],
                "exclude_patterns": ["*test*", "*example*"]
            },
            "build_steps": [
                "clean",
                "compile",
                "package",
                "test"
            ],
            "post_build": {
                "archive": True,
                "sign": False,
                "deploy": False
            }
        }
        try:
            with open(config_file, 'r') as f:
                config = json.load(f)
                return {**default_config, **config}
        except FileNotFoundError:
            print(f"未找到配置文件,使用默认配置")
            return default_config
    def clean(self):
        """清理构建目录"""
        print("\n🧹 清理构建目录...")
        dirs_to_clean = [
            "__pycache__",
            self.config['output_dir'],
            "build"
        ]
        for dir_name in dirs_to_clean:
            path = Path(dir_name)
            if path.exists():
                import shutil
                shutil.rmtree(path)
                print(f"  删除:{dir_name}")
    def compile(self):
        """编译所有Python文件"""
        print("\n🔨 编译Python文件...")
        from compileall import compile_dir
        # 创建输出目录
        output_dir = Path(self.config['output_dir'])
        output_dir.mkdir(exist_ok=True)
        # 编译设置
        compile_options = self.config['compile_options']
        # 使用compileall编译
        compile_dir(
            ".",
            force=True,
            quiet=0,
            rx=compile_options.get('exclude_patterns', [])
        )
        print("✅ 编译完成")
    def package(self):
        """打包项目"""
        print("\n📦 打包项目...")
        import zipfile
        output_dir = Path(self.config['output_dir'])
        output_file = output_dir / f"{self.config['name']}_{self.config['version']}.zip"
        with zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) as zf:
            # 添加Python文件
            for py_file in Path('.').rglob('*.py'):
                if '__pycache__' not in str(py_file):
                    zf.write(py_file)
            # 添加配置文件
            if Path('config.json').exists():
                zf.write('config.json')
        print(f"✅ 打包完成:{output_file}")
    def run_tests(self):
        """运行测试"""
        print("\n🧪 运行测试...")
        import pytest
        test_files = list(Path('.').rglob('test_*.py'))
        if test_files:
            result = pytest.main(["-v"])
            if result == 0:
                print("✅ 所有测试通过")
            else:
                print("❌ 测试失败")
                return False
        else:
            print("⚠️ 未找到测试文件")
        return True
    def build(self):
        """执行完整构建流程"""
        print(f"\n{'='*50}")
        print(f"🏗️  开始构建:{self.config['name']} v{self.config['version']}")
        print(f"  构建ID:{self.build_id}")
        print(f"{'='*50}")
        self.start_time = datetime.now()
        # 按步骤执行构建
        for step in self.config['build_steps']:
            print(f"\n📌 步骤:{step}")
            if step == "clean":
                self.clean()
            elif step == "compile":
                self.compile()
            elif step == "package":
                self.package()
            elif step == "test":
                if not self.run_tests():
                    print("❌ 构建失败:测试未通过")
                    return False
        # 构建完成
        elapsed = (datetime.now() - self.start_time).seconds
        print(f"\n{'='*50}")
        print(f"✅ 构建完成!")
        print(f"  耗时:{elapsed} 秒")
        print(f"  输出位置:{Path(self.config['output_dir']).absolute()}")
        print(f"{'='*50}")
        return True
if __name__ == "__main__":
    # 创建构建系统实例
    builder = BuildSystem("build_config.json")
    # 执行完整构建
    builder.build()

使用示例

基础编译

# 编译单个文件
python compile_single.py my_script.py
# 批量编译项目
python compile_project.py /path/to/project

编译为可执行文件

# 需要先安装PyInstaller
pip install pyinstaller
# 打包为单文件
python compile_and_package.py my_app.py --onefile
# 打包为单个可执行文件,带图标
python compile_and_package.py my_app.py --name MyApp --icon app.ico

高级编译优化

# 使用优化级别2编译
python advanced_compile.py

自动化构建

# 创建配置文件
echo '{
  "name": "MyApp",
  "version": "1.0.0",
  "entry_point": "main.py"
}' > build_config.json
# 执行构建
python build_system.py

这些脚本覆盖了Python编译的常见场景,从简单的单文件编译到完整的自动化构建系统,你可以根据实际需求选择使用或修改这些脚本。

标签: Python编译

抱歉,评论功能暂时关闭!