Cython和deepcopy()使用引用的方法/函数来解决问题.还有其他想法吗?

gar*_*man 13 python typeerror cython

我最近一直在玩Cython以加速,但我的项目继承了一个模块,它有一个copy()使用的方法deepcopy().我尝试deepcopy()在一个覆盖的版本中实现copy(),我认为我有它的工作,但它似乎不再存在.

TypeError: object.__new__(cython_binding_builtin_function_or_method) is not safe,
   use cython_binding_builtin_function_or_method.__new__()
Run Code Online (Sandbox Code Playgroud)

这是在python/lib/copy_reg.py中发生的:

return cls.__new__(cls, *args)
Run Code Online (Sandbox Code Playgroud)

我在这里使用Python 2.7.新版本的Python是否有可能deepcopy()以"安全"的方式返回?我也是最新版的Cython,0.15.1.

UPDATE3

请注意,我已删除以前的更新,以尽可能简化此操作.

好!我想我发现了不兼容性,但我真的不知道该怎么做.

class CythonClass:
    def __init__(self):
        self._handle = self._handles.get("handle_method")

    def call_handle(self):
        self._handle(self)

    def handle_method(self):
        print "I'm a little handle!"

    handles = {"handle_method", handle_method}
Run Code Online (Sandbox Code Playgroud)

然后在我的主应用程序中:

from cython1 import CythonClass
from copy import deepcopy

if __name__ == "__main__":
    gc1 = CythonClass()
    gc1.call_handle()
    gc2 = deepcopy(gc1)
Run Code Online (Sandbox Code Playgroud)

我明白了:

I'm a little handle!

Traceback (most recent call last):
  File "cythontest.py", line 8, in <module>
    gc2 = deepcopy(gc1)
  File "C:\python26\lib\copy.py", line 162, in deepcopy
    y = copier(x, memo)
  File "C:\python26\lib\copy.py", line 292, in _deepcopy_inst
    state = deepcopy(state, memo)
  File "C:\python26\lib\copy.py", line 162, in deepcopy
    y = copier(x, memo)
  File "C:\python26\lib\copy.py", line 255, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "C:\python26\lib\copy.py", line 189, in deepcopy
    y = _reconstruct(x, rv, 1, memo)
  File "C:\python26\lib\copy.py", line 323, in _reconstruct
    y = callable(*args)
  File "C:\python26\lib\copy_reg.py", line 93, in __newobj__
    return cls.__new__(cls, *args)
TypeError: object.__new__(cython_binding_builtin_function_or_method) is not safe, use cython_binding_builtin_function_or_method.__new__()
Run Code Online (Sandbox Code Playgroud)

关键是函数/句柄参考:

handles = {"handle_method", handle_method}
Run Code Online (Sandbox Code Playgroud)

如果我不包含方法/函数引用,Cython在深度复制期间不会爆炸.如果我包含一个,它不喜欢deepcopy/copy_reg如何复制引用.

除了不使用方法/函数引用之外的任何想法?如果这个简单的答案,我有点想要做.(当我完成输入时,我已经在做了)

谢谢!

gar*_*man 8

啊,终于找到了"回答你自己的问题"按钮.

我可能很不耐烦,但是因为没有人回复(我的意思是谁在周四下午使用Cython并回答问题),我想我会关闭这个.

1)Cython不喜欢对具有函数/方法引用变量的类进行深度复制.那些变量副本将失败.从我所知,它没有解决它,你只需要想出一个不需要它们的新设计.我最终使用上面的代码和我的项目中的代码.

2)Cython根本不处理属性装饰器.你不能@property@<property name>.setter.属性需要以旧时尚的方式设置.例如<property name> = property(get_property, set_property).

3)继承的非Cython类的方法"可能"不可访问.我知道这个含糊不清.我无法完全解释它.我所说的只是我继承NetworkX.DiGraph,并且number_of_nodes()在Cython之后无法访问,当时它是直接的Python.我必须创建一个方法的引用并使用它.例如number_of_verts = NetworkX.DiGraph.number_of_nodes.


teh*_*rus 7

发现了这个:

"深度复制是否适用于Cython?"

不.在这种情况下(您正在使用扩展类型,即cdef类),您必须为您的类实现pickle协议 http://docs.python.org/library/pickle.html#pickling-and-unpickling-extension-类型

从这里:https://groups.google.com/forum/#!topic/cyce -users/p2mzJrnOH4Q

链接文章中的"实现pickle协议"实际上很简单,并且很容易解决我的问题(虽然我做的事情略有不同 - 我的类是一个cdef class,我有一个指向存储在那里的CPP对象的指针,不能轻易复制 -我不知道这是否会解决上面的python继承问题,但它肯定值得一试.)

无论如何,实现pickle协议(下面的例子中,使用"C++用Cython",它具有用于双重含义是微不足道的del关键字,除其他事项外):

cdef class PyObject(object):
    cdef CppObject* cpp
    cdef object arg1
    cdef object arg2

    def __cinit__(self, arg1=[], arg2=False):
        # C++ constructor using python values, store result in self.cpp.

        # new code: cache the python arguments that were used.
        self.arg1 = arg1
        self.arg2 = arg2

    def __init__(self, arg1=[], arg2=False):
        # logic for validating arguments.
        pass

    def __dealloc__(self):
        if not self.cpp == NULL:
            del self.cpp

    def __reduce__(self):
        # a tuple as specified in the pickle docs - (class_or_constructor, 
        # (tuple, of, args, to, constructor))
        return (self.__class__, (self.arg1, self.arg2))
Run Code Online (Sandbox Code Playgroud)

当我尝试这个时,我可以调用copy.deepcopy()包含我的Cython扩展类型的实例的dict,并获得一个包含新实例的新字典(当打印到终端时具有不同的内存地址.)以前相同的代码导致了段错误.