【问题标题】:Why does deepcopy fail when copying a complex object复制复杂对象时为什么deepcopy会失败
【发布时间】:2015-09-15 18:23:19
【问题描述】:

如何复制一个复杂的对象,以便向它添加新成员?当我尝试使用 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)

但我明白了:

TypeError: cannot serialize '_io.TextIOWrapper' object

评论的后续回答,“什么是 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

【问题讨论】:

  • SomeSqlObject.get_root() 返回什么?
  • 我的名字可能会误导...试图混淆公司代码...但是,这是我称之为 SomeSqlObject 的类结构:class SomeSqlObject(ParentRegisterSet): """ 实现的功能设备“根”寄存器集。“””def init__(self, db, v1, dg, ui): self.__db = db self.__dg__ = dg self.__ui__ = ui SomeSqlObject.__init__ (self, v1, None) # 注意:这个类现在被锁定了
  • 好吧,那失败了……也许我必须用不同的方法来回答这个问题……
  • 尝试在python3中复制文件对象是行不通的,SomeSqlObject.get_root()实际上返回了什么?

标签: python object python-3.x


【解决方案1】:

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

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

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

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)

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

【讨论】:

  • 是的......那里的一切都很重要,包括创建 _io.TextIOWrapper 错误的 I/O 功能。感谢您的协助。找到上面的答案。
【解决方案2】:
TypeError: cannot serialize '_io.TextIOWrapper' object

这个异常意味着在某个地方,你的对象以某种方式链接到一个文件对象、一个套接字或类似的东西。

TextIOWrapper 是包装文件描述符并允许您读取/写入 unicode 字符串的类。

而且,如您所见,TextIOWrapper 无法复制。

【讨论】:

    【解决方案3】:

    我猜你真正想要的是一个 proxy 类,来自 google 的一个例子: http://python-3-patterns-idioms-test.readthedocs.org/en/latest/Fronting.html

    您将从要包装的对象初始化您的代理类;代理类知道的属性在本地处理;代理类不知道的属性被传递给被包装的对象。

    (通常,如果你自己创建这些对象,你会继承子类......听起来这不是一个选项......)

    【讨论】:

    • YES... 看起来正是我需要的... 然后我就不必去查看我的原始对象并发现 setattr 方法使用了阻塞我必须重写的机制。我希望早点看到这个。 ...我将来会以这种方式实现它。谢谢。
    【解决方案4】:

    好的,找到答案了。

    在尝试执行 setattr() 时看到的原始错误是 AttributeError: Object is fixed. 这是原始 SomeSqlObject 中自定义 __setatter__() 代码中的错误,该代码正在寻找检查位 _attr_lock 并阻止将成员添加到目的。一旦我取消了这个锁,我就可以轻松地添加成员了。

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

    所以,我最终采用复杂对象并添加列表类型成员的代码是。

    # 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__)
    

    【讨论】:

      猜你喜欢
      • 2016-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-03
      • 1970-01-01
      • 2014-11-17
      • 2022-02-01
      • 1970-01-01
      相关资源
      最近更新 更多