JokerMartini
JokerMartini

Reputation: 6147

creating a new copy of a python class each time it's called

I need some guidance on how to set this up correctly for what I'm trying to do. I have a class called Attribute Block which I'll then use to create 3 or 4 Attribute block Objects. As seen below...

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 objects

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"),
    ]
)

What I then want to do is be able to add one of these AttributeBlocks to an Object, making sure when it's added, its a new instance of the AttributeBlock so its sub attribute objects are new instances.

This is the object I will add my attribute blocks to.

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

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

pseudo code

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"),
        ]
    )
]

Upvotes: 2

Views: 113

Answers (4)

Serge Ballesta
Serge Ballesta

Reputation: 148890

If you want to automatically keep track of all created AttributeBlockobjects, you could use a class attribute:

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)

Once this is done, add_attribute could become:

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

You could also use a mapping instead of a list for the class attribute:

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

Then you can simply use:

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

If you want to put a copy of the objects of the list in the ToyBox, you must change the creation method to allow to not put that copy in the global list. In that case, code would become:

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)

with a dummy class object that allow to not store the newly created object in any container, and an optional glob parameter that allows to store it in an external dict. Also notice the copy method that precisely uses dummy.

add_attribute_block becomes:

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())

using the copy method to store in the ToyBox, a copy of the original object that is not stored in the global container.

Upvotes: 1

Pynchia
Pynchia

Reputation: 11590

If I understand correctly, you want the each ToyBox instance to contain a list of AttributeBlock instances, checking no other such object with the same name is present in the list yet.

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

So now we can do

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

As you notice, it makes sense to make the add_attribute function an instance method of ToyBox

BTW, in case the number of objects in the attributes list is large, it's better to use a dictionary:

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()))

Note: in case you want to keep the order in which the objects are added, use an OrderedDict instead

Upvotes: 1

haffla
haffla

Reputation: 1066

Put all your attribute blocks in a list.

blocks = []

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

Then it's easy.

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

Note:

l.key for l in toybox.attributes is a list comprehension and gives you a list of all keys.

any(key in l.key for l in toybox.attributes) returns True if key is in that list.

Upvotes: 1

Andrew Allaire
Andrew Allaire

Reputation: 1330

If you want to make sure the Attribute instance added to your ToyBox is a copy, the easiest way is to use the standard copy module

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

Upvotes: 1

Related Questions