在自定义C模块中使用Python模块的C API

3 投票
3 回答
591 浏览
提问于 2025-04-15 18:41

这看起来有点像我想要的,不过我想在另一个C模块中创建一个C的PyObject实例。

用C API创建一个在Python中声明的Python类的实例

但是我找不到关于如何从C使用Python模块的文档。网上的资料大多数都在讲如何用C扩展Python。

我的问题是,我有一个Pygame项目,我想加快它的速度。所以我正在制作一个C模块,需要访问Pygame。我该如何导入pygame呢?我可不想导入两个副本,对吧?代码部分我能搞定,但我就是不知道怎么配置和链接这些代码。我知道我一定漏掉了一些文档,如果有人能指点我一下,我会非常感激。


更新:抱歉,我重新读了一下我的帖子,发现我的措辞很糟糕。

如果你安装了pygame,可以去你的Python/include目录下找pygame的头文件。它们是干什么用的?我以为你的C模块可以直接访问pygame的C模块,这样你的Python脚本和C模块就能使用同一个pygame实例。

有人能帮我澄清一下吗?

3 个回答

-1

我在网上搜索了一下,结果点了一个“我很幸运”的链接。

0

可以使用pygame的C接口,尽管这可能不是它本来的用途(这就引出了一个问题,为什么pygame的头文件会和pygame包一起安装)。

如果你用这种“内部”接口构建的扩展,可能在下一个版本或构建的pygame发布时会出现问题,除非pygame的开发者关注这些C头文件的接口兼容性...

总之,这里有一个例子:

C扩展模块代码(red.c):

#include <Python.h>
#include <pygame/pygame.h>

static PyObject* fill_surface(PyObject* self, PyObject* args)
{
  PyObject* py_surface = NULL;
  if( !PyArg_ParseTuple(args, "O", &py_surface) )
    return NULL;

  PySurfaceObject* c_surface = (PySurfaceObject*) py_surface;
  SDL_FillRect( c_surface->surf, 0, SDL_MapRGB(c_surface->surf->format, 255, 0, 0) );

  return Py_None;
}

PyMethodDef methods[] = {

  {"fill_surface", fill_surface, METH_VARARGS, "Paints surface red"},
  {NULL, NULL, 0, NULL},
};

void initred()
{
  Py_InitModule("red", methods);
}

用于构建的Makefile(Linux):

SOURCES = red.c
OBJECTS = $(SOURCES:.c=.o)
TARGET = red.so

CFLAGS += -Wall -O2 -g
CFLAGS += $(shell python-config --cflags)
CFLAGS += $(shell sdl-config --cflags)

LDFLAGS += $(shell sdl-config --libs)

all: $(TARGET)

clean:
    $(RM) $(OBJECTS)
    $(RM) $(TARGET)

$(TARGET): $(OBJECTS)
    $(CC) -shared -o $@ $(OBJECTS) $(LDFLAGS)

$(OBJECTS): Makefile

Python测试代码:

import pygame
import red

pygame.init()
screen = pygame.display.set_mode( (640, 480) )
red.fill_surface(screen)
pygame.display.update()
pygame.time.delay(3000)
1

这个技巧就是用C语言来写普通的Python程序。你可以使用一些特定的函数,比如 PyImport_ImportModule() 来导入模块,PyObject_GetAttr() 来获取对象的属性,以及 PyObject_Call() 来调用对象的方法,具体使用哪个要根据情况来决定。

撰写回答