Как я могу отладить проблему, вызывая Python copy.deepcopy () для пользовательского типа? - PullRequest
4 голосов
/ 21 декабря 2009

В моем коде я пытаюсь сделать копии экземпляров класса, используя copy.deepcopy. Проблема в том, что при некоторых обстоятельствах происходит ошибка со следующей ошибкой:

TypeError: 'object.__new__(NotImplementedType) is not safe, use NotImplementedType.__new__()'

После долгих копаний я обнаружил, что могу воспроизвести ошибку, используя следующий код:

import copy
copy.deepcopy(__builtins__) 

Проблема заключается в том, что в какой-то момент он пытается скопировать встроенную NotImplementedType. Вопрос в том, почему он это делает? Я не переопределил __deepcopy__ в моем классе, и это не происходит все время. У кого-нибудь есть советы по отслеживанию того, откуда поступил запрос на создание копии этого типа?

Я поместил некоторый отладочный код в сам модуль copy, чтобы убедиться, что это именно то, что происходит, но точка, в которой возникает проблема, заключается в том, что до рекурсивного стека очень трудно сделать большую часть того, что я делаю ». Я вижу.

Ответы [ 3 ]

3 голосов
/ 22 декабря 2009

В конце я немного покопался в исходном коде copy и нашел следующее решение:

from copy import deepcopy, _deepcopy_dispatch
from types import ModuleType

class MyType(object):

    def __init__(self):
        self.module = __builtins__

    def copy(self):
        ''' Patch the deepcopy dispatcher to pass modules back unchanged '''
        _deepcopy_dispatch[ModuleType] = lambda x, m: x
        result = deepcopy(self)
        del _deepcopy_dispatch[ModuleType]
        return result

MyType().copy()

Я понимаю, что для этого используется закрытый API, но я не смог найти другого чистого способа добиться того же. Я сделал быстрый поиск в Интернете и обнаружил, что другие люди использовали тот же API без каких-либо проблем. Если это изменится в будущем, я приму удар.

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

Надеюсь, это поможет кому-то еще в какой-то момент.

1 голос
/ 23 декабря 2009

Вы можете переопределить поведение Deepcopy класса, который содержит указатель на модуль, используя протокол выбора , который поддерживается модулем копирования, как указано здесь, В частности, вы можете определить __getstate__ и __setstate__ для этого класса. E.g.:

>>> class MyClass:
...     def __getstate__(self):
...         state = self.__dict__.copy()
...         del state['some_module']
...         return state
...     def __setstate__(self, state):
...         self.__dict__.update(state)
...         self.some_module = some_module
1 голос
/ 22 декабря 2009

вы можете переопределить метод __deepcopy__: ( документация по питону )

Чтобы класс мог определить собственную реализацию копирования, он может определять специальные методы __ copy __ () и __ deepcopy __ () . Первый вызывается для реализации операции мелкого копирования; дополнительные аргументы не передаются. Последний вызывается для реализации операции глубокого копирования; пропущен один аргумент, мемо словарь. Если реализация __ deepcopy __ () должна сделать глубокую копию компонента, она должна вызвать функцию deepcopy () с компонентом в качестве первого аргумента и словарем memo в качестве второго аргумента.

В противном случае вы можете сохранить модули в глобальном списке или что-то еще.

...