【问题标题】:creating a new copy of a python class each time it's called每次调用 python 类时创建一个新副本
【发布时间】:2015-11-24 14:15:40
【问题描述】:

我需要一些指导,了解如何正确设置它以完成我正在尝试做的事情。 我有一个名为属性块的类,然后我将使用它来创建 3 或 4 个属性块对象。如下图...

class AttributeBlock():
    def __init__(self, key, label, isClosed, isRequired, attributes):
        self.key = key
        self.label = label
        self.isClosed = isClosed
        self.isRequired = isRequired
        self.attributes = attributes if attributes is not None else {}

3 属性块对象

AttributeBlock(
    key="Sphere",
    isRequired=True,
    attributes=[
        ''' Other class objects '''
        BoolProperty("ishidden", False, "Hidden"),
    ]
)

AttributeBlock(
    key="Box",
    isRequired=True,
    attributes=[
        ''' Other class objects '''
        BoolProperty("ishidden", True, "Hidden"),
    ]
)

AttributeBlock(
    key="Circle",
    isRequired=False,
    attributes=[
        ''' Other class objects '''
        BoolProperty("ishidden", True, "Hidden"),
    ]
)

然后我想要做的是能够将这些 AttributeBlocks 之一添加到一个对象中,确保在添加它时,它是 AttributeBlock 的一个新实例,因此它的子属性对象是新实例。

这是我将添加属性块的对象。

class ToyBox():
    def __init__(self, name="", attributes=[]):
        self.name = name
        self.attributes = attributes[:]

newToyBox = ToyBox()
newToyBox.name = "Jimmy"

伪代码

def add_attribute_block(toybox = None, key = "" ):
    if an AttributeBlock with the matching key exists:
        add it to toybox.attributes

add_attribute_block( newToyBox, "Box" )

print newToyBox
>>
ToyBox
name="Jimmy"
attributes=[
    AttributeBlock(
        key="Box",
        isRequired=True,
        attributes=[
            BoolProperty("ishidden", True, "Hidden"),
        ]
    ),
    AttributeBlock(
        key="Sphere",
        isRequired=True,
        attributes=[
            BoolProperty("ishidden", True, "Hidden"),
        ]
    )
]

【问题讨论】:

    标签: python class oop


    【解决方案1】:

    如果您想自动跟踪所有创建的AttributeBlockobjects,您可以使用类属性:

    class AttributeBlock():
        objects = []
        def __init__(self, key, label, isClosed, isRequired, attributes):
            self.key = key
            self.label = label
            self.isClosed = isClosed
            self.isRequired = isRequired
            self.attributes = attributes if attributes is not None else {}
            self.objects.append(self)
    

    完成此操作后,add_attribute 可能变为:

    def add_attribute_block(toybox = None, key = "" ):
        if toybox is not None:
            for obj in AttributeBlock.objects:
                if obj.key == key:
                    toybox.attributes.append(obj)
                    break
    

    您也可以为类属性使用映射而不是列表:

    class AttributeBlock():
        objects = {]
        def __init__(self, key, label, isClosed, isRequired, attributes):
            if key in self.objects:
                # raise a custom exception
            ...
            self.objects[key] = self
    

    那么你可以简单地使用:

    def add_attribute_block(toybox = None, key = "" ):
        if toybox is not None:
            if key in AttributeBlock.objects:
                toybox.attributes.append(AttributeBlock.objects[key])
    

    如果要将列表对象的副本放入ToyBox,则必须更改创建方法以允许不将该副本放入全局列表中。在这种情况下,代码将变为:

    class AttributeBlock():
        objects = {}
        dummy = {}
        def __init__(self, key, label, isClosed, isRequired,
                 attributes, glob = None):
            if glob is None:
                glob = self.objects
            if key in glob:
                raise ValueError(str(key) + " already exists")
            self.key = key
            self.label = label
            self.isClosed = isClosed
            self.isRequired = isRequired
            self.attributes = attributes if attributes is not None else {}
            if glob is not self.dummy:
                glob[key] = self
        def copy(self):
            return AttributeBlock(self.key, self.label, self.isClosed,
                          self.isRequired, self.attributes[:],
                          self.dummy)
    

    带有一个允许不将新创建的对象存储在任何容器中的虚拟类对象,以及一个允许将其存储在外部字典中的可选glob 参数。还要注意精确使用dummycopy 方法。

    add_attribute_block 变为:

    def add_attribute_block(toybox = None, key = "", glob = None ):
        if glob is None:
            glob = AttributeBlock.objects
        if toybox is not None:
            if key in glob:
                toybox.attributes.append(AttributeBlock.objects[key].copy())
    

    使用copy方法在ToyBox中存储一个未存储在全局容器中的原始对象的副本。

    【讨论】:

    • 这是一种有趣的方法。这是一个好习惯还是我应该创建一个更全局的变量来存储所有属性块
    • 在执行此方法时我应该注意他们的任何问题吗?似乎是一个很好的解决方案
    • @JokerMartini:没有一般性的答案。如果您想跟踪每个AttributeBlock,类属性很有趣。如果您希望能够拥有几个独立的列表(想想大学里的学生,如果您以后想与不同的大学打交道怎么办......),您最好有一个外部列表并将其传递给构造函数。就像经常发生的那样,这取决于实际用例。
    • 在我的情况下,我只需要一个包含所有创建的属性块的列表。如果是这种情况,那么您的解决方案似乎可以完美运行,对吗?
    • 我也注意到你在做一个直接的追加操作,我不需要做一个深拷贝吗?
    【解决方案2】:

    如果要确保添加到 ToyBox 的 Attribute 实例是副本,最简单的方法是使用 standard copy module

    import copy
    ...
    class ToyBox(object):
        ...
        def add_attribute(self, attribute):
            self.attributes.append(copy.deepcopy(attribute))
    

    【讨论】:

    • 如果是这样,我该如何设置 AttributeBlock 对象以便创建添加函数?
    • 不知道我明白你在问什么。 copy.deepcopy() 函数不需要为其设置对象的类。无论是什么类,copy.deepcopy(x) 都会复制一个对象 x。
    【解决方案3】:

    将所有属性块放在一个列表中。

    blocks = []
    
    // add your AttributeBlocks to this list
    blocks.append(block1)
    blocks.append(block2)
    blocks.append(block3)
    

    那么就简单了。

    def add_attribute_block(toybox, key):
        #loop over list of blocks and find the block with that key
        for block in blocks:
            if block.key == key:
                #only add it to the toybox if its not already in there
                if not any(key in l.key for l in toybox.attributes):
                    toybox.attributes.append(block)
                    break
    

    注意:

    l.key for l in toybox.attributes 是一个列表推导,并为您提供所有键的列表。

    如果key 在该列表中,则any(key in l.key for l in toybox.attributes) 返回True

    【讨论】:

      【解决方案4】:

      如果我理解正确,您希望每个 ToyBox 实例都包含一个 AttributeBlock 实例列表,使用 列表中还存在相同的名称。

      class AttributeBlock():
          def __init__(self, key): # demo, add the other parameters/attrs
              self.key = key
          def __str__(self):
              return self.key # add the other parameters/attrs
      
      class ToyBox(object):
          def __init__(self):
              self.attributes = []
      
          def add_attr(self, a):
              gen = (attr for attr in self.attributes if attr.key == a.key)
              try:
                  next(gen)
              except StopIteration:
                  self.attributes.append(a)
      
          def __str__(self):
              return ','.join(map(str,self.attributes))
      

      所以现在我们可以做

      >>> toy = ToyBox()
      >>> toy.add_attr(AttributeBlock("Box"))
      >>> toy.add_attr(AttributeBlock("Sphere"))
      >>> toy.add_attr(AttributeBlock("Box"))
      >>> print toy
      Box,Sphere
      

      如您所见,将add_attribute 函数设为ToyBox 的实例方法是有意义的

      顺便说一句,如果attributes 列表中的对象数量很大,最好使用字典:

      class ToyBox(object):
          def __init__(self):
              self.attributes = dict()
      
          def add_attr(self, a):
              if a.key not in self.attributes:
                  self.attributes[a.key] = a
      
          def __str__(self):
              return ','.join(map(str,self.attributes.values()))
      

      注意:如果您想保持添加对象的顺序,请改用OrderedDict

      【讨论】:

      • 你在正确的轨道上。当我创建所有 attritubeBlocks 时,我将它们存储在哪里。我只想在工具首次启动时创建一次。我应该按照 Serge Ballesta 下面的建议去做吗?
      • 它们存储在 ToyBox 实例的列表中。查看我编辑中的示例,无需在变量中跟踪它们
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多