是否存在无法创建深拷贝的对象?

7 投票
3 回答
3733 浏览
提问于 2025-04-20 03:16

当我执行以下代码时,出现了一个错误信息:

img = copy.deepcopy(img_file_obj)

这个 img_file_obj 的类型是:

<class 'werkzeug.datastructures.FileStorage'>

难道不允许创建一个文件存储对象的深拷贝吗?

补充说明

我可能需要解释一下,为什么我想要创建一个文件存储对象的副本。在我代码的最后,我执行了:

img_obj.save(fname)

但在那之前,我需要检查一下文件的大小。我是这样做的:

img_obj.seek(0, os.SEEK_END)
size = img.tell()

问题是,检查文件大小会“破坏”文件。如果我检查了文件大小,然后再保存,就会在磁盘上得到一个空文件。这就是我想要创建文件对象副本的原因,我想先检查副本的大小,如果大小合适,再把原始文件对象保存到磁盘上。

3 个回答

2

来自copy模块的文档

这个模块不会复制像模块、方法、堆栈跟踪、堆栈帧、文件、套接字、窗口、数组或任何类似类型的东西。

因为werkzeug.datastructures.FileStorage被描述为“对传入文件的一个简单封装”,所以我认为它可以算作上面提到的“类似类型”的file

3

确实可以创建一种无法进行深拷贝的类型。文档中提到:

为了让一个类定义自己的拷贝实现,它可以定义特殊的方法 __copy__()__deepcopy__()

所以,一个类可以通过在这些方法里抛出异常来让自己变得无法进行深拷贝。

从概念上讲,像文件这样的东西通常是不太可能被深拷贝的。对文件进行深拷贝,可能意味着要复制磁盘上的实际文件。如果文件很大,这样的操作可能会很耗资源;如果用户没有足够的权限,或者磁盘空间不足,那就更麻烦了。尝试复制一些不局限于程序本身的资源时,可能会出现很多问题。因此,引用外部资源的对象可能无法进行深拷贝;如果你真的想深拷贝它们,就必须手动复制外部资源,并创建一个新的对象。

5

有没有一些对象是无法进行深拷贝的?

有的。

任何类型重写了标准的 __deepcopy__ 方法,或者通过 copyreg 注册了一个会抛出异常的函数的对象,都无法进行深拷贝。

任何使用标准 __deepcopy__ 方法但无法进行(浅)拷贝的对象,也无法进行深拷贝。

任何包含子元素的对象(无论是通过标准的 __deepcopy__ 还是它自己的实现)如果子元素无法进行深拷贝,那么这个对象也无法进行深拷贝。

还有很多类型的对象甚至连浅拷贝都无法进行。关于 copy 模块的文档中给出了一些例子:

这个模块不会拷贝像模块、方法、堆栈跟踪、堆栈帧、文件、套接字、窗口、数组或任何类似类型的对象。

并不是说拷贝文件是不可能的,但这意味着什么其实很模糊(是复制文件句柄、重新打开文件,还是共享句柄?应该从同样的文件指针开始吗?它的缓冲区或状态编码器必须处于相同状态吗?),所以让它们可以被拷贝反而会让人困惑,而不是提供帮助。

当然,Werkzeug 框架可以选择让它的 FileStorage 对象可以被拷贝,尽管标准的 Python 文件对象不能,但他们可能有同样的理由不这样做。

撰写回答