要从当前目录加载模块,有许多选项。
- 您需要通过初始化结构配置Python(包括路径配置)。请参阅Python Initialization Configuration。
Py_Initialize():
PyConfig config;
PyConfig_InitPythonConfig(&config);
config.module_search_paths_set = 1;
PyWideStringList_Append(&config.module_search_paths, L".");
Py_InitializeFromConfig(&config);
pModule = PyImport_Import(pName);
一个简单的选项是在
Py_Initialize()
之后也运行
PySys_SetArgv(0, NULL);
,以将当前目录添加到Python的路径中。虽然这个选项自Python 3.11起已被弃用,但在许多情况下仍然是一个简单有效的选项。该选项利用了
PySys_SetArgvEx(int argc, char **argv, int updatepath)
的行为,当满足以下条件时:
- 非零的
updatepath
argc = 0
或者argv
没有指向现有文件名。
请参见docs。从Python 2.7开始有效。要添加特定目录,argv[0]
应包含该目录中Python脚本的路径(例如"NULL")。PySys_SetArgv
会为您正确设置updatepath
值。尽管如上所述,此选项目前已被弃用。
示例项目
从当前目录中加载在Python脚本中定义的模块,并使用输入参数调用该模块中的函数,所有这些都指定给C应用程序。为简单起见,检查已省略。此示例与嵌入C/C ++应用程序中的Python文档页面提供的示例密切相关。
./hello.py
def sayHello1():
print("Hello from sayHello1!")
return 1
def sayHello2():
print("Hello from sayHello2!")
return 2
./main.c
(为简单起见,已删除检查)
#define PY_SSIZE_T_CLEAN
#include <Python.h>
int main(int argc, char *argv[])
{
PyObject *pName = NULL;
PyObject *pModule = NULL;
PyObject *pFunc = NULL;
PyObject *pValue = NULL;
Py_Initialize();
PyConfig config;
PyConfig_InitPythonConfig(&config);
config.module_search_paths_set = 1;
PyWideStringList_Append(&config.module_search_paths, L".");
Py_InitializeFromConfig(&config);
pName = PyUnicode_FromString(argv[1]);
pModule = PyImport_Import(pName);
Py_XDECREF(pName);
pFunc = PyObject_GetAttrString(pModule, argv[2]);
pValue = PyObject_CallNoArgs(pFunc);
Py_XDECREF(pValue);
Py_XDECREF(pFunc);
Py_XDECREF(pModule);
Py_Finalize();
return 0;
}
编译(cmake):
cmake_minimum_required(VERSION 3.2)
project(python_c_api)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_package(Python COMPONENTS Interpreter Development)
cmake_path(GET Python_EXECUTABLE PARENT_PATH Python_BIN_DIR)
set(config_cmd_flags "--cflags")
execute_process(COMMAND ${Python_BIN_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}-config ${config_cmd_flags}
OUTPUT_VARIABLE Python_FLAGS)
add_executable(python_extension main.c)
target_link_libraries(python_extension PUBLIC python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR})
target_link_directories(python_extension PUBLIC ${Python_LIBRARY_DIRS})
target_include_directories(python_extension PUBLIC ${Python_INCLUDE_DIRS})
set_target_properties(python_extension PROPERTIES
OUTPUT_NAME "call"
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}
)
separate_arguments(Python_FLAGS_NORM UNIX_COMMAND "${Python_FLAGS}")
target_compile_options(python_extension PUBLIC ${Python_FLAGS_NORM})
cmake .
cmake --build .
执行:(为简单起见,省略了检查。按照此处所示调用应用程序)。
./call hello sayHello1
Hello from sayHello1!
./call hello sayHello2
Hello from sayHello2!
编辑:此解决方案将当前工作目录附加到搜索路径中,而不是包含嵌入式Python的二进制可执行文件所在的目录。因此,如果从不同目录调用应用程序,则会将该目录附加到搜索路径中。
将包含嵌入式Python解释器的二进制可执行文件的路径添加到搜索路径中并不容易,并且似乎不存在易于跨平台的解决方案。如果需要这样做,建议将应用程序包装在另一个应用程序或脚本中,在正确的目录中运行。
os.getcwd()
来检查你是否在你认为的位置吗? - Matti Lyramultiply
是根据教程在文件中定义的函数,请尝试使用from __main__ import multiply
。 - Matti Lyra