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

Почему deepcopy не создает новые ссылки на функцию лямбда?

Обнаружено это странным в 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 во втором случае? означает ли это, что лямбда-функции являются атомарными?

4b9b3361

Ответ 1

Посмотрите на строки 222 и 223 из исходного кода:

d[types.BuiltinFunctionType] = _deepcopy_atomic
d[types.FunctionType] = _deepcopy_atomic

Модуль считает их атомарными, и я не знаю, как вы можете мутировать lambda.

Ответ 2

Это относится не только к лямбдам, но и к функциям без состояния в целом.

>>> 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

Ответ 3

В качестве побочной заметки для некоторых людей, которые думают, что лямбды не изменяются, наблюдайте следующее поведение:

>>> a = lambda x: x + 1
>>> a(12)
13
>>> b = lambda x: x - 1
>>> b(12)
11
>>> a.__code__ = b.__code__
>>> a(12)
11
>>> 

Ответ 4

Я не знаю полной истории Python, но возможно, что функции были неизменными, как строки и кортежи, и поэтому изначально было вполне разумно вести себя как deepcopy.

Кроме того, на практике большинство людей больше обеспокоены тем, что их объекты данных копируются, чем их функции. Необходимость deepcopy сделать копию функций - это явный крайний случай, и ожидание того, что люди, подверженные этому краевому делу, откатывают свое собственное решение, кажется мне совершенно прекрасным.

Разумным подходом было бы написать декоратор @copyablefunc, который обертывает функцию в вызываемом объекте (т.е. классе, который имеет метод __call__()), а также определяет методы __copy__() и __deepcopy__(), которые будут использоваться с помощью процедуры deepcopy.

copy.deepcopy также по-настоящему не копирует классы, кстати.