用 Pyinstaller 模块将 Python 程序打包成 exe 文件(全网最全面最详细)(三)

简介: 用 Pyinstaller 模块将 Python 程序打包成 exe 文件(全网最全面最详细)(三)

2、修改文件打开函数

这里有三种方法,前两种是我的方法,最后一种是网上别人的方法。

① 引入特定的模块

这个模块的代码很简单,放在下面,一定要将模块命名为 _.py,并在引用其他第三方模块之前就引用它,但又一定要在下划线开头的模块之后引用(否则会有 BUG),它在主 Python 文件里引用一次即可(其他的文件不用引用)!它可以将 open 函数改成我们想要的,而且原来的代码还完全不用修改!

import builtins
def wrapper(function):
    def _open(*args, **kw):
        """ 修改路径 """
        _args = list(args)
        _args[0] = __file__[:-4] + args[0]
        if kw.get('file'):
            kw['file'] = __file__[:-4] + kw['file']
        return function(*_args, **kw)
    return _open
setattr(builtins, 'open', wrapper(open))

这里的 __file__ 是 Python 文件的属性,是一个字符串,为该文件的绝对路径,不管该文件在哪里,__file__ 都是对应的绝对路径。然后我们用写一个 wrapper 函数充当装饰器,将内置的 open 函数包装一下。再引入 builtins 模块(内置函数和类的模块),给其添加一个名为 open 新属性以覆盖原来的 open 函数,并对该项目整体生效即可!


这种方法的好处在于,它只需要在主文件里引用一次即可,其他的什么都不用改!(④ 特别注意 里的除外)

② 自己手动修改 open 函数修改路径

这个修改是在源代码中修改的(每一个用到了 open 函数的 Python 文件都要改一次),目的就是要让相对路径变成会根据主 Python 文件的路径而变化的绝对路径。修改的装饰器如下:

# 编写装饰器
def wrapper(function):
    def _open(*args, **kw):
        """ 修改路径 """
        args_list = list(args)
        key = '/'.join(__file__.split('\\')[:-1]) + '/'
        args_list[0] = key + args[0]
        if kw.get('file'):
            kw['file'] = key + kw['file']
        return function(*args_list, **kw)
    return _open
# 装饰内置函数open
open = wrapper(open)

把这段代码写在文件的开头即可(或者说在使用open函数之前)。

③ 网友的其他方法

他们就是写了这样一个函数来代替 open,也是手动修改的 open 函数,不得不说,看起来有点麻烦(每个用了 open 函数的 Python 文件都要引入 os 和 sys 模块)。

import os
import sys
def get_resource_path(relative_path):
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.abspath("."), relative_path)

其他的都是一样的。

④ 特别注意

这里还要提一下,无论是前面的哪一种方法,只要你使用了参数为路径的其他函数时,也要改一下,其实就是在相对路径前面加上方法②中的 key 即可。


其实直接方法③来代替也可以,但是功能上容易出错,而且如果 Python 文件较多,那么每个 Python 文件都这样引用两个模块(sys 和 os),看起来比较麻烦。


当我们使用了 tkinter 模块的时候,PhotoImage 类就是要这样写的一个例子(其中的 __init__ 方法用到了路径):

class PhotoImage(tkinter.PhotoImage):
    def __init__(self, *args, **kw):
        if kw.get('file'):
            key = '/'.join(__file__.split('\\')[:-1]) + '/'
            kw['file'] = key + kw['file']
        tkinter.PhotoImage.__init__(self, *args, **kw)

这个代码就要写在使用 PhotoImage 的开头,后续调用时就用这个 PhotoImage,使用其他模块时,遇到参数为路径的函数或类,都要这样修改。

最后一步,和之前的方法一样,打包你的程序即可!

打包实战

我这里以一个我的半成品为例,进行打包。项目是一个图形化界面的程序。我们要将其打包成只含有一个 exe 的文件。

打包方式的选择

我的项目里面包含多个 Python 文件,要用多 Python 文件打包方式;

项目比较大,为节省打包时间,并追求极致的 exe 大小,采用虚拟环境打包方式;

项目含有资源文件夹,采用包含资源文件的打包方式。

打包方式选择好了,开始打包!

打包全过程

项目全部文件(蓝色背景的是主文件)项目文件

包含四个 Python 文件、一个资源文件夹(res),资源文件夹里面又包含了一些子文件夹和 json 文件。

第一步:启动 Anaconda Prompt,切换至目标文件夹路径位置

cd C:\Users\小康\Desktop\SuperGameLauncher

第二步:启动虚拟环境(我的是一个纯净的、第三方包只有 Pyinstaller 的环境)

conda activate e1

启动虚拟环境切换至目标文件夹路径位置

第二步:启动虚拟环境(我的是一个纯净的、第三方包只有 Pyinstaller 的环境)

conda activate e1

启动虚拟环境

第四步:引入 _.py 模块(我的程序用到了大量 open 函数且涉及多文件)

_.py 自定义模块

引入 _ 模块

处理特殊的情况(tkintertools 模块里面有参数为路径的类):

处理特殊情况

第五步:编辑 spec 文件

修改图中标识的两处地方(_.py 不要忘记了)。

编辑 spec 文件

第六步:打包项目(注意这里的对象是 spec 文件)

Pyinstaller SuperGameLauncher.spec

准备打包

打包成功!

打包成功

第七步:检验打包效果

找到项目中的 dist 文件夹,打开后会有一个 exe 文件。对于我这个项目而言,这已经是非常小的大小了(50.8MB),毕竟资源文件就有 42.2MB,也就是说,除去资源文件,源代码占用的大小为 8.6MB!怎么样,是不是很不错呢?

dist 文件夹

双击运行!

成功运行

目录
相关文章
|
1天前
|
Java Serverless Python
探索Python中的并发编程与`concurrent.futures`模块
探索Python中的并发编程与`concurrent.futures`模块
10 4
|
13天前
|
Python
像导入Python模块一样导入ipynb文件
像导入Python模块一样导入ipynb文件
|
13天前
|
Python
如何在 Python 中导入模块
【8月更文挑战第29天】
18 1
|
13天前
|
Python
|
13天前
|
数据采集 JSON 算法框架/工具
我常用的几个经典Python模块
我常用的几个经典Python模块
|
13天前
|
开发者 Python
什么是 python 模块?
【8月更文挑战第29天】
9 0
|
13天前
|
TensorFlow 算法框架/工具 C++
Python exe文件打包神器-Nuitka!
Python exe文件打包神器-Nuitka!
|
30天前
|
区块链 Python
最详细Python打包exe教程,并修改图标,只需30秒
最详细Python打包exe教程,并修改图标,只需30秒
54 4
最详细Python打包exe教程,并修改图标,只需30秒
|
14天前
|
区块链 Python
Python脚本打包 exe,auto-py-to-exe来帮你!
Python脚本打包 exe,auto-py-to-exe来帮你!
|
27天前
|
Linux 数据安全/隐私保护 Python
LInux下 python混淆代码打包产出exe
安装 PyArmor 加密Python程序:使用`pip install pyarmor`。为避免混淆 venv 目录,可指定排除此目录:`.\/venv\/bin\/pyarmor-7 pack -e \"--onefile\" -x \"--exclude venv\" main.py`。查阅详细文档:[官方指南](https://pyarmor.readthedocs.io/zh/v7.x/advanced.html)。