Подтвердить что ты не робот

Проблемы Cython и deepcopy() с ссылочными методами/функциями. Любые альтернативные идеи?

Недавно я играл с 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__()

Это происходит в python/lib/copy_reg.py здесь:

return cls.__new__(cls, *args)

Я здесь на Python 2.7. Возможно ли, что более новая версия Python возвращается с deepcopy() безопасным способом? Я также на последней версии Cython, 0.15.1.

Update3

Обратите внимание, что я удалил предыдущие обновления, чтобы сделать это максимально простым.

Ok! Я думаю, что нашел несовместимость, но я действительно не знаю, что с этим делать.

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}

Затем в моем основном приложении:

from cython1 import CythonClass
from copy import deepcopy

if __name__ == "__main__":
    gc1 = CythonClass()
    gc1.call_handle()
    gc2 = deepcopy(gc1)

Я получаю:

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__()

Ключ - это ссылка на функцию /handle:

handles = {"handle_method", handle_method}

Если я не включаю ссылку на метод/функцию, Cython не будет взорваться во время глубокой печати. Если я включаю один, ему не нравится, как deepcopy/copy_reg копирует ссылку.

Любые идеи, кроме ссылок на методы/функции? У меня есть немного распутывания, если это простой ответ. (на котором я уже работаю, когда я заканчиваю набирать это)

Спасибо!

4b9b3361

Ответ 1

А, наконец, нашел кнопку "Ответ на свой вопрос".

Я, наверное, нетерпелив, но так как никто не ответил (я имею в виду, кто использует Cython и отвечает на вопросы в четверг днем), я думал, что я закрою это.

1) Cython не любит deepcopy для классов, у которых есть переменные с функцией/методом. Эти переменные копии не удастся. Из того, что я могу сказать, там нет работы вокруг, вам просто нужно придумать новый дизайн, который их не требует. Я закончил с таким же кодом выше и в моем проекте.

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.

Ответ 2

нашел это:

"Работает ли deepcopy с Cython правильно?"

Нет. В этом случае (вы используете типы расширений, классы i.e cdef), вы должны выполнить протокол рассола для вашего класса http://docs.python.org/library/pickle.html#pickling-and-unpickling-extension-types

отсюда: https://groups.google.com/forum/#!topic/cython-users/p2mzJrnOH4Q

"реализация протокола pickle" в связанной статье на самом деле прост и разрешает мои проблемы тривиально (хотя я делаю что-то немного по-другому - мой класс является cdef class, и у меня есть указатель на объект CPP, хранящийся там который не может быть тривиально дублирован - я не знаю, поможет ли это решению проблемы наследования python выше, но это, безусловно, стоит попробовать.)

В любом случае реализация протокола pickle тривиальна (в примере ниже используется "С++ 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))

Когда я пытаюсь это сделать, я могу вызвать copy.deepcopy() в файле dict, содержащем экземпляр моего типа расширения Cython, и получить новый словарь, содержащий новый экземпляр (с другим адресом памяти при печати на терминал). Ранее код вызвал segfault.