为什么在复制复杂对象时 deepcopy 会失败

And*_*ard 5 python object python-3.x

如何复制复杂对象以便可以向其中添加新成员?当我尝试使用 deepcopy 时,它失败并显示“ TypeError: cannot serialize...”

最初的问题是我想向现有对象添加一些成员变量但不能,因为这样做会导致“ AttributeError: Object is fixed

因此,想法是在具有添加成员的新类中创建原始对象的完整副本。

orig_obj = SomeSqlObject.get_root() # contents unclear, complex

class orig_expanded():
    def __init__(self, replicable_object):
        self.__dict__ = copy.deepcopy(replicable_object.__dict__)

        self.added_member1 = None
        self.added_list    = []

expanded_thing = orig_expanded(orig_obj)
Run Code Online (Sandbox Code Playgroud)

但我得到:

TypeError: cannot serialize '_io.TextIOWrapper' object
Run Code Online (Sandbox Code Playgroud)

评论的后续回答,“什么是 SomeSqlObject?” 也许我的名字是错误的......公司的实际名称被混淆了。它是一种返回一个对象的方法,该对象表示(某种)树的基部该树被定义

class SomeSqlObject(ParentRegisterSet):
    """
    Implements the functionality of the Device "root" Register Set.

    """
    def __init__(self, db, v1, dg, ui):
        self.__db__ = db
        self.__dg__ = dg
        self.__ui__ = ui
        SomeSqlObject.__init__(self, v1, None)

        # note:  this class is now locked
Run Code Online (Sandbox Code Playgroud)

Sha*_*ger 5

copy.deepcopy对不提供直接支持(通过定义__deepcopy__)的类的行为是pickle然后unpickle对象以确保创建新实例。io.TextIOWrapper(这是一个包装器,而不是将二进制文件类对象转换为文本文件类对象)不能序列化(因为它假定它可能具有外部/运行时状态,例如在文件中具有特定位置的文件描述符以后反序列化时可能不可用)。

出现错误是因为您正在复制的对象包含io.TextIOWrapper,并且序列化失败。

如果共享状态没问题,您可能会将自己限制为浅拷贝,或者使用基于组合的包装器(基于__getattr__)通过包装器对象半无缝地访问底层对象(除了那些讨厌的特殊方法),或者您可能尝试单独从字典中深度复制值并忽略您无法复制的值,例如:

for attr, value in vars(replicable_object).items():
    try:
        setattr(self, attr, copy.deepcopy(value))
    except Exception:
        pass
        # Alternatively, copy reference when copy impossible:
        #setattr(self, attr, value)
Run Code Online (Sandbox Code Playgroud)

只希望你不能复制的东西不要太重要。


And*_*ard 0

好的找到答案了。

尝试执行 a 时看到的原始错误setattr()AttributeError: Object is fixed. That was an error from code in the original SomeSqlObject for a custom __setatter__()that was getting Checking for bit _attr_lockand PreventingAdding Members to the object. 一旦我解除此锁定,我就可以轻松添加成员。

最初的问题是我有许多形式为 、 、 、 等的类成员(称为 id)。id0它们id1id3id2每一个也是一个复杂的对象。但是,从代码用户的角度来看,更好的方法是使用列表类型成员id[#]来访问它们。因此,我需要添加一个列表类型成员id[],并确保每个连续元素都指向id0, id1, ie 所指向的相同对象。id[0]id[1], ETC。

因此,我获取复杂对象并添加列表类型成员的最终代码是。

# Below we ADD ON to the existing SqlObject with a list-type member
# and populate the list with proper pointers to each register/register-
# set. This is PFM!

id_name_re = "\W*id\D*(\d+)"

# SqlObject has a feature to block adding attributes.... This will override
self.regs._attr_lock = None

# add a list-type placeholder in the SqlObj for a id array
setattr(self.regs, 'id', [])

# now get a list of all SqlObject  members called id<#> like:
#  ['id13', 'id12', 'id11', 'id10', 'id9', 'id8', 'id14 ...
id_list = [id_member  for id_member in self.regs.__dict__ if re.match(id_name_re, id_member)]

# Sort the list since we need to place them in the new id[] list
# sequentially
id_list = sorted(id_list, key=h.natural_sort_key)

# now go through the list and create a new list element and populate
# it with the SqlObject  goofy-name which is not in a list-type format
for id_member in id_list:
    offset = int(re.match(id_name_re, id_member).group(1))

    # this is NEEDED!. It causes the SqlObject  to rescan and keep
    # everything updated ('_' is kinda like /dev/null)
    _ = eval("self.regs.id%d" % (offset))

    self.regs.id.append(self.regs.__dict__[id_member].__getitem__.__self__)
Run Code Online (Sandbox Code Playgroud)