14

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

这和预期的一样——deepcopySomeClass()为 b 创建了新对象。

但如果,

f = lambda x:x+1
a = [f]
b = copy.deepcopy(a)

我得到:

>>> a
[<function <lambda> at 0x10056e410>]
>>> b
[<function <lambda> at 0x10056e410>]

为什么 deepcopy 在第二种情况下不创建新的 lambda 实例?这是否意味着 lambda 函数是原子的?

4

4 回答 4

6

这不仅适用于 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

于 2012-05-29T15:30:01.270 回答
5

作为一些认为 lambda 不可变的人的旁注,请注意以下行为:

>>> a = lambda x: x + 1
>>> a(12)
13
>>> b = lambda x: x - 1
>>> b(12)
11
>>> a.__code__ = b.__code__
>>> a(12)
11
>>> 
于 2012-05-29T16:20:55.637 回答
4

查看源代码中的第 222 和 223行:

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

该模块认为它们是原子的,我不知道如何改变 lambda。

于 2012-05-29T15:22:52.187 回答
0

我不知道 Python 的完整历史,但函数过去可能像字符串和元组一样不可变,因此最初的deepcopy行为是完全合理的。

此外,在实践中,大多数人更关心他们的数据对象被复制而不是他们的功能。制作函数副本的必要性deepcopy是一个明确的边缘案例,期望受该边缘案例影响的人推出他们自己的解决方案对我来说似乎完全没问题。

一种合理的方法是编写一个装饰器@copyablefunc,将函数包装在可调用对象(即具有__call__()方法的类)中,并且还定义例程要使用的方法__copy__()__deepcopy__()deepcopy

copy.deepcopy顺便说一句,也没有真正复制课程。

于 2012-05-29T17:40:15.347 回答