【问题标题】:Is it simple to implement a simple python function in C?用C实现一个简单的python函数是否简单?
【发布时间】:2026-02-24 06:00:02
【问题描述】:

我正在解决这篇帖子提出的问题:Fast way to remove a few items from a list/queue

基本上我想做的就是在 C 中实现一个 for 循环。for 循环需要访问一个生成器并能够删除数组的元素(并增加一个整数)。我的内心告诉我这会非常困难,但另一部分说它可以在几分钟内完成。

我没有编写高级 C 的经验(不过我已经为微控制器编写过代码),而 ctypes 和其他 c-> python 的教程似乎正在解决更困难的问题。

def forfilt():
   marked = (i for i, x in enumerate(b) if tokeep(x))
   shift = 0
   for n in marked:
      del b[n - shift]
      shift += 1

我要问两个问题:

  • 这很难吗?

  • 您有什么建议/想自己编写代码吗? :D

实际上,这对我来说似乎是一个相当重要的问题。我不知道有什么方法可以快速完成最初的问题。我想如果你知道这个问题的答案,那么这个问题就是无效的。

【问题讨论】:

  • 为什么你认为 C 会比原生 Python 代码更快?
  • 因为 for 循环是出了名的慢,而这不能通过压缩来解决。
  • 为什么不使用 Cython 或者 ShedSkin?
  • 在 C 中实现的给定代码将比使用过滤器或列表推导式更慢。每次使用 del 时,它都会将列表中的所有内容都转移过来,多次这样做是一种浪费。只需使用过滤器或列表推导,您不会通过转到 C 来获得更好的速度。
  • @Winston Ewert:谢谢。我什至没有考虑列表如何工作的内部结构。我想我认为他们的工作方式不同。

标签: python c for-loop ctypes


【解决方案1】:

如果您只需要删除 for 循环开销,那么在 Cython (pip install cython) 中定义 for 循环变量的类型就足够了。这是 Cython delitems.pyx 中修改后的 remove_inplace_senderle2()

#cython: boundscheck=False, wraparound=False
import cython

@cython.locals(end=cython.Py_ssize_t, i=cython.Py_ssize_t)
def remove_inplace_senderle2(L, keep):
    end = 0
    for i in range(len(L)):
        x = L[end] = L[i]
        if keep(x):
           end += 1

    del L[end:]

for i in range(len(L)) 转换为经典的 C 循环:for (i=0; i < L_length; ++i),其开销与 keep() 的函数调用开销相比相形见绌。

注意:上述函数在纯 Python 中可能比 L = filter(keep, L)(或 listcomp)慢。

有关如何编译和使用 Cython 的更简单示例,请参阅 gcd() function

【讨论】:

  • 嘿,非常感谢。这正是我要问的问题,解决方案似乎很简单而且很pythonic:D
【解决方案2】:

这取决于简单是多么简单。是的,只要输入是一个数组,这个特定的函数就可以写成就地内存移动。

size_t for_filt( my_struct *b, size_t n ) {
    my_struct *src_pen, *dst_pen;

    for ( src_pen = dst_pen = b;
          src_pen != b + n;
          ++ src_pen ) {
        if ( tokeep( src_pen ) ) {
            memmove( dst_pen ++, src_pen, sizeof (my_struct) );
        }
    }

    return dst_pen - b; /* return number of elements in resulting array */
}

C++ 标准库将上述函数简化为单行:

n = std::remove_if( b, b+n, std::not1( tokeep ) ) - b;

该函数适用于数组以外的结构,但n = … - b; 是特定于数组的。

【讨论】:

    【解决方案3】:

    根据 CPython C-API 编写 C 代码比在没有此类 API 支持的情况下编写 C 代码要愉快得多。但是,主要是构建和链接过程以及进行所有设置可能相当乏味。一旦你有了一个 C 扩展,添加它就不是太困难了(尽管在 Python 级别上正确地公开一些东西并确保你的所有引用计数都是正确的)。

    类似 Cython 的其他静态编译工具首先要获得相对较高的设置成本才能使编译后的扩展工作,但一旦已经到位,使用起来就容易得多。

    关于您的具体问题,通过将列表理解方法与内置函数 filter(或其 Py3k 等效项 functools.filter)进行比较,您链接的问题的海报已经展示了将循环代码放到 C 中的效果 - 原生循环是内置迭代和归约函数(如 sumanyallmapfilter)的主要优点之一。

    消除 Python 级别的循环开销很可能是导致两种方法(列表理解与过滤器调用)的性能差异的大部分原因。

    【讨论】: