user8491363
user8491363

Reputation: 3921

How do I get the sequence of creation of class instances in Python?

Here's what I'm trying to do. I'm trying to make a note app that creates a Note that consists of NoteBlocks. People would usually create Note from the first block to the last so I want to somehow sort class instances by their order of creation.

Is there any way to somehow get the number of the instance's creation? The first instance I created would have 1 and the second 2 and so on.

Alternatively, I've thought of the other way to do this. If I can somehow make the newly created NoteBlock to point to the previous block already created, I would be able to sort them like a simple linked list.

The worst way I can think of is to give each instance a physical self.created_at attribute to order them by the time of creation but I think it is a dumb way to do it and hopefully there is another way.

Given my scenario, what do you suggest I do?

from datetime import datetime

Class NoteBlock():
  def __init__(self):
    self.contents = None
    self.previous = None
    self.created_at = datetime.now()
    return

a = Foo()
b = Foo()
c = Foo()
d = Foo()

Upvotes: 0

Views: 739

Answers (3)

musicamante
musicamante

Reputation: 48509

I would like to expand the class variable solution provided by Hkoof, by adding direct reference to the instancies, which can also help keeping a class-consistent instance list, allowing access to the previous/next created instance.

The only problem (which, actually, isn't covered by other solutions either) is that the removal of an instance requires an explicit method call, as del won't suffice: __del__ is only called as soon as there's no reference left to the object. Since there's obviously no way to know if the user/programmer is keeping any reference to it, we need an explicit way to do that; it won't ensure that the instance will be garbage collected, as that will only happen as soon as there's no reference left to it.

class NoteBlock(object):
    instancies = []

    def __init__(self, id):
        self.id = id

    def __new__(cls, *args, **kwargs):
        instancy = object.__new__(cls)
        cls.instancies.append(instancy)
        return instancy

    def delete(self):
        self.instancies.remove(self)

    # the following property methods are only useful for "browsing" between the
    # instance list

    @property
    def previous(self):
        try:
            # "-1" index returns the last object in the instancies list, we
            # don't want that...
            previous_index = self.instancies.index(self) - 1
            assert previous_index >= 0
            return self.instancies[previous_index]
        except:
            return

    @property
    def next(self):
        try:
            return self.instancies[self.instancies.index(self) + 1]
        except:
            return

# create some random objects
from random import randrange

scope_instance_list = []

print('Creating instancies:')
for i in range(8):
    index = randrange(100)
    block = NoteBlock(index)
    scope_instance_list.append(block)
    print('\t{} - Block {} created'.format(i + 1, index))

# remove a single instance
toRemoveIndex = randrange(8)
toRemove = scope_instance_list.pop(toRemoveIndex)
print('\nRemoving instance n. {} ({})...'.format(toRemoveIndex + 1, format(toRemove.id)))
# we can't use "del", as the __del__ magic method only works as soon as there is
# *no* reference left for the object: since we're keeping the "instancies" list
# it will never be called, then we need to use an "internal" way to do that;
# keep in mind that if you have *any* reference to that object, it will never be
# garbage collected until it's "released".
toRemove.delete()
print('Done!\n')

# show the current instance list, including previous and next instancies,
# according to the existing objects
print('Remaining instance list (class instance based):')
for i, inst in enumerate(block.instancies):
    print('\t{} - Block {}: previous: {}, next: {}'.format(
        i + 1, 
        inst.id, 
        inst.previous.id if inst.previous else None, 
        inst.next.id if inst.next else None))

Example output:

Creating instancies:
    1 - Block 10 created
    2 - Block 23 created
    3 - Block 4 created
    4 - Block 28 created
    5 - Block 9 created
    6 - Block 67 created
    7 - Block 70 created
    8 - Block 73 created

Removing instance n. 5 (9)...
Done!

Remaining instance list (class instance based):
    1 - Block 10: previous: None, next: 23
    2 - Block 23: previous: 10, next: 4
    3 - Block 4: previous: 23, next: 28
    4 - Block 28: previous: 4, next: 67
    5 - Block 67: previous: 28, next: 70
    6 - Block 70: previous: 67, next: 73
    7 - Block 73: previous: 70, next: None

Upvotes: 0

Hkoof
Hkoof

Reputation: 796

You can use a class variable to keep track of the number of instances created:

class NoteBlock:
    instance_count = 0  # <== Note strange placement:
                        #     it's a class variable (also
                        #     called "class attribute")

    def __init__(self):
        NoteBlock.instance_count += 1   # <== Note class namespace (NoteBlock)
        self.instance_number = NoteBlock.instance_count

    def __str__(self):
        return str(self.instance_number)

note1 = NoteBlock()
note2 = NoteBlock()
note3 = NoteBlock()
note4 = NoteBlock()

# ...and for good measure, assign note2 another instance
#
note2 = NoteBlock()

print(note1)
print(note2)
print(note3)
print(note4)

Output:

1
5
3
4

Upvotes: 2

Marco Zamboni
Marco Zamboni

Reputation: 224

An object doesn't save the time of instantiation automatically; to do so you have to add an attribute and save at __init__ the time (as you showed).

But if you don't like to create an attribute of the object itself you can as well have a data structure outside containing the object in order, for example a simple list:

foos = []
foos.append(Foo())
...
foos.append(Foo())
...
foos.append(Foo())
...

foos[0] #the first created
foos[1] #the second
foos[2] #the third

Upvotes: 1

Related Questions