确定Python函数是否已在C扩展中实现

5
假设我有一个运行缓慢的Python程序-经过分析,我已经确定了瓶颈。我导入的第三方模块中的一个特定函数特别慢。
对于这种情况,我知道该函数是用Python实现的(使用Eclipse很容易跳转到函数定义)。因此,我知道我可以将该函数转换为Cython以进行加速选项。(如果它已经在C中实现,那么将其编写成Cython没有意义...)。
如果我没有IDE,有什么简单的方法来确定这一点吗? 我知道我可以转到安装模块的目录,并推断出如果模块是.so,则它在C中。但是否有任何替代方案?
谢谢
1个回答

4

检查它是否是 types.FunctionType 的实例:

>>> import types
>>> isinstance(len, types.FunctionType)
False
>>> def mylen(): pass
... 
>>> isinstance(mylen, types.FunctionType)
True

您最好检查是否为isinstance(X, (types.FunctionType, types.LambdaType),这样更安全。

C函数是builtin_function_or_method的实例:

>>> len.__class__
<type 'builtin_function_or_method'>
>>> np.vdot.__class__
<type 'builtin_function_or_method'>

您可以通过 types.BuiltinFunctionType/types.BuiltinMethodType 访问此类型。
或者,您可以检查函数是否具有 __code__ 属性。由于 C 函数没有字节码,因此它们不能具有 __code__ 属性。
请注意,有时看起来像函数的东西实际上是一个类,例如 enumerate,但某些第三方库可能会执行相同的操作。这意味着您还应检查类是否是用 C 实现的。这比较困难,因为所有类都是 type 的实例。一种方法是检查类的 dir 中是否有 __dict__,如果没有,则应检查是否有 __slots__
以下类似的内容应该是相当准确的:
def is_implemented_in_c(obj):
    if isinstance(obj, (types.FunctionType, types.LambdaType)):
        return False
    elif isinstance(obj, type):
        if '__dict__' in dir(obj): return False
        return not hasattr(obj, '__slots__')
    # We accept also instances of classes.
    # Return True for instances of C classes, False for python classes.
    return not isinstance(obj, types.InstanceType)

使用示例:

>>> is_implemented_in_c(enumerate)
True
>>> is_implemented_in_c(len)
True
>>> is_implemented_in_c(np.vdot)
True
>>> is_implemented_in_c(lambda x: True)
False
>>> is_implemented_in_c(object)
True
>>> class A(object):
...     __slots__ = ('a', 'b')
... 
>>> is_implemented_in_c(A)
False

感谢您的非常好的答案 - 不过有一个小问题。如果C函数“cf”被另一个Python函数“pf”包装,并且您调用is_implemented_in_c(pf),它将返回False。遗憾的是,我们不能说“哦,这是一个Python函数,我可以转换为Cython来加速!”如果C函数被一个单行Python函数包装,那么我们肯定可以递归地确定这是一个伪装成Python的C函数。但是,如果pf是一个多行函数,并且在pf的某个地方调用了cf,则很难确定pf是否只是cf的包装器。 - sungeunbae
1
@user2005856 我很清楚这一点,但对于那种情况并没有解决方案,而且已经被证明。你想做的是从函数文本中确定一个语义属性。根据 Rice 定理,该属性要么是平凡的(例如始终为 True 或 False),要么是不可判定的。你可以尝试编写一些复杂的代码来处理某些函数,但无论你多努力,这永远不会在一般情况下起作用。如果你找到了一个 Python 包装器,请使用 dis 模块阅读源代码/反汇编并找到真正的 C 函数。 - Bakuriu
1
注意:is_implemented_in_c() 函数假定存在 Python 2.x 特有的 types.InstanceType 属性来检测旧式类,但是在 Python 3.x 中已经不存在了(出于显而易见的原因)。该函数并没有像它所宣传的那样工作。事实上,它的错误程度非常可怕。在 CPython 下,基于 C 的对象可以通过以下代码片段轻松检测到:is_obj_c = bool(type(obj).__flags__ & (1<<9)),其中 (1<<9) 是位于官方 CPython 代码库中的 Include/object.h C 头文件中的魔数 _Py_TPFLAGS_HEAPTYPE - Cecil Curry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接