Python 将contextlib与cython一起使用
作为将游戏引擎代码转换为cython的一部分,我正在移植顶点缓冲区对象(Vbo)类。我使用这个Vbo类将3D模型数据发送到GPU。代码(Python 将contextlib与cython一起使用,python,opengl,cython,Python,Opengl,Cython,作为将游戏引擎代码转换为cython的一部分,我正在移植顶点缓冲区对象(Vbo)类。我使用这个Vbo类将3D模型数据发送到GPU。代码(vbo.pyx)当前如下所示: cimport gl from enum import Enum import contextlib class VboTarget(Enum): ARRAY = gl.GL_ARRAY_BUFFER INDEX = gl.GL_ELEMENT_ARRAY_BUFFER cdef class Vbo:
vbo.pyx
)当前如下所示:
cimport gl
from enum import Enum
import contextlib
class VboTarget(Enum):
ARRAY = gl.GL_ARRAY_BUFFER
INDEX = gl.GL_ELEMENT_ARRAY_BUFFER
cdef class Vbo:
cdef readonly gl.GLuint id_
cdef readonly double[:] data
cdef readonly int target
def __init__(self, data=None, target=VboTarget.ARRAY):
gl.glewInit()
gl.glGenBuffers(1, &self.id_)
self.target = target.value
if data is not None:
self.data = data
@contextlib.contextmanager
def bind(self):
gl.glBindBuffer(self.target, self.id_)
try:
yield
finally:
gl.glBindBuffer(self.target, 0)
def set_data(self, new_data):
self.data = new_data
def update(self):#perform gpu update
with self.bind():
gl.glBufferData(self.target, self.data.nbytes, &self.data[0], gl.GL_DYNAMIC_DRAW)
我想使用contextlib
,因为这样可以确保到GPU的缓冲区绑定和解除绑定将干净地自动发生。cython代码编译无误;但是,当我将此cython模块导入python代码时,会收到以下错误消息:
Traceback (most recent call last):
File "main.py", line 2, in <module>
import vbo
File "vbo.pyx", line 21, in init vbo (vbo.c:15766)
@contextlib.contextmanager
File "C:\Python27\lib\contextlib.py", line 82, in contextmanager
@wraps(func)
File "C:\Python27\lib\functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'method_descriptor' object has no attribute '__module__'
在尝试了简化版本的代码后,它似乎对我来说仍然有效(Cython 0.25.1,Python 3.6.1): 我不认为您的更复杂示例的任何更改实际上会影响这一点,但我没有
gl.pxd
,因此很难进行测试。也许值得确保你的Cython版本是最新的(如果你还没有的话)
编辑:我认为重要的区别可能是Python2.7与Python3.6。Python3.6和Python2.7。因此,我不认为这是Cython行为的改变,因此可能不是真正的bug
如评论中所述,您可以使用带有
\uuuuuu enter\uuuuuu
和\uuuu exit\uuuuuu
的非cdef类来获得相同的行为:
cdef class Vbo:
def __init__(self):
pass
def bind(self):
class C:
def __enter__(self2):
# note that I can access "self" from the enclosing function
# provided I rename the parameter passed to __enter__
self.do_something() # gl.BindBuffer(self.target, self.id_) for you
def __exit__(self2, exc_type, exc_val, exc_tb):
print("Done") # gl.glBindBuffer(self.target, 0)
return C()
def do_something(self):
print("something")
总之,我无法重现您的问题,但这里有一个替代方案…在尝试了简化版的代码后,它看起来对我来说仍然有效(Cython 0.25.1,Python 3.6.1):
我不认为您的更复杂示例的任何更改实际上会影响这一点,但我没有gl.pxd
,因此很难进行测试。也许值得确保你的Cython版本是最新的(如果你还没有的话)
编辑:我认为重要的区别可能是Python2.7与Python3.6。Python3.6和Python2.7。因此,我不认为这是Cython行为的改变,因此可能不是真正的bug
如评论中所述,您可以使用带有\uuuuuu enter\uuuuuu
和\uuuu exit\uuuuuu
的非cdef类来获得相同的行为:
cdef class Vbo:
def __init__(self):
pass
def bind(self):
class C:
def __enter__(self2):
# note that I can access "self" from the enclosing function
# provided I rename the parameter passed to __enter__
self.do_something() # gl.BindBuffer(self.target, self.id_) for you
def __exit__(self2, exc_type, exc_val, exc_tb):
print("Done") # gl.glBindBuffer(self.target, 0)
return C()
def do_something(self):
print("something")
总之,我无法重现您的问题,但这里有一个替代方案…对于最新的Cython master(Cython版本0.26b0),这看起来实际上不是问题。只要在源文件顶部应用binding=True
提示,@DavidW的答案中描述的contextlib代码的简化版本就可以完美地工作。可以找到有关Cython问题的讨论。对于最新的Cython master(Cython版本0.26b0),现在看来这实际上不是问题。只要在源文件顶部应用binding=True
提示,@DavidW的答案中描述的contextlib代码的简化版本就可以完美地工作。可以找到关于Cython问题的讨论。如果在Cython中使用普通类而不是cdef
类,那么contextlib
几乎肯定是兼容的。我还不确定它是否可以与cdef
类一起工作-它看起来很复杂@DavidW如果我使用普通类,那么我就不能在代码中使用任何cdef
扩展类型,对吗?Vbos是我在使用python时遇到的性能瓶颈之一,因此我肯定希望键入尽可能多的变量。我可以使用contextlib
来代替我的cdef
类,而使用\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
来解决这个问题,但是用户会用my uVBO:
调用类似的东西,而不是用my uVBO.bind()
调用,这并不理想。是的-一个普通类没有类型化属性。我认为您可以从bind()
返回一个带有\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,但我还是不清楚。我更新了问题,以显示我使用\uuuuu enter\uuuu
和\uuu exit\uuuu
的备选版本。bind
函数是什么样子的?如果在Cython中使用普通类而不是cdef
类,那么contextlib
几乎肯定是兼容的。我还不确定它是否可以与cdef
类一起工作-它看起来很复杂@DavidW如果我使用普通类,那么我就不能在代码中使用任何cdef
扩展类型,对吗?Vbos是我在使用python时遇到的性能瓶颈之一,因此我肯定希望键入尽可能多的变量。我可以使用contextlib
来代替我的cdef
类,而使用\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
来解决这个问题,但是用户会用my uVBO:
调用类似的东西,而不是用my uVBO.bind()
调用,这并不理想。是的-一个普通类没有类型化属性。我认为您可以从bind()
返回一个带有\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,但我还是不清楚。我更新了问题,以显示我使用\uuuuu enter\uuuu
和\uuu exit\uuuu
的备选版本。bind
函数是什么样子的?非常感谢<代码>cython--version
显示我使用的是0.25.2,尽管我使用的是python 2.7.12。有趣的是,你的第一个样本给出了和我问题中相同的错误。但是,您的第二个解决方案对我的gl*
函数非常有效!我不会想到重命名self
cdef class Vbo:
def __init__(self):
pass
def bind(self):
class C:
def __enter__(self2):
# note that I can access "self" from the enclosing function
# provided I rename the parameter passed to __enter__
self.do_something() # gl.BindBuffer(self.target, self.id_) for you
def __exit__(self2, exc_type, exc_val, exc_tb):
print("Done") # gl.glBindBuffer(self.target, 0)
return C()
def do_something(self):
print("something")