Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/61.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python C扩展装饰程序_Python_C_Decorator_Python C Api - Fatal编程技术网

Python C扩展装饰程序

Python C扩展装饰程序,python,c,decorator,python-c-api,Python,C,Decorator,Python C Api,我为冗长的背景信息提前道歉 我最近一直在玩弄Python/C-API(Python3.4),结果被难倒了。我的目标是拥有一个C扩展,可以用作函数/方法装饰器。我在gist中有一个运行良好的LRU缓存原型。尽管缓存工作正常且速度非常快,但存在两个问题: my Decorator返回的类型不是函数:即 >>> from lrucache import lrucache >>> @lrucache() ... def f(a, b): ... return a

我为冗长的背景信息提前道歉

我最近一直在玩弄Python/C-API(Python3.4),结果被难倒了。我的目标是拥有一个C扩展,可以用作函数/方法装饰器。我在gist中有一个运行良好的LRU缓存原型。尽管缓存工作正常且速度非常快,但存在两个问题:

  • my Decorator返回的
    类型
    不是函数:即

    >>> from lrucache import lrucache
    >>> @lrucache()
    ... def f(a, b):
    ...    return a+b
    ...
    >>> type(f)
    >>> <class 'lrucache.cache'>
    
    >>从lrucache导入lrucache
    >>>@lrucache()
    ... def f(a,b):
    ...    返回a+b
    ...
    >>>类型(f)
    >>> 
    
    因此,decorator不能在方法上正常工作-似乎
    self
    丢失了,因为Python在看到我的类时没有创建实例方法(这很有意义)

  • \uuuuu doc\uuuu
    从修饰函数复制到我的缓存类不会影响
    帮助显示的消息

  • 我绕过这些问题的想法是只返回用户函数并进行一些修改,而不是返回一个新的自定义类对象。这些修改是

  • 在函数中添加
    \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    属性,并将其指向函数

  • 覆盖
    \uuuu call\uuu
    属性,以便将函数调用定向到自定义C例程

  • 我能够完成(1),但是重写functions
    \uuu call\uu
    方法没有任何作用,因为解释器通常不使用它

    我的问题(最后): 如何创建调用C函数的Python函数(即PyFunctionObject)

    或者,如果有更好的方法,我也会对此感兴趣。这主要是一个学习/有趣的练习,因此我对基于Cython的解决方案不太感兴趣。

    您的(2)步骤显然失败了,因为特殊属性是在类上查找的,而不是在实例上:

    In [1]: class Test:
       ...:     def __call__(self):
       ...:         print('Called class attribute!')
       ...:         
    
    In [2]: t = Test()
    
    In [3]: def new_call():
       ...:     print('Called instance attribute!')
       ...:     
    
    In [4]: t.__call__ = new_call
    
    In [5]: t()
    Called class attribute!
    
    您不想修改
    函数
    \uuuuu调用
    方法,因为这将极大地修改python代码的语义

    据我所知,有一种方法可以实例化
    PyFunctionObject
    ,即调用。它的问题是需要一个
    PyCodeObject
    作为参数,要创建这样一个对象,您可以调用:

    • :创建无效的代码对象。它仅用于创建框架对象,而您不关心要放在那里的代码
    • :创建字节码对象。这是一个有14个参数的beast,其中一个参数是
      PyObject*code
      ,它应该是一个可读的缓冲区,包含字节码的二进制表示形式
    • 这似乎是唯一合理的解决办法。基本上,这是内置的
      compile
    一旦创建了代码对象,函数的创建就很简单了。 但是请注意,由于函数对象的主体是解释的,因此这样做不会获得任何性能优势

    但是,您的问题还有其他解决方案:

    • 你可以把你的
      lrucache
      a。这就是python为自动传递
      self
      作为第一个参数的方法所做的,因此您可以模拟该行为

    • 您可以编写一个非常简单的python模块,导入您的C扩展,并提供如下函数:

      from functools import wraps
      
      from _lrucache import cache
      
      def lrucache(func):
          cached_func = cache(func)
      
          @wraps(func)
          def wrapper(*args, **kwargs):
              return cached_func(*args, **kwargs)
      
          return wrapper
      
      通过这种方式,您可以通过python代码绕过整个问题

    • 您可以创建函数类型的子类并返回该对象。 但是,如果python在构建类时正在检查确切的类型,那么这可能不起作用,而且实现可能并不简单,因为解释器可能会假定您必须提供的函数对象的某些属性。不,你不能给一个函数子类化


    我没有直接使用Python/C-API,但可能(因为这是一个学习/有趣的练习)尝试在Python中创建一个调用C函数的装饰器。或者创建一个简单的C装饰器,返回正确的函数值/类型(取决于它的丑陋程度)。对不起,我不知道解决办法。谢谢你深思熟虑的回答。遗憾的是,我没有足够的代表投票。简单的包装器解决方案工作得很好。我的性能下降了50%,但仍然比纯python版本快4-5倍。我曾尝试对函数进行子类化,但遇到了问题。我认为这可能是因为函数类型没有Py\u TPFLAGS\u BASETYPE。就描述符路由而言,这是否也适用于常规函数,还是我也需要一个非描述符版本?@ptb哦,我没有检查
    函数
    是否是子类的,因此如果没有C级的严重攻击,该路由似乎不实用。就描述符而言,它应该可以工作。您必须在正常函数调用的
    lrucache
    上实现一个
    \uuuu call\uuu
    方法,而方法查找将首先调用
    \uu get\uuuuu
    并对返回的对象使用
    \uu call\uuuu
    。我能够通过在tp\u descr\u get中添加一个简单函数来创建描述符版本。另外,通过使用doc的getset方法(就像在descrobject.c中一样),我能够获得正确的docstring。已完成的版本位于。谢谢你的帮助。