当前位置:   article > 正文

python打包之一文打尽:pyinstaller 和 nuitka 打包参数与使用方法详解_python nuitka

python nuitka

应用程序开发完毕,一般都需要对程序进行打包后发布,除开源项目外,很少有发布源码的。另一方面,Python 程序的运行必须要有 Python 的环境,如果每次都给客户电脑上部署一个python运行环境,就显得啰嗦又复杂,这时我们就要将 Python 程序打包为 exe 文件。这样,在 Windows 平台下,就可以直接运行该程序,不论有没有 Python 环境。

python程序的打包模块也比较多,但常用的有两种,pyinstaller和nuitka,掌握这两个打包模块,一般的项目打包就能够应付了。本文仅对这两种打包模块的常用命令进行介绍,供程序打包时做参考。

一、pyinstaller

最终因此拿出一节来记录一下我这两天的历程。我只能说没有解决不了的问题只有定义不清晰的问题,只要方向对了,问题会随着了解的加深而迎刃而解。

(一)安装pyinstaller

在终端安装,,相对比较简单:

pip install pyinstaller

注意,如果没有打开虚拟环境,此时的安装位置在全局环境中,使用的时候要注意。

(二)一般打包方法

1.打包命令形式

在你想放置应用的文件夹下打开cmd,pyinstaller + 参数 +文件入口或打包定义文档

注意观察上面的目录结构和命令形式。

2. 打包参数

(1)通用参数
参数名描述说明
-h显示帮助
-v显示版本号
–distpath生成文件放在哪里默认:当前目录的dist文件夹内
–workpath生成过程中的中间文件放在哪里默认:当前目录的build文件夹内
-y如果dist文件夹内已经存在生成文件,则不询问用户,直接覆盖默认:询问是否覆盖
–upx-dir UPX_DIR指定upx工具的目录默认:execution path
-a不包含unicode支持默认:尽可能支持unicode
–clean在本次编译开始时,清空上一次编译生成的各种文件默认:不清除
–log-level LEVEL控制编译时pyi打印的信息一共有6个等级,由低到高分别为TRACE DEBUG INFO(默认) WARN ERROR CRITICAL。也就是默认清空下,不打印TRACE和DEBUG信息
(2)与生成结果有关的参数
参数名描述说明
-D生成one-folder的程序(默认)生成结果是一个目录,各种第三方依赖、资源和exe同时存储在该目录
-F生成one-file的程序生成结果是一个exe文件,所有的第三方依赖、资源和代码均被打包进该exe内
–specpath指定.spec文件的存储路径默认:当前目录
-n生成的.exe文件和.spec的文件名默认:用户脚本的名称,即main.py和main.spec
(3)指定打包哪些资源、代码
参数名描述说明
–add-data打包额外资源用法:pyinstaller main.py --add-data=src;dest。windows以;分割,linux以:分割
–add-binary打包额外的代码用法:同–add-data。与–add-data不同的是,用binary添加的文件,pyi会分析它引用的文件并把它们一同添加进来
-p指定额外的import路径,类似于使用PYTHONPATH参见PYTHONPATH
–hidden-import打包额外py库pyi在分析过程中,有些import没有正确分析出来,运行时会报import error,这时可以使用该参数
–additional-hooks-dir指定用户的hook目录hook用法参见其他,系统hook在PyInstaller\hooks目录下
–runtime-hook指定用户runtime-hook如果设置了此参数,则runtime-hook会在运行main.py之前被运行
–exclude-module需要排除的modulepyi会分析出很多相互关联的库,但是某些库对用户来说是没用的,可以用这个参数排除这些库,有助于减少生成文件的大小
–keypyi会存储字节码,指定加密字节码的key16位的字符串

–add-binary 使用实例可以参考:《python3 pyinstaller 打包后执行文件运行错误 No such file or directory 和 Cannot load native module 解决方法》python3 pyinstaller 打包后执行文件运行错误 No such file or directory 和 Cannot load native module 解决方法_whatday的博客-CSDN博客

(4)生成参数
参数名描述说明
-d执行生成的main.exe时,会输出pyi的一些log,有助于查错默认:不输出pyi的log
-s优化符号表原文明确表示不建议在windows上使用
–noupx强制不使用upx默认:尽可能使用。
(5)其他
参数名描述说明
–runtime-tmpdir指定运行时的临时目录默认:使用系统临时目录
(6)Windows和Mac特有的参数
参数名描述说明
-c显示命令行窗口与-w相反,默认含有此参数
-w不显示命令行窗口编写GUI程序时使用此参数有用。
-i为main.exe指定图标pyinstaller -i beauty.ico main.py
(7)Windows特有的参数
参数名描述说明
–version-file添加版本信息文件pyinstaller --version-file ver.txt
-m, --manifest添加manifest文件pyinstaller -m main.manifest
-r RESOURCE请参考原文
–uac-admin请参考原文
–uac-uiaccess请参考原文

常用参数有以下几个:
-F:仅仅生成一个文件,不暴露其他信息,启动较慢。
-D:生成一个文件夹,里面是多文件模式,启动快。
-w:窗口模式打包,不显示控制台。
-c:跟图标路径,作为应用icon。

3.打包示例

(1)一般打包
pyinstaller -F 文件名.py

此时双击生成的程序,会出来一个CMD控制台,如果程序有输出,会在控制台上输出相应的程序运转过程。

(2)打包pyqt5文件
  1. pyinstaller --paths PyQt5模块路径 -D -w --icon=窗口图标文件 文件名.py
  2. # 参数说明:
  3. # --paths:指定第三方模块的安装路径。
  4. # -w:表示窗口程序。
  5. # --icon:可选项,如果设置了窗口图标,则指定相应文件路径;如果没有,则省略。
  6. # 文件名.py:窗口程序的入口文件。

此时打包生成一个带依赖资源文件夹的程序,不显示控制台窗口,并装软件图标打入程序。

(三)报错处理

1 . ModuleNotFoundError: No module named 没有找到模块的问题

直接输入命令

 pyinstaller -F -p (项目目录\venv\Lib\site-packages) (py文件路径)

即可.

2. 打包含ddddocr模块的程序,显示 onnxruntime_providers_shared.dll 、common.onnx缺少的问题

见前面的文章:pyinstaller快捷打包含ddddocr模块的程序,解决 onnxruntime_providers_shared.dll 、common.onnx缺少的问题_pyinstaller ddddocr-CSDN博客

二、Nuitka

Nuitka是一个Python代码打包工具,它可以将Python代码转换成C或C++代码,并最终生成一个可执行文件。

 官方参考网站

Nuitka the Python Compiler — Nuitka the Python Compiler documentation

Nuitka的目标是创建一个功能齐全的Python解释器的C语言对应版本,这样就可以将Python代码编译成可执行文件,同时也可以实现Python的动态类型特性。

使用Nuitka可以有效地提高Python代码的运行效率,因为它将Python代码转换成C或C++代码,并使用高效的编译器将其编译成二进制代码。此外,Nuitka还提供了很多优化选项,可以进一步改善代码的效率。

(一)安装nuitka

pip install nuitka

(二)一般打包方法

1. 最简单的方法:

nuitka 程序文件.py

使用这条命令生成的exe可以正常运行。

但如果将生成的exe拷贝到其他路径执行,将会执行失败,提示找不到mdl模块,这是因为使用上述命令生成的exe里面是并不包含上面的mdl模块,于是在执行该exe的时候,它会尝试去调用mdl.py,如果找不到mdl.py,当然就会提示找不到模块,这和py文件的执行原理是一样的。

exe同级目录下面会有一个python3x.dll文件,执行exe文件时,如果需要调用外部模块,需要这个文件的支持。

关于python3x.dll
python3x.dll是与版本相对应的,如果是python3.8.3,那么就是python38.dll。实际上,这个文件和python安装目录下面的python3x.dll是同一个文件(的拷贝)。python3x.dll相当于一个运行时(runtime),类似于javac,或者微软的framwork,python代码通过nuitka生成了二进制代码(exe或者pyd),但仍然通过python3x.dll保留了python的特性,比如调用一个python模块

现在将整个目录拷贝到其他路径,可以发现exe能够正常执行,因为此时exe文件能够通过python3x.dll找到mdl.py。

如果不想借助python3x.dll去调用模块,就要将这个模块打包进exe

nuitka --follow-import-to=mdl hello.py

再次将生成的exe单独拷贝到其他地方执行,可以看到能够正常运行,此时不再需要拷贝python3x.dll和其他任何的文件。

2.打包tk-inter开发的GUI程序

  1. nuitka --standalone --onefile --enable-plugin=tk-inter --remove-output --windows-disable-console xxx.py
  2. 参数说明:
  3. --standalone 独立环境,使结果可移植
  4. --onefile 打包为单个exe文件
  5. --enable-plugin=tk-inter 打包tkinter模块的需要
  6. --remove-output 打包结束后删除产生的临时文件
  7. --windows-disable-console 去掉运行时cmd窗口

此处注意和上面用pyinstall打包pyqt5的打包方法对比。

3.打包成模块(module)

这个功能主要用于对程序进行加密,重要的算法和核心代码,如果需要保护,可以写到一个文件中,然后将这个文件再打包成二进制模块,对外只留调用接口,这样就不至于泄漏算法了。

  1. # 指令示例
  2. nuitka --mingw64 --plugin-enable=numpy --module ./self_strategy/TaLib_extension_numpy.py

4.复杂一点的打包命令解析

  1. nuitka --standalone --mingw64 --show-memory --show-progress --nofollow-imports --enable-plugin=pyqt5 --follow-import-to=need --output-dir=output 你的.py
  2. # 参数说明:
  3. # --standalone 独立环境,使结果可移植
  4. # --mingw64 指定c/c++编译器
  5. # --show-memory 显示内存的占用
  6. # --show-progress 显示编译的进度
  7. # --nofollow-imports 所有的import不编译,交给python3x.dll执行
  8. # --enable-plugin=pyqt5 打包pyqt5模块的需要
  9. # --follow-import-to=need need为你需要编译成C/C++的py文件夹命名
  10. # --output-dir=output 输出文件目录
  11. # --onefile 打包为单个exe文件

注意:刚开始调试的时候, --windows-disable-console这个命令一定不要加入,否则看不到报错

(三)常用参数速查

  1. --standalone 独立环境,使结果可移植
  2. --windows-disable-console 去掉CMD控制窗口
  3. --output-dir=out 生成exe到out文件夹下面去
  4. --show-progress 显示编译的进度
  5. --show-memory 显示内存的占用
  6. --enable-plugin=pyside6 打包pyside6模块的需要
  7. --plugin-enable=tk-inter 打包tkinter模块的需要
  8. --plugin-enable=numpy 打包numpy,pandas,matplotlib模块的需要
  9. --plugin-enable=torch 打包pytorch的需要
  10. --plugin-enable=tensorflow 打包tensorflow的需要
  11. --windows-icon-from-ico=你的.ico 软件的图标
  12. --windows-company-name=Windows下软件公司信息
  13. --windows-product-name=Windows下软件名称
  14. --windows-file-version=Windows下软件的信息
  15. --windows-product-version=Windows下软件的产品信息
  16. --windows-file-description=Windows下软件的作用描述
  17. --windows-uac-admin=Windows下用户可以使用管理员权限来安装
  18. --linux-onefile-icon=Linux下的图标位置
  19. --onefile 打包为单个exe文件
  20. --include-package=复制比如numpy,PyQt5 这些带文件夹的叫包或者轮子
  21. --include-module=复制比如when.py 这些以.py结尾的叫模块
  22. –-include-package-data=包含给定软件包名称中的数据文件,等号后软件包名称。有的时候Nuitka并不能正确分析出一些Python软件包所需要使用的数据文件,在运行程序时提示FileNotFoundError等错误,此时就需要使用该选项。如:--include-package-data=ultralytics
  23. –-include-data-files= 按文件名包含数据文件,等号后的格式为<SRC=DEST>。SRC指的是文件夹的路径,DEST指的是文件夹相对于打包结果的路径,其中DEST只能使用相对路径。如:--include-data-files=/Users/admin/Downloads/yolov5n.pt=./yolov5n.pt
  24. -–include-data-dir= 包含文件夹中的数据文件,等号后的格式为<SRC=DEST>。使用方法与--include-data-files=相同。
  25. --follow-import-to=MODULE/PACKAGE 如果使用该模块,请遵循该模块;如果是一个包,请遵循整个包。可以多次给定。默认为空。

(四)nuitka打包优缺点

1.Nuitka优点:

程序运行速度较快。

相对于pyinstaller,打包之后的程序占用空间较小。

2.Nuitka缺点:

打包时间比较长,新手非常容易出错。

(五)注意事项

1. 首先下载并安装c/c++编译器

推荐使用https://download.csdn.net/download/qq_58168857/88152647?spm=1001.2014.3001.5503,经过本人多个项目验证,此编译器能解决您的大部分涉及c/c++ 软件包的安装、编译调用出错问题。

2 .关于参数--mingw64

实际上 --mingw64--msvc=MSVC是一对孪生参数,这两个参数二选一,用于指定编译器,如果当前环境既安装了mingw64,又安装了msvc,可以使用该参数选择兼容性最好的编译器,建议使用mingw64。如果不存在上面两种编译器都存在的情况,就不需要显式设置这个参数,默认会调用系统中能用的编译器。

3.关于直接用C语言开发的python包的打包

  •  Numpy等类似c程式和pyd的调用还是忽略编译好,有大佬测试,编译后反而使打包出来的程序运行更慢。
  • PyQT,Numpy,Scipy,Pandas,Opencv,OpenpyXL等pyd的模块不编译,交给python3x.dll来调用,避免模块依赖失败。生成的UI_xxx.py文件和你编写的py模块(可以包含IP,密码)放到一个下一级的文件夹,设置为必须编译为C/C++。从此你的打包成功率提升到95%,exe打开速度提升到一秒左右


4.参数plugin control

这部分参数用于设置对某些第三方库或者python功能进行支持,在使用--standalone时才会用到

(1)如果程序中使用了pyqt或者pyside,那么  --plugin-enable = qt-plugins

(2)如果程序中使用了numpy, scipy, pandas, matplotlib,那么 --plugin-enable=numpy

如果使用了这些库或功能,但是忘了进行插件参数设置,命令运行过程中会以红字今天提醒,按照提醒对命令进行调整即可

(3)如果有多个插件需要启用 --plugin-enable=numpy   --plugin-enable=qt-plugins  --plugin-enable=tensorflow

可以使用 nuitka --plugin-list查看可用的插件
 

三、附件:nuitka参数列表

  1. Usage: __main__.py [--module] [--run] [options] main_module.py
  2. Options:
  3. --version show program's version number and exit
  4. -h, --help show this help message and exit
  5. --module Create an extension module executable instead of a
  6. program. Defaults to off.
  7. --standalone Enable standalone mode in build. This allows you to
  8. transfer the created binary to other machines without
  9. it relying on an existing Python installation. It
  10. implies these option: "--recurse-all". You may also
  11. want to use "--python-flag=no_site" to avoid the
  12. "site.py" module, which can save a lot of code
  13. dependencies. Defaults to off.
  14. --python-arch=PYTHON_ARCH
  15. Architecture of Python to use. One of "x86" or
  16. "x86_64". Defaults to what you run Nuitka with
  17. (currently "x86_64").
  18. --python-debug Use debug version or not. Default uses what you are
  19. using to run Nuitka, most likely a non-debug version.
  20. --python-flag=PYTHON_FLAGS
  21. Python flags to use. Default uses what you are using
  22. to run Nuitka, this enforces a specific mode. These
  23. are options that also exist to standard Python
  24. executable. Currently supported: "-S" (alias
  25. "nosite"), "static_hashes" (do not use hash
  26. randomization), "no_warnings" (do not give Python
  27. runtime warnings), "-O" (alias "noasserts"). Default
  28. empty.
  29. --python-for-scons=PYTHON_SCONS
  30. If using Python3.3 or Python3.4, provide the path of a
  31. Python binary to use for Scons. Otherwise Nuitka can
  32. use what you run Nuitka with or a "scons" binary that
  33. is found in PATH, or a Python installation from
  34. Windows registry.
  35. --warn-implicit-exceptions
  36. Enable warnings for implicit exceptions detected at
  37. compile time.
  38. --warn-unusual-code Enable warnings for unusual code detected at compile
  39. time.
  40. --assume-yes-for-downloads
  41. Allow Nuitka to download code if necessary, e.g.
  42. dependency walker on Windows.
  43. Control the inclusion of modules and packages:
  44. --include-package=PACKAGE
  45. Include a whole package. Give as a Python namespace,
  46. e.g. ``some_package.sub_package`` and Nuitka will then
  47. find it and include it and all the modules found below
  48. that disk location in the binary or extension module
  49. it creates, and make it available for import by the
  50. code. Default empty.
  51. --include-module=MODULE
  52. Include a single module. Give as a Python namespace,
  53. e.g. ``some_package.some_module`` and Nuitka will then
  54. find it and include it in the binary or extension
  55. module it creates, and make it available for import by
  56. the code. Default empty.
  57. --include-plugin-directory=MODULE/PACKAGE
  58. Include the content of that directory, no matter if
  59. it's used by the given main program in a visible form.
  60. Overrides all other recursion options. Can be given
  61. multiple times. Default empty.
  62. --include-plugin-files=PATTERN
  63. Include into files matching the PATTERN. Overrides all
  64. recursion other options. Can be given multiple times.
  65. Default empty.
  66. Control the recursion into imported modules:
  67. --follow-stdlib, --recurse-stdlib
  68. Also descend into imported modules from standard
  69. library. This will increase the compilation time by a
  70. lot. Defaults to off.
  71. --nofollow-imports, --recurse-none
  72. When --recurse-none is used, do not descend into any
  73. imported modules at all, overrides all other recursion
  74. options. Defaults to off.
  75. --follow-imports, --recurse-all
  76. When --recurse-all is used, attempt to descend into
  77. all imported modules. Defaults to off.
  78. --follow-import-to=MODULE/PACKAGE, --recurse-to=MODULE/PACKAGE
  79. Recurse to that module, or if a package, to the whole
  80. package. Can be given multiple times. Default empty.
  81. --nofollow-import-to=MODULE/PACKAGE, --recurse-not-to=MODULE/PACKAGE
  82. Do not recurse to that module name, or if a package
  83. name, to the whole package in any case, overrides all
  84. other options. Can be given multiple times. Default
  85. empty.
  86. Immediate execution after compilation:
  87. --run Execute immediately the created binary (or import the
  88. compiled module). Defaults to off.
  89. --debugger, --gdb Execute inside "gdb" to automatically get a stack
  90. trace. Defaults to off.
  91. --execute-with-pythonpath
  92. When immediately executing the created binary
  93. (--execute), don't reset PYTHONPATH. When all modules
  94. are successfully included, you ought to not need
  95. PYTHONPATH anymore.
  96. Dump options for internal tree:
  97. --xml Dump the final result of optimization as XML, then
  98. exit.
  99. Code generation choices:
  100. --full-compat Enforce absolute compatibility with CPython. Do not
  101. even allow minor deviations from CPython behavior,
  102. e.g. not having better tracebacks or exception
  103. messages which are not really incompatible, but only
  104. different. This is intended for tests only and should
  105. not be used for normal use.
  106. --file-reference-choice=FILE_REFERENCE_MODE
  107. Select what value "__file__" is going to be. With
  108. "runtime" (default for standalone binary mode and
  109. module mode), the created binaries and modules, use
  110. the location of themselves to deduct the value of
  111. "__file__". Included packages pretend to be in
  112. directories below that location. This allows you to
  113. include data files in deployments. If you merely seek
  114. acceleration, it's better for you to use the
  115. "original" value, where the source files location will
  116. be used. With "frozen" a notation "<frozen
  117. module_name>" is used. For compatibility reasons, the
  118. "__file__" value will always have ".py" suffix
  119. independent of what it really is.
  120. Output choices:
  121. -o FILENAME Specify how the executable should be named. For
  122. extension modules there is no choice, also not for
  123. standalone mode and using it will be an error. This
  124. may include path information that needs to exist
  125. though. Defaults to <program_name> on this platform.
  126. .exe
  127. --output-dir=DIRECTORY
  128. Specify where intermediate and final output files
  129. should be put. The DIRECTORY will be populated with C
  130. files, object files, etc. Defaults to current
  131. directory.
  132. --remove-output Removes the build directory after producing the module
  133. or exe file. Defaults to off.
  134. --no-pyi-file Do not create a ".pyi" file for extension modules
  135. created by Nuitka. This is used to detect implicit
  136. imports. Defaults to off.
  137. Debug features:
  138. --debug Executing all self checks possible to find errors in
  139. Nuitka, do not use for production. Defaults to off.
  140. --unstripped Keep debug info in the resulting object file for
  141. better debugger interaction. Defaults to off.
  142. --profile Enable vmprof based profiling of time spent. Not
  143. working currently. Defaults to off.
  144. --graph Create graph of optimization process. Defaults to off.
  145. --trace-execution Traced execution output, output the line of code
  146. before executing it. Defaults to off.
  147. --recompile-c-only This is not incremental compilation, but for Nuitka
  148. development only. Takes existing files and simply
  149. compile them as C again. Allows compiling edited C
  150. files for quick debugging changes to the generated
  151. source, e.g. to see if code is passed by, values
  152. output, etc, Defaults to off. Depends on compiling
  153. Python source to determine which files it should look
  154. at.
  155. --generate-c-only Generate only C source code, and do not compile it to
  156. binary or module. This is for debugging and code
  157. coverage analysis that doesn't waste CPU. Defaults to
  158. off. Do not think you can use this directly.
  159. --experimental=EXPERIMENTAL
  160. Use features declared as 'experimental'. May have no
  161. effect if no experimental features are present in the
  162. code. Uses secret tags (check source) per experimented
  163. feature.
  164. --disable-dll-dependency-cache
  165. Disable the dependency walker cache. Will result in
  166. much longer times to create the distribution folder,
  167. but might be used in case the cache is suspect to
  168. cause errors.
  169. --force-dll-dependency-cache-update
  170. For an update of the dependency walker cache. Will
  171. result in much longer times to create the distribution
  172. folder, but might be used in case the cache is suspect
  173. to cause errors or known to need an update.
  174. Backend C compiler choice:
  175. --clang Enforce the use of clang. On Windows this requires a
  176. working Visual Studio version to piggy back. Defaults
  177. to off.
  178. --mingw64 Enforce the use of MinGW64 on Windows. Defaults to
  179. off.
  180. --msvc=MSVC Enforce the use of specific MSVC version on Windows.
  181. Allowed values are e.g. 14.0, specify an illegal value
  182. for a list of installed compilers. Defaults to the
  183. most recent version.
  184. -j N, --jobs=N Specify the allowed number of parallel C compiler
  185. jobs. Defaults to the system CPU count.
  186. --lto Use link time optimizations if available and usable
  187. (gcc 4.6 and higher). Defaults to off.
  188. Tracing features:
  189. --show-scons Operate Scons in non-quiet mode, showing the executed
  190. commands. Defaults to off.
  191. --show-progress Provide progress information and statistics. Defaults
  192. to off.
  193. --show-memory Provide memory information and statistics. Defaults to
  194. off.
  195. --show-modules Provide a final summary on included modules. Defaults
  196. to off.
  197. --verbose Output details of actions taken, esp. in
  198. optimizations. Can become a lot. Defaults to off.
  199. Windows specific controls:
  200. --windows-dependency-tool=DEPENDENCY_TOOL
  201. When compiling for Windows, use this dependency tool.
  202. Defaults to depends.exe, other allowed value is
  203. 'pefile'.
  204. --windows-disable-console
  205. When compiling for Windows, disable the console
  206. window. Defaults to off.
  207. --windows-icon=ICON_PATH
  208. Add executable icon (Windows only).
  209. Plugin control:
  210. --plugin-enable=PLUGINS_ENABLED, --enable-plugin=PLUGINS_ENABLED
  211. Enabled plugins. Must be plug-in names. Use --plugin-
  212. list to query the full list and exit. Default empty.
  213. --plugin-disable=PLUGINS_DISABLED, --disable-plugin=PLUGINS_DISABLED
  214. Disabled plugins. Must be plug-in names. Use --plugin-
  215. list to query the full list and exit. Default empty.
  216. --plugin-no-detection
  217. Plugins can detect if they might be used, and the you
  218. can disable the warning via --plugin-disable=plugin-
  219. that-warned, or you can use this option to disable the
  220. mechanism entirely, which also speeds up compilation
  221. slightly of course as this detection code is run in
  222. vain once you are certain of which plug-ins to use.
  223. Defaults to off.
  224. --plugin-list Show list of all available plugins and exit. Defaults
  225. to off.
  226. --user-plugin=USER_PLUGINS
  227. The file name of user plugin. Can be given multiple
  228. times. Default empty.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/657381
推荐阅读
相关标签
  

闽ICP备14008679号