我想使用python 3.6(64位)从VS 2019x64 Native Tools命令提示符内将python3包编译成可分发的二进制形式(没有源代码)。但是,我在指定包应该包含的文件的正确路径时遇到了问题。site-packages目录中生成的文件夹结构都是错误的,而不是我对源文件夹结构和setup.py
的期望。对我来说,似乎来自顶层包的模块被不同地对待,但我不知道为什么会这样。
目录结构如下:
.
├── hello
│ ├── hello1.py
│ ├── hello2.py
│ └── __init__.py
│ bye
│ ├── bye1.py
│ ├── bye2.py
│ └── __init__.py
├── setup.py
└── test
└── test.py
hello
包中的__init__.py
包含from . import hello1, hello2, bye
行,bye
包中的__init__.py
包含from . import bye1, bye2
行。hello1.py
和hello2.py
分别包含print_hello
和print_hello2
函数,bye1.py
和bye2.py
包含print_bye
和print_bye2
函数。
setup.py
:
from setuptools import Extension, setup, find_packages
from Cython.Build import cythonize
setup(name='hello',
version='0.3',
url='https://someurl',
license='somelicense',
author='Pepper Wutz',
author_email='wutz@gmail.com',
description='Prints "Hello World"',
ext_modules=cythonize([Extension("hello", ["hello/*.py"]), Extension("hello.bye", ["hello/bye/*.py"]), language_level=3),
zip_safe=False)
当我从top_level文件夹中运行这个模块时:
py -3.6 setup.py bdist_wheel
,然后安装它(从tests子文件夹中):
py -3.6 -m pip install dist/hello-0.3-cp36-cp36m-win_amd64.whl
得到如下输出文件:
Verzeichnis von C:UsersUserAppDataRoamingPythonPython36site-packages
18.01.2021 17:32 <DIR> hello
18.01.2021 17:32 <DIR> hello-0.3.dist-info
18.01.2021 17:32 20.480 hello.cp36-win_amd64.pyd
1 Datei(en), 20.480 Bytes
2 Verzeichnis(se), 15.070.900.224 Bytes frei
Verzeichnis von C:UsersUserAppDataRoamingPythonPython36site-packageshello
18.01.2021 17:32 <DIR> .
18.01.2021 17:32 <DIR> ..
18.01.2021 17:32 20.480 bye.cp36-win_amd64.pyd
1 Datei(en), 20.480 Bytes
2 Verzeichnis(se), 15.070.142.464 Bytes frei
显然,site-packages中hello包的文件夹结构与我原来的文件夹结构有很大的不同。例如,将hello.cp36-win_amd64.pyd
放入顶级作用域,将bye包分别放入hello子目录的子作用域。hello1
和hello2
模块不放在hello包中,只放在bye包中。这完全搞乱了从解释器运行代码时工作的相对导入:
C:UsersUsersourcereposcython_teststest>py -3.6 -c "import hello; print(dir(hello))"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "hello__init__.py", line 1, in init hello
ImportError: attempted relative import with no known parent package
值得怀疑的是,包含hello源代码的hello
文件夹中只有__init__.c
文件,而没有hello1.c
和hello2.c
文件。hellobye
文件夹也是如此。
18.01.2021 17:27 <DIR> .
18.01.2021 17:27 <DIR> ..
18.01.2021 17:27 <DIR> bye
18.01.2021 10:58 147 hello1.py
15.01.2021 20:11 150 hello2.py
18.01.2021 17:27 103.044 __init__.c
18.01.2021 17:20 35 __init__.py
4 Datei(en), 103.376 Bytes
3 Verzeichnis(se), 15.068.041.216 Bytes frei
我只是迷失在写适当的setup.py
,特别是指定ext_modules
。我不知道我做错了什么。也许其他人看到了。我想要获得的是一个二进制包,我可以使用import hello
导入,从中我可以调用hello1和hello2中的函数,像这样:hello.hello1.print_hello()
,hello.hello2.print_hello2()
以及hello.bye.bye1.print_bye()
,hello.bye.bye2.print_bye2()
。
您可能想做的是为每个.py文件创建一个包含扩展模块的包。
setup.py包含:
ext_modules=cythonize([
Extension("hello.hello1", ["hello/hello1.py"]),
Extension("hello.hello2", ["hello/hello2.py"]),
Extension("bye.bye1", ["bye/bye1.py"]),
Extension("bye.bye2", ["bye/bye2.py"]), language_level=3),
我跳过了__init__.py
文件,因为在Cython中编译它们通常没有什么价值,而且你必须绕过Windows上的setuptools错误。
PyInit_<module_name>
函数可以调用。因此,当Cython编译一个文件(例如hello1.py
)时,它将创建一个PyInit_hello1
函数。
因此你假定的组合"hello"模块最终包含PyInit_hello1
,PyInit_hello2
,但没有PyInit_hello
,因此不导入。
如果你真的想要将多个模块捆绑到一个。so文件中,那么你可以按照下面的说明将多个子模块合并到一个Cython扩展中。您会注意到它不是一个内置特性,它涉及到Python导入机制的许多细节。或者,你也可以使用一些第三方工具来实现自动化(例如https://github.com/smok-serwis/snakehouse)。我不建议这样做,因为它很复杂,而且可能有点脆弱。