10

我有一个动态链接到 Python 解释器的 C++ 应用程序。我希望能够从特定目录导入 python 模块。我想为我的进程修改 PYTHONPATH,以便 sys.path 将包含我添加到 PYTHONPATH 的路径。根据本文档,这似乎是它的工作方式:

http://docs.python.org/c-api/intro.html#embedding-python

但是,当我从 Python-land 打印 sys.path 时,它具有 PYTHONPATH 的原始内容,而不是我设置的内容。这是我正在做的一个例子(使用Boost.Python):

int main(int argc, char* argv[])
{
  _putenv_s("PYTHONPATH", "C:\\source\\\\modules");
  Py_Initialize();
  object main = import("__main__");
  object global = (main.attr("__dict__"));
  exec("import sys\nprint sys.path"), global, global);
}

PS - 我知道还有其他方法可以实现我的目标,但这不是我要问的。我想知道为什么 Py_Initialize() 在设置 sys.path 时不使用 PYTHONPATH 的当前值。或者也许我误解了它应该如何工作?

4

7 回答 7

11

我找到了跨平台解决方案。在调用任何其他 python 代码之前,只需执行以下 python 行:

import sys
sys.path.append("C:\\source\\\\modules")
于 2011-11-18T18:55:18.280 回答
3

如果您一次使用多个 C 运行时库,就会发生这种情况。在这种情况下,您的应用程序和 Python DLL 可能链接到不同的 CRT。每个 CRT 都有自己的一组环境变量;从一个 CRT 使用 putenv 对环境所做的更改在使用不同 CRT 进行的 getenv 调用中是不可见的。

请参阅http://msdn.microsoft.com/en-us/library/ms235460%28v=vs.80%29.aspx上的“readEnv”示例。

您可以通过确保仅使用单个 CRT 来解决此问题,但这在实践中很棘手。程序的调试版本通常使用调试 CRT(启用堆检查和 API 断言等功能);生产 DLL,即使在调试中使用,通常也使用 MSVCRT,生产线程安全版本。我已经通过完全禁用调试 CRT,将所有构建设置为“多线程动态”来解决这个问题,因为维护单独的调试 DLL 太麻烦了。这样做会失去一些调试功能。

于 2011-03-16T00:12:14.217 回答
3

查看:

void PySys_SetPath(char *path) 将 sys.path 设置为在 path 中找到的路径的列表对象,它应该是用平台的搜索路径分隔符分隔的路径列表(在 Unix 上为 :,在 Windows 上为 ;)。

或者

Py_SetProgramName(argv[0]); 这会为您将 dirname(argv[0]) 添加到您的 PYTHONPATH 中。

于 2014-01-27T00:39:15.987 回答
2

正如其他人所说,您可能会遇到 CRT 不匹配的问题。我能够让它与 Python 2.6 和 Visual C++ 2008 一起工作:

#include "stdafx.h"
#include "Python.h"

int _tmain(int argc, _TCHAR* argv[])
{
  _putenv_s("PYTHONPATH", "C:\\source\\\\modules");
  Py_Initialize();
  PyRun_SimpleString("import sys\nprint sys.path");
  PyRun_SimpleString("raw_input()");
  return 0;
}

这个输出:

['C:\\Python26\\lib\\site-packages\\distribute-0.6.13-py2.6.egg', 'C:\\Python26\
\lib\\site-packages\\virtualenv-1.4.9-py2.6.egg', 'C:\\source\\modules', ...

另一种选择可能是更改到该目录,因为当前目录通常以路径结束,例如:

    _chdir("c:\\");
    Py_Initialize();
[...]

这给了我:

['C:\\Python26\\lib\\site-packages\\distribute-0.6.13-py2.6.egg', 'C:\\Python26\
\lib\\site-packages\\virtualenv-1.4.9-py2.6.egg', 'C:\\Windows\\system32\\python
26.zip', 'C:\\Python26\\Lib', 'C:\\Python26\\DLLs', 'C:\\Python26\\Lib\\lib-tk',
 'c:\\', ...
于 2011-03-16T04:23:40.933 回答
1

Python DLL 在加载时可能会获得自己的环境副本。更改环境后尝试使用 LoadLibrary 和 GetProcAddress 加载它,看看是否有任何改变。

于 2011-03-16T03:20:39.003 回答
1
#include "Python.h"
int main()
{
  Py_Initialize();
  PyRun_SimpleString("import sys");
  PyRun_SimpleString("sys.path.append(\"<some_path>\")");
  return 0;
}

这适用于所有 python 版本(2.6、2.7、3.1、3.2、3.3 和 3.4)。
关于<some_path>

  • 它应该只包含一个目录。具有有效分隔符(等)的目录列表d:/path1;d:/path2不起作用
  • Windows 路径(如d:\\path1:)仅适用于 Python 3 之前的 Python 版本,d:\\\\path1应使用更高版本。我建议用 unix 分隔符替换 windows 路径分隔符。以下代码块执行此操作。

    std::string my_path = "<some_path>"; std::replace(my_path.begin(), my_path.end(), '\\', '/');

一个温和的建议: 如果您想支持不同的 python 版本,请不要浪费时间尝试使用以下任一 API 方法修改 PYTHONPATH

  • Py_SetPythonHome()- 对于 python 2 需要一个 ascii 字符串,对于 python 3 - 一个 unicode 字符串,但对于高于 3.1 的版本不能可靠地工作
  • Py_SetPath()- 在 python 3 中引入,但有问题(参见http://bugs.python.org/issue11320

一般来说,上面列出的 API 方法在调用后不会影响Py_Initialize()

于 2015-05-05T04:53:43.260 回答
1

你可以做python3 -c "import sys; sys.path.append('C:\Path\To\Modules')"

于 2018-06-10T13:37:45.997 回答