【问题标题】:Python: How to generate n different copies of an objectPython:如何生成对象的 n 个不同副本
【发布时间】:2013-12-08 23:16:58
【问题描述】:

我有一个对象,其中包含几个字典,其中填充了其他对象以及一个 id 和对象对列表。

每次对其中一个字典进行更改时,生成此对象的 n 个副本的最 Pythonic 方式是什么?

例如:

class B(object):
    pass

class A(object):

    def __init__(self):
        d = {1: B(), 2: B(), 3: B()}


a = A()
b = [(1, B()), (2, B())]

我想生成两个 a 的副本,每个副本都有一个不同的 B 实例。如果可能的话,我想避免使用 deepcopy。

编辑:因为我被要求更具体:

我正在运行 n 次计算,每次更改某个子模块的一个参数。我想避免在内存中保存子模块的 n 个副本,因为它是一个相当大的对象,我想避免重新初始化模拟的其他部分。因此,我想要一个初始化的基础对象,我想要对其进行 n 次小的修改,从而使用不同的参数运行 n 次计算。

【问题讨论】:

  • 为什么要避免在这里使用deepcopy()b 与创建 A() 的副本有什么关系?
  • 请注意,A.d 是一个 class 属性。 A() 的所有实例都共享该属性,此处无需复制。如果A.d 应该是实例属性,请将其设置为__init__
  • 最后但同样重要的是,A.d,即使它是一个实例属性,也不会改变。如果您想要一个新的A() 并为B() 提供新值,只需创建一个新实例。此处无需复制。
  • 首先,这就是我想避免 deepcopy 的原因:twitter.com/gvanrossum/status/383618349000691712 其次,很抱歉 - 我现在修复了它。它应该是一个实例属性。第三,我不想创建 A 类的新实例的原因是因为它是一个非常大的对象,我希望副本除了一个小的变化之外是相同的,即更新一个字典元素。
  • 那是因为每当使用 deepcopy 时,程序员就放弃了,选择了便宜的选择。 很少需要使用 deepcopy。重新考虑这种情况并询问是否真的需要完整对象层次结构的深层副本。

标签: python copy generator deep-copy


【解决方案1】:

A().d 永远不会更改,因此创建副本或创建新实例没有区别。

所以要创建一个新的 A() 实例来保存新的、独立的 B() 实例,只需创建一个新实例:

second_a = A()

实例之间的任何变化都可以直接在这个新实例上创建。如果创建此类对象的成本很高,请使用缓存策略以避免重复计算。

【讨论】:

    【解决方案2】:

    我认为import_fresh_module 可以为您带来帮助,根据文档中的描述和您的需求描述。但我不能说更多,不够熟练。

    【讨论】:

      【解决方案3】:

      您的示例没有将d 分配给任何东西,因此当__init__ 返回时它将丢失。但是 cmets 建议您希望它是 A 的实例属性;我假设这就是你想要的。我会实现一个copy 方法来复制你想要的东西。例如,我相信这符合您指定的标准,至少对于您提供的示例代码:

      class B(object):
          pass
      
      class A(object):
      
          def __init__(self, d=None):
              if d is not None:
                  self.d = d
              else:
                  self.d = {1: B(), 2: B(), 3: B()}
      
          def copy(self, updates=None):
              a = type(self)(self.d.copy())
              if updates is not None:
                  a.d.update(updates)
              return a
      
      
      a = A()
      b = [(1, B()), (2, B())]
      a2 = a.copy(b)
      

      【讨论】:

        【解决方案4】:

        你可以在这里做一种单例模式,它会缓存“昂贵”的位,并允许你通过再次调用它来设置变量位来设置不同的参数:

        class BigAssThang(object):
            instances={}
            def __new__(cls, ID, *args):
                if ID in cls.instances:
                    return cls.instances[ID]
                else:
                    # do somthing really big, expensive and immutable...
                    cls.instances[ID]=super(BigAssThang, cls).__new__(cls)
                    return cls.instances[ID]
        
            def __init__(self, ID, thing1=None, thing2=None, etc=None):
                # set the parameters...
                # ANY PREVIOUS INSTANCE's PARAMS ARE WIPED OUT!!!
                self.ID=ID
                self.thing1=thing1
                self.thing2=thing2
                self.etc=etc
        
            def __repr__(self):
                return 'id={} ID: {} thing1: {} thing2: {} etc: {}'.format(
                     id(self), self.ID, self.thing1, self.thing2, self.etc) 
        

        使用方法:

        >>> a=BigAssThang(1,'thing 1', {}, [])
        >>> a.etc.append(2)
        >>> a
        id=4481412944 ID: 1 thing1: thing 1 thing2: {} etc: [2]
        >>> b=BigAssThang(1,'thing 1', {}, [])
        >>> b
        id=4481412944 ID: 1 thing1: thing 1 thing2: {} etc: []
        >>> a
        id=4481412944 ID: 1 thing1: thing 1 thing2: {} etc: []
        >>> c=BigAssThang(2,'thing 1', {'a':22}, [1,2,3])
        >>> c
        id=4481413072 ID: 2 thing1: thing 1 thing2: {'a': 22} etc: [1, 2, 3]
        >>> b
        id=4481412944 ID: 1 thing1: thing 1 thing2: {} etc: []
        

        要知道,一旦将参数设置为 ba 就会消失。它由__new__ID 参数控制——而不是Python 变量名。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-10-03
          • 1970-01-01
          • 1970-01-01
          • 2011-06-15
          相关资源
          最近更新 更多