Глубокая копия списка Python при назначении

Получил это упражнение на экзамене по Python. Попытка вернуть глубокую копию списка следующим образом:

l = list()
l = [0,1,2]
l1 = l
l[0] = 1

l1 должен содержать [0,1,2], а не [1,1,2]

Упражнение указано для его реализации с помощью метакласса.

class deep(type):
    def __new__(meta, classname, bases, classDict):
        return type.__new__(meta, classname, bases, classDict)
    def __init__(cls, name, bases, dct):
        super(deep, cls).__init__(name, bases, dct)
    def __call__(cls, *args, **kwds):
        return type.__call__(cls, *args, **kwds)            
class list(metaclass=deep):
    def __init__(self):
        pass

Насколько я знаю, '=' в python — это оператор, а не оператор, и его нельзя переопределить. Любая идея о том, как вернуть глубокую копию по заданию? Пробовали довольно много, но безуспешно.


person amn    schedule 24.02.2012    source источник
comment
Короче говоря, вы не можете. И даже если какой-нибудь изобретательный хакер найдет способ, его никогда не следует использовать. И я не буду делать такие опрометчивые заявления, поверьте мне.   -  person    schedule 24.02.2012
comment
Это довольно странное упражнение, учитывая, что вы можете выполнить глубокую копию всего с тремя символами, используя нотацию среза (l1 = l[:]). Использование метаклассов здесь кажется излишним.   -  person Frédéric Hamidi    schedule 24.02.2012
comment
Если это экзаменационный вопрос для школы, я предлагаю вам просто закрыть его. Просить нас помочь с вашим экзаменом — это верх академического списывания.   -  person S.Lott    schedule 24.02.2012
comment
@S.Lott: возможно, экзамен закончен. По крайней мере, он должен быть помечен homework или exam.   -  person ninjagecko    schedule 24.02.2012
comment
С точки зрения этого, кажется, что он предназначен для реализации прозрачной замены стандартного класса списка, чтобы добавить автоматическое глубокое копирование поведения __repr__ в списки. Надеюсь, это поможет.   -  person Don Question    schedule 24.02.2012
comment
@Frederic Hamidi: возможно, в вашей версии Python вы можете сделать глубокую копию таким образом, большинству других нужно from copy import deepcopy и b=deepcpy(a), иначе копируется только основной список, а все вложенные классы-контейнеры сохраняются в качестве ссылок в новом списке! Вы можете проверить это, например: for l in (a,b): id(l); map (id,l)   -  person Don Question    schedule 24.02.2012
comment
Спасибо всем за ответы. @ninjagecko да, экзамен закончился пару дней назад, я пытался сделать это только для обучения.   -  person amn    schedule 24.02.2012
comment
@ Дон, да, я знаю об этом, может быть, мне не следовало использовать deep в этом контексте отдельно. Тем не менее, список в вопросе имеет глубину только одного уровня, поэтому копия фрагмента будет работать :)   -  person Frédéric Hamidi    schedule 24.02.2012


Ответы (2)


Насколько мне известно, в python это невозможно без использования какого-то дополнительного синтаксиса. Как вы сказали, вы не можете переопределить =.

person Daenyth    schedule 24.02.2012
comment
но он может использовать метод __repr__ для достижения аналогичных результатов! просто возвращайте не строку, а новый класс! - person Don Question; 24.02.2012
comment
@DonQuestion: вызывается ли __repr__ при привязке имени? Это кажется странным. - person Daenyth; 24.02.2012
comment
@Дэнит Это не так. Дон Вопрос сбился с пути. - person ; 24.02.2012
comment
@DonQuestion Я тоже пытался использовать repr, возвращая copy.deepcopy(self), но python возвращает: TypeError: repr возвращает нестроковый (список типов), потому что он ожидает только строки от repr я думаю - person amn; 24.02.2012
comment
Я имел в виду не repr(), а специальный метод __repr__ с двумя символами подчеркивания. Тем не менее, Делнан прав, потому что не похоже, чтобы вы могли копировать таким образом. Я не проверял это раньше, поэтому я был не в курсе. Извини! - person Don Question; 24.02.2012

Чтобы понять, что l1 = l делает в Python, прочитайте В других языках есть «переменные», в Python — «имена».

Чтобы изменить то, что делает l[0] = 1, вы можете переопределить метод __setitem__() l:

>>> class L(list):
...     def __setitem__(self, i, v):
...         list.__setitem__(self, i, 10)
... 
>>> l = L()
>>> l.append(1)
>>> l
[1]
>>> l[0] = 0
>>> l
[10]

Чтобы изменить то, что делает l = List(); l = [0,1,2], вы можете определить метод List.__del__() в CPython и манипулировать пространством имен, к которому принадлежит l, например, используя модуль inspect. Излишне говорить, что вы никогда не должны этого делать.

>>> import inspect
>>> class L:
...     def __del__(self):
...         inspect.currentframe().f_globals['l'] = [1,2,3]
... 
>>> l = L()
>>> l = [0]
>>> l
[1, 2, 3]
person jfs    schedule 24.02.2012