Python C扩展装饰程序
我为冗长的背景信息提前道歉 我最近一直在玩弄Python/C-API(Python3.4),结果被难倒了。我的目标是拥有一个C扩展,可以用作函数/方法装饰器。我在gist中有一个运行良好的LRU缓存原型。尽管缓存工作正常且速度非常快,但存在两个问题: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
类型
不是函数:即
>>> 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例程\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
- 你可以把你的
a。这就是python为自动传递lrucache
作为第一个参数的方法所做的,因此您可以模拟该行为self
- 您可以编写一个非常简单的python模块,导入您的C扩展,并提供如下函数:
通过这种方式,您可以通过python代码绕过整个问题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在构建类时正在检查确切的类型,那么这可能不起作用,而且实现可能并不简单,因为解释器可能会假定您必须提供的函数对象的某些属性。不,你不能给一个函数子类化
函数
是否是子类的,因此如果没有C级的严重攻击,该路由似乎不实用。就描述符而言,它应该可以工作。您必须在正常函数调用的lrucache
上实现一个\uuuu call\uuu
方法,而方法查找将首先调用\uu get\uuuuu
并对返回的对象使用\uu call\uuuu
。我能够通过在tp\u descr\u get中添加一个简单函数来创建描述符版本。另外,通过使用doc的getset方法(就像在descrobject.c中一样),我能够获得正确的docstring。已完成的版本位于。谢谢你的帮助。