如何将 Python 包编译为 DLL

40
我有一个 Python 包需要编译成 dll,以便易于导入分发。您可能会建议使用 "*.pyc"。但我在某个地方读到,任何 "*.pyc" 都很容易被反编译!更新:请按照以下步骤操作:
1)编写一个 Python 包
2)想要分发它
3)不想分发源代码
4)*.pyc 可被反编译>>可提取源文件!
5)dll 是标准的。

6
.pyc文件是将Python脚本编译为字节码的一种格式,以便在解释器中更快地加载。它不是可执行文件。那么,“编译为dll文件的Python包”有什么用途呢? - Joel Cornett
2
你的答案已经在问题中给出了!与 *.pyc 相比,dll 无法反编译为源代码,对吗? - Developer
12个回答

17
如今有一个简单的解决方案:使用Nuitka编译器,如《Nuitka用户手册》所述。
用例2 - 扩展模块编译 如果您想编译单个扩展模块,只需执行以下操作: python -m nuitka --module some_module.py 然后,生成的文件some_module.so可以代替some_module.py使用。
您需要为每个要支持的平台进行编译,并编写一些初始化代码来导入适用于特定平台/Python版本等的so/pyd文件。
[编辑 2021-12]:实际上,在Python 3中,根据文件名自动确定正确的so/dll(如果包含Python版本和平台 - 目前找不到此功能的PEP),但Nuitka会为编译的模块创建正确的名称。因此,对于Python 2.7,库名称将为something.pyd或something.so,而对于Python 3,这将更改为something.cp36-win32.pyd或something.cpython-36m-x86_64-linux-gnu.so(适用于32位Python 3.6在x86上)。
结果并非所要求的DLL,而是Python本地编译的二进制格式(不同于pyc文件中的字节码;so/pyd格式不容易被反编译 - Nuitka通过C++翻译编译成机器码)。
编辑[2020-01]:编译的模块容易通过Python标准机制进行评估方法 - 例如,它可以像其他模块一样被导入并列出其方法等。为了保护实现不被以这种方式暴露,需要进行更多的工作,而不仅仅是编译成二进制模块。

哇!这真是一颗宝石!!非常感谢。 - TAbdiukov

13

把想要隐藏的所有内容用 Cython 编写,并编译成 pyd。这是你能够得到编译后 Python 代码的最接近方式。

此外,dll 不是标准,至少在 Python 领域中不是。它们也不具有可移植性。


你知道吗,我有一个用Python编写的模块。如果我在另一个脚本中导入它,就会生成*.pyc文件。虽然这很简单,但人们可以将*.pyc转换为源代码,而我不想让他们这样做。重写Cython模块应该不是一件容易的事情,对吧?顺便说一下,在Windows中dll是一种严格的标准,我的客户只使用Windows系统。 - Developer
你知道如何从Python中使用dll吗? - Kien Truong
棘手的问题!我知道那里有困难。哦,天啊,我只需要不提供源代码。怎么办?!我知道*.pyc只需准备好导入,没有头疼。现在我可以想象一个dll会有多难。 - Developer
使用类似py2exe的工具并不会增加反向工程的难度,与直接发布.pyc文件相比没有区别。 - patmanpato
4
但 DLL 在 Windows 环境中是标准的。 - rhody
开发者,如果你想操作DLL,你可能需要查看ctypes,但这并不是很有趣。 - Xantium

7

如果你有一个 DllMain,你可以在头文件中声明它为 "CFFI_DLLEXPORT int __stdcall DllMain(void* hinstDLL, unsigned long fdwReason, void* lpvReserved);"。 - Fax

7
你可以使用py2exe.org将Python脚本转换为Windows可执行文件。虽然这只在Windows上有效,但总比没有好。

1
从包中,我指的是一个包(模块),这意味着我可以分发一些Python代码,但对于某些模块,我只想分发dll文件。 - Developer
我可能错了,所以不要引用我的话,但是.exe文件可以像dll模块一样使用。因此,使用py2exe创建.exe文件,然后将其重命名为.dll,这样您的用户就不会尝试“运行”一个软件包。 - jb.
我认为你的想法值得一试。谢谢。 - Developer
可惜的是,exe文件和DLL文件是不同的东西。在C/C++编程中,DLL文件缺少main()函数。 - Xantium
DLLs有DllMain函数,用于处理进程和线程的附加和分离事件。如果你编译成EXE文件,就无法钩入这些事件了。 - Fax

6
你可以在C语言中嵌入Python。真正的诀窍是在C值和Python值之间进行转换。一旦完成了这个过程,制作DLL就相对简单
然而,为什么需要制作dll呢?你需要从非Python程序中使用它吗?

2
给定问题:1)dll是可移植的,2)没有分发源代码。 - Developer
1
这个想法,第一部分看起来不容易。有没有任何应用程序可以自动嵌入我的Python模块并给我一个可导入的dll? - Developer
1
“importable” dll是什么意思?可以从哪里导入?C++?Python?INTERCAL? - Nick ODell
谢谢提供链接。很有趣!我问了完全相同的问题。这里有118个赞,而-3!世界真有趣。 - Developer

3
我也建议使用Cython生成pyd文件,就像Dikei所写的那样。但是如果你真的想保护你的代码,最好还是用C++编写重要部分。最好的方法是结合C++Python。这个想法是:你会将python代码开放给用户进行调整,这样你就不必一遍又一遍地编译所有内容。这意味着,你会在C++中编写“核心”(这是目前最安全的解决方案),并在你的Python代码中使用那些dll文件。这真的取决于你正在构建什么样的工具或程序以及你想如何执行它。我通常在完成一个工具或程序后创建一个执行文件(exeapp),但这更多是为了最终用户。这可以通过py2exepy2app(都支持64位)来完成。如果你实现了解释器,则最终用户的机器不必在系统上安装Python。

pyd文件与dll文件相同,完全支持Python。因此,您可以正常导入您的模块。您可以在这里找到更多信息。

使用和生成pyd文件是创建安全且可移植的Python代码的最快最简单的方法。

您还可以使用C ++编写真实的dll文件,并使用ctypes导入它们以使用它们(在这里是一个很好的文章,以及如何工作的Python描述)。


3

对Nick ODell的回答进行补充

您必须在Windows上才能使用DLL,它们不具备可移植性。

但是,以下代码是跨平台的,所有平台都支持运行时,因此可以为需要其工作的每个平台重新编译它。

Python目前还没有提供创建dll的简单工具,但可以使用C/C++完成。


首先,您需要一个编译器(Windows默认没有),特别是Cygwin、MinGW或Visual Studio。

同时也需要基本的C语言知识(因为我们将主要使用C来编码)。

您还需要包含必要的头文件,我将跳过这部分,以免变得非常冗长,并假定一切都已正确设置。


为了演示,我们将打印传统的hello world:

我们将转换为DLL的Python代码:

def foo(): print("hello world")

C代码:

#include "Python.h" // Includes everything to use the Python-C API

int foo(void); // Declare foo

int foo(void) { // Name of our function in our DLL

    Py_Initialize(); // Initialise Python

    PyRun_SimpleString("print('hello world')"); // Run the Python commands

    return 0; // Finish execution
}

以下是嵌入Python的教程,与此相关的还有一些额外内容,但为了简洁起见,我将省略它们。

编译后,您应该会得到一个DLL文件。:)


但这还不够。您需要分发所需的任何依赖项,这意味着需要运行Python脚本的python36.dll运行时和其他一些组件。

我的C编码不完美,如果有人发现任何改进,请评论并尽力修复。


从这个回答中,也可能可以使用C#,因为C#可以创建DLL文件,并且您可以从C#调用Python函数:如何在C#中调用Python脚本中的特定方法?


0

这是我的想法,可能会起作用。我不知道那是否有效。

1.创建你的*.py文件。
2.将它们重命名为*.pyx
3.使用Cython将它们转换成*.c文件
4.将*.c编译成*.dll文件。

但我不建议你这样做,因为它只能在Windows平台上运行。


0
你可以使用pyinstaller将.py文件转换为可执行文件,并将所有所需的包转换为.dll格式。
步骤1. pip install pyinstaller,
步骤2. 新建一个Python文件,命名为code.py。
步骤3. 编写一些代码,例如print("Hello World")。
步骤4. 在相同位置打开命令提示符,输入pyinstaller code.py并按回车键。最后一步,在相同位置会创建两个文件夹:build、dist。在dist文件夹中,存在一个名为code的文件夹,其中包含一个exe文件code.exe以及所需的.dll文件。

有趣 - 你能提供一些代码来展示如何完成吗? - Ferdinando Randisi
1
步骤1. pip install pyinstaller步骤2. 新建一个Python文件,命名为code.py步骤3. 编写一些代码,例如print("Hello World")步骤4. 在相同的位置打开命令提示符,并输入pyinstaller code.py,然后按回车键。最后一步在相同的位置中将会创建两个文件夹,名称为build, dist。在dist文件夹中有一个名为code的文件夹,在该文件夹中有一个exe文件code.exe以及所需的.dll文件。 - Adnan Mohib
1
这将创建一个exe而不是DLL,DLL用于运行exe。 - Xantium

0

如果您的唯一目标是隐藏源代码,那么更简单的方法是将代码编译为可执行文件(例如使用PyInstaller),并使用具有可读源代码的模块进行通信。 注意:您可能需要更多的转换函数,就像这个例子中所示。 例子: 模块:

import subprocess
import codecs
def _encode_str(str):
    encoded=str.encode("utf-32","surrogatepass")
    return codecs.encode(encoded,"base64").replace(b"\n",b"")
def _decode_str(b64):
    return codecs.decode(b64,"base64").decode("utf-32","surrogatepass")
def strlen(s:str):#return length of str;int
    proc=subprocess.Popen(["path_to_your_exe.exe","strlen",_encode_str(str).decode("ascii")],stdout=subprocess.PIPE)
    return int(proc.stdout.read())
def random_char_from_string(str):
    proc=subprocess.Popen(["path_to_your_exe.exe","randchr",_encode_str(str).decode("ascii")],stdout=subprocess.PIPE)
    return _decode_str(proc.stdout.read())

可执行文件:

import sys
import codecs
import random
def _encode_str(str):
    encoded=str.encode("utf-32","surrogatepass")
    return codecs.encode(encoded,"base64").replace(b"\n",b"")
def _decode_str(b64):
    return codecs.decode(b64,"base64").decode("utf-32","surrogatepass")
command=sys.argv[1]
if command=="strlen":
    s=_decode_str(sys.argv[2].encode("ascii"))
    print(len(str))
if command=="randchr":
    s_decode_str(sys.argv[2].encode("ascii"))
    print(_encode_str(random.choice(s)).decode("ascii"))

你可能还想考虑为不同的平台编译不同的可执行文件,如果你的软件包不仅限于 Windows 平台的话。

1
也许可以添加一些注释或者对代码进行分解? - Mark
我在编写Python程序方面的经验有限,可能会有错误,但是PyInstaller或CxFreeze类型的“编译”实际上只是嵌入了Python解释器,创建了一些初始化模块,并打包了pyc文件和依赖项。这与公开源代码一样。建议通过生成子进程来处理函数调用是另一回事。总的来说,Python运行速度较慢,这种额外的CPU周期浪费只会让我们的地球变得更加温暖。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接