Ответ 1
Посмотрите на строки 222 и 223 из исходного кода:
d[types.BuiltinFunctionType] = _deepcopy_atomic
d[types.FunctionType] = _deepcopy_atomic
Модуль считает их атомарными, и я не знаю, как вы можете мутировать lambda.
Обнаружено это странным в python:
class SomeClass():
def __init__(self):
pass
a = [SomeClass()]
b = copy.deepcopy(a)
Вывод:
>>> a
[<__main__.Some instance at 0x10051b1b8>]
>>> b
[<__main__.Some instance at 0x10051b092>]
Это так же, как ожидалось, - deepcopy создал новый объект SomeClass()
для b.
Но если,
f = lambda x:x+1
a = [f]
b = copy.deepcopy(a)
Я получаю:
>>> a
[<function <lambda> at 0x10056e410>]
>>> b
[<function <lambda> at 0x10056e410>]
Почему deepcopy не создает новый экземпляр lambda во втором случае? означает ли это, что лямбда-функции являются атомарными?
Посмотрите на строки 222 и 223 из исходного кода:
d[types.BuiltinFunctionType] = _deepcopy_atomic
d[types.FunctionType] = _deepcopy_atomic
Модуль считает их атомарными, и я не знаю, как вы можете мутировать lambda.
Это относится не только к лямбдам, но и к функциям без состояния в целом.
>>> def some_function(word): print word
>>> a = [some_function]
>>> a
[<function some_function at 0x1007026e0>]
>>> copy.deepcopy(a)
[<function some_function at 0x1007026e0>]
Поскольку функции не сохраняют состояние, deepcopy не создает для них новую ссылку. Интересное обсуждение тем, подобных этой проблеме (хотя и не совсем такая же проблема), записывается здесь: http://bugs.python.org/issue1515
В качестве побочной заметки для некоторых людей, которые думают, что лямбды не изменяются, наблюдайте следующее поведение:
>>> a = lambda x: x + 1
>>> a(12)
13
>>> b = lambda x: x - 1
>>> b(12)
11
>>> a.__code__ = b.__code__
>>> a(12)
11
>>>
Я не знаю полной истории Python, но возможно, что функции были неизменными, как строки и кортежи, и поэтому изначально было вполне разумно вести себя как deepcopy
.
Кроме того, на практике большинство людей больше обеспокоены тем, что их объекты данных копируются, чем их функции. Необходимость deepcopy
сделать копию функций - это явный крайний случай, и ожидание того, что люди, подверженные этому краевому делу, откатывают свое собственное решение, кажется мне совершенно прекрасным.
Разумным подходом было бы написать декоратор @copyablefunc
, который обертывает функцию в вызываемом объекте (т.е. классе, который имеет метод __call__()
), а также определяет методы __copy__()
и __deepcopy__()
, которые будут использоваться с помощью процедуры deepcopy
.
copy.deepcopy
также по-настоящему не копирует классы, кстати.