Windows 上的 Python 常见问题解答

如何在 Windows 上运行 Python 程序?

这不一定是一个简单的问题。如果你已经熟悉从 Windows 命令行运行程序,那么一切都会显得很明显;否则,你可能需要更多的指导。

除非你使用某种集成开发环境,否则你最终会在一个被称为“命令提示符窗口”中 键入 Windows 命令。通常你可以通过在搜索栏中搜索 cmd 来创建这样的窗口。你应该能够识别何时启动了这样的窗口,因为你会看到一个 Windows“命令提示符”,它通常看起来像这样

C:\>

字母可能不同,后面可能还有其他内容,所以你可能会很容易看到类似这样的内容

D:\YourName\Projects\Python>

这取决于你的计算机的设置方式以及你最近用它做了什么。一旦你启动了这样的窗口,你就离运行 Python 程序不远了。

你需要意识到你的 Python 脚本必须由另一个名为 Python 解释器 的程序处理。解释器读取你的脚本,将其编译成字节码,然后执行字节码来运行你的程序。那么,你如何安排解释器来处理你的 Python 呢?

首先,你需要确保你的命令窗口将“py”一词识别为启动解释器的指令。如果你已经打开了一个命令窗口,你应该尝试输入命令 py 并按回车键

C:\Users\YourName> py

然后你应该会看到类似这样的内容

Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>

你已经以“交互模式”启动了解释器。这意味着你可以交互式地输入 Python 语句或表达式,并让它们在你等待时被执行或评估。这是 Python 最强大的功能之一。通过输入你选择的一些表达式并查看结果来验证它

>>> print("Hello")
Hello
>>> "Hello" * 3
'HelloHelloHello'

许多人将交互模式用作方便且高度可编程的计算器。当你想要结束交互式 Python 会话时,调用 exit() 函数或按住 Ctrl 键并输入 Z,然后按“Enter”键返回到你的 Windows 命令提示符。

你可能还会发现有一个开始菜单条目,例如 开始 ‣ 程序 ‣ Python 3.x ‣ Python(命令行),这会导致你在新窗口中看到 >>> 提示符。如果是这样,在调用 exit() 函数或输入 Ctrl-Z 字符后,窗口将消失;Windows 在窗口中运行单个“python”命令,并在你终止解释器时关闭它。

现在我们知道 py 命令被识别了,你可以将你的 Python 脚本提供给它。你必须提供 Python 脚本的绝对路径或相对路径。假设你的 Python 脚本位于你的桌面,名为 hello.py,并且你的命令提示符已很好地在你的主目录中打开,所以你看到类似这样的内容

C:\Users\YourName>

所以现在你将通过键入 py 加上你的脚本路径来要求 py 命令将你的脚本交给 Python

C:\Users\YourName> py Desktop\hello.py
hello

如何使 Python 脚本可执行?

在 Windows 上,标准的 Python 安装程序已经将 .py 扩展名与文件类型 (Python.File) 关联起来,并为该文件类型提供了一个运行解释器的打开命令 (D:\Program Files\Python\python.exe "%1" %*)。这足以使脚本可以在命令提示符下以 'foo.py' 形式执行。如果你希望能够通过简单地键入 'foo' 而不带扩展名来执行脚本,你需要将 .py 添加到 PATHEXT 环境变量中。

为什么 Python 有时启动会这么慢?

通常 Python 在 Windows 上启动非常快,但偶尔会有错误报告称 Python 突然开始启动很长时间。这变得更加令人费解,因为 Python 在其他配置似乎相同的 Windows 系统上运行良好。

问题可能是由问题机器上病毒检查软件配置错误引起的。已知某些病毒扫描程序在配置为监视文件系统中的所有读取时会引入两个数量级的启动开销。尝试检查系统上病毒扫描软件的配置,以确保它们确实配置相同。McAfee 在配置为扫描所有文件系统读取活动时是一个特别的罪魁祸首。

如何从 Python 脚本制作可执行文件?

有关可用于制作可执行文件的工具列表,请参阅如何从 Python 脚本创建独立二进制文件?

*.pyd 文件与 DLL 相同吗?

是的,.pyd 文件就是 DLL,但有一些区别。如果你有一个名为 foo.pyd 的 DLL,那么它必须有一个函数 PyInit_foo()。然后你可以编写 Python“import foo”,Python 将搜索 foo.pyd(以及 foo.py、foo.pyc),如果找到它,将尝试调用 PyInit_foo() 来初始化它。你不需要将你的 .exe 与 foo.lib 链接,因为那会导致 Windows 要求 DLL 存在。

请注意,foo.pyd 的搜索路径是 PYTHONPATH,与 Windows 用于搜索 foo.dll 的路径不同。此外,foo.pyd 不必存在即可运行你的程序,而如果你将程序与 DLL 链接,则 DLL 是必需的。当然,如果你想说 import foo,foo.pyd 是必需的。在 DLL 中,链接是在源代码中用 __declspec(dllexport) 声明的。在 .pyd 中,链接是在可用函数列表中定义的。

如何将 Python 嵌入到 Windows 应用程序中?

将 Python 解释器嵌入 Windows 应用程序可以概括如下

  1. 不要 将 Python 直接构建到你的 .exe 文件中。在 Windows 上,Python 必须是一个 DLL 才能处理导入本身也是 DLL 的模块。(这是第一个关键的未文档化事实。)相反,链接到 pythonNN.dll;它通常安装在 C:\Windows\System 中。NN 是 Python 版本,例如 Python 3.3 的“33”。

    你可以通过两种不同的方式链接到 Python。加载时链接意味着链接到 pythonNN.lib,而运行时链接意味着链接到 pythonNN.dll。(一般注意:pythonNN.lib 是与 pythonNN.dll 对应的所谓“导入库”。它仅为链接器定义符号。)

    运行时链接大大简化了链接选项;一切都在运行时发生。你的代码必须使用 Windows LoadLibraryEx() 例程加载 pythonNN.dll。代码还必须使用通过 Windows GetProcAddress() 例程获得的指针访问 pythonNN.dll 中的访问例程和数据(即 Python 的 C API)。宏可以使任何调用 Python C API 中例程的 C 代码透明地使用这些指针。

  2. 如果你使用 SWIG,可以轻松创建 Python“扩展模块”,从而使应用程序的数据和方法可供 Python 使用。SWIG 会为你处理几乎所有繁琐的细节。结果是 C 代码,你将其 链接 到你的 .exe 文件中(!)你不必创建 DLL 文件,这也简化了链接。

  3. SWIG 将创建一个 init 函数(一个 C 函数),其名称取决于扩展模块的名称。例如,如果模块的名称是 leo,则 init 函数将被称为 initleo()。如果你使用 SWIG 阴影类,你应该这样做,init 函数将被称为 initleoc()。这会初始化阴影类使用的几乎隐藏的辅助类。

    你可以在步骤 2 中将 C 代码链接到 .exe 文件中的原因是,调用初始化函数等同于将模块导入到 Python 中!(这是第二个关键的未文档化事实。)

  4. 简而言之,你可以使用以下代码通过你的扩展模块初始化 Python 解释器。

    #include <Python.h>
    ...
    Py_Initialize();  // Initialize Python.
    initmyAppc();  // Initialize (import) the helper class.
    PyRun_SimpleString("import myApp");  // Import the shadow class.
    
  5. Python 的 C API 有两个问题,如果你使用 MSVC 以外的编译器(用于构建 pythonNN.dll 的编译器),这些问题将变得显而易见。

    问题 1:接受 FILE * 参数的所谓“极高级”函数在多编译器环境中将不起作用,因为每个编译器对 struct FILE 的概念都不同。从实现的角度来看,这些是非常低级的函数。

    问题 2:当为 void 函数生成包装器时,SWIG 会生成以下代码

    Py_INCREF(Py_None);
    _resultobj = Py_None;
    return _resultobj;
    

    唉,Py_None 是一个宏,它扩展为对 pythonNN.dll 中名为 _Py_NoneStruct 的复杂数据结构的引用。同样,此代码在多编译器环境中会失败。将此类代码替换为

    return Py_BuildValue("");
    

    可能可以使用 SWIG 的 %typemap 命令自动进行更改,尽管我尚未成功实现(我是一个完全的 SWIG 新手)。

  6. 从你的 Windows 应用程序内部使用 Python shell 脚本弹出一个 Python 解释器窗口不是一个好主意;生成的窗口将独立于你的应用程序的窗口系统。相反,你(或 wxPythonWindow 类)应该创建一个“原生”解释器窗口。将该窗口连接到 Python 解释器很容易。你可以将 Python 的 I/O 重定向到支持读写的_任何_对象,所以你只需要一个包含 read() 和 write() 方法的 Python 对象(在你的扩展模块中定义)。

如何防止编辑器在我的 Python 源文件中插入制表符?

常见问题解答不建议使用制表符,Python 风格指南 PEP 8 建议对分发的 Python 代码使用 4 个空格;这也是 Emacs python-mode 的默认设置。

在任何编辑器中,混用制表符和空格都不是一个好主意。MSVC 在这方面也不例外,并且很容易配置为使用空格:选择 工具 ‣ 选项 ‣ 制表符,对于文件类型“默认”,将“制表符大小”和“缩进大小”设置为 4,并选择“插入空格”单选按钮。

如果混合制表符和空格在引导空白中导致问题,Python 会引发 IndentationErrorTabError。你还可以运行 tabnanny 模块以批处理模式检查目录树。

如何检查按键而不阻塞?

使用 msvcrt 模块。这是一个标准的 Windows 专用扩展模块。它定义了一个函数 kbhit() 用于检查是否有按键存在,以及 getch() 用于获取一个字符而不回显它。

如何解决 api-ms-win-crt-runtime-l1-1-0.dll 缺失错误?

这可能发生在 Python 3.5 及更高版本在使用 Windows 8.1 或更早版本且未安装所有更新时。首先确保你的操作系统受支持且已更新,如果这不能解决问题,请访问 Microsoft 支持页面 以获取手动安装 C 运行时更新的指导。