Как я могу глубоко скопировать объект, который имеет упакованную функцию? - PullRequest
3 голосов
/ 19 апреля 2019

Итак, у меня возникла проблема, которую я не понимал, когда переменная отображается по-разному, в зависимости от функции, которую я использовал в контейнере, имеющем ее в качестве атрибута.Мне удалось свести его к следующему коду:

import copy
from functools import wraps


def do_wrapping(func):
    @wraps(func)
    def wrapper(*args):
        return func(*args)
    return wrapper


class Container:
    def __init__(self, var):
        self.var = var
        self.show = self._show_method

    @property
    def show(self):
        return self._show

    @show.setter
    def show(self, shower):
        self._show = do_wrapping(shower)

    def _show_method(self):
        print(self.var)

first_container = Container(1)
second_container = copy.deepcopy(first_container)
second_container.var = 0
second_container.show()
second_container._show_method()

Я хотел бы, чтобы оба метода распечатали 0, но первый печатает 1.Эта проблема решается с помощью этой строки second_container.show = second_container._show_method, но я бы хотел ее избежать, так как она кажется довольно хакерской.

В моем реальном случае обёртка немного сложнее, поэтому я не могу от нее избавиться.Если вы удалите упаковку, она работает как положено.Я предполагаю, что когда обернута, функция вроде как «установлена ​​в камне», но я не знаю, как это объяснить.

Так что мой вопрос состоит из двух частей: - почему этот код не работает должным образом (2 выхода 0)?- как мне это исправить?

1 Ответ

1 голос
/ 20 апреля 2019

Поскольку вы создаете second_container с помощью deepcopy, его метод __init__ никогда не вызывается, поэтому его атрибут _show копируется из first_container. Поскольку first_container._show - это функция, обертывающая first_container._show_method, это то, что вы получаете, когда вызываете second_container.show()

На вашем простом примере вы можете просто позвонить second_container.__init__(0) вместо second_container.var = 0. Это немного необычно, но может работать в вашем реальном коде.

Если вы не можете вызвать метод __init__ скопированного объекта, я думаю, что я бы предпочел сделать перенос функций при вызове метода show, а не во __init__ time:

class ContainerParent(object):
    @property
    def show(self):
        return do_wrapping(self._show)

    @show.setter
    def show(self, shower):
        self._show = shower

Если это невозможно, ну, есть много других возможностей - например, вы можете сделать var свойством и переместить строку self.show = self._show_method в его установщик - но трудно сказать, что было бы лучше, не зная больше о вашем фактическом коде.

...