How do you create an incremental ID in a Python Class

Question:

I would like to create a unique ID for each object I created – here’s the class:

class resource_cl :
    def __init__(self, Name, Position, Type, Active):
        self.Name = Name
        self.Position = Position
        self.Type = Type
        self.Active = Active

I would like to have a self.ID that auto increments everytime I create a new reference to the class, such as:

resources = []
resources.append(resource_cl('Sam Sneed', 'Programmer', 'full time', True))

I know I can reference resource_cl, but I’m not sure how to proceed from there…

Asked By: meade

||

Answers:

First, use Uppercase Names for Classes. lowercase names for attributes.

class Resource( object ):
    class_counter= 0
    def __init__(self, name, position, type, active):
        self.name = name
        self.position = position
        self.type = type
        self.active = active
        self.id= Resource.class_counter
        Resource.class_counter += 1
Answered By: S.Lott

Using count from itertools is great for this:

>>> import itertools
>>> counter = itertools.count()
>>> a = next(counter)
>>> print a
0
>>> print next(counter)
1
>>> print next(counter)
2
>>> class A(object):
...   id_generator = itertools.count(100) # first generated is 100
...   def __init__(self):
...     self.id = next(self.id_generator)
>>> objs = [A(), A()]
>>> print objs[0].id, objs[1].id
100 101
>>> print next(counter) # each instance is independent
3

The same interface works if you later need to change how the values are generated, you just change the definition of id_generator.

Answered By: Roger Pate

Are you aware of the id function in python, and could you use it instead of your counter idea?

class C(): pass

x = C()
y = C()
print(id(x), id(y))    #(4400352, 16982704)
Answered By: llimllib

Concise and elegant:

import itertools

class resource_cl():
    newid = itertools.count().next
    def __init__(self):
        self.id = resource_cl.newid()
        ...
Answered By: Algorias

Another note on id() and rethinking other’s answer about it. id() may return a unique number, if and only if it remembers every id ever returned even if an object is deleted; which it (id()) does not do. So therefore…

In support of what others were saying that id() does not return a unique number; It is true that it can not guarentee a unique value if and only if you are storing those id() values as references to objects AND that you are deleting the instances of objects you are getting id()s for. BUT ! using id() as a reference means that you basically have an object that has a key linked somehow with another object.

This is not invalidated by non-uniqueness of id(). It is only invalidated if you do not check if adding a new object has a preexisting id() already stored as a reference to some other instance of the object.

storeit = {}

object1 = object()
print id(object1)
4357891223

storeit[ id(object1) ] = object1

object2 = object()
print id(object2)
9834923411

storeit[ id(object2) ] = object2

storeit[ id(object1) ] = object()
del object1

object3 = object()
print id(object3)
# after some 2 gigawatt tries magically i got
4357891223
# the same id as object1 had

BUT storeit[ 4357891223 ] returns some other object instance not object3; therefore the < link > remains valid but the uniqueness fails.

Answered By: DevPlayer

I like to use generators for ids. Allow the generator to maintain a list of ids already used.

# [email protected]
# 2012-07(jul)-19


class MakeUniqueStr(object):
    '''
    unqstr = MakeUniqueStr(default_name='widget', sep='_')
    print(repr(unqstr('window')))
    print(repr(unqstr('window')))
    print(repr(unqstr('window')))
    print(repr(unqstr('hello')))
    print(repr(unqstr('hello')))
    print(repr(unqstr('window')))
    print(repr(unqstr('hello')))

    'window'
    'window_00000'
    'window_00001'
    'hello'
    'hello_00000'
    'window_00002'
    'hello_00001'
    '''

    def __init__(self, default_name='default', sep='_'):
        self.default_name = default_name
        self.last_base_name = default_name
        self.sep = sep

        self.used_names = []

        self.generator = self.Generator()
        self.generator.next() # initialize

    def __call__(self, name=None):
        if name <> None: self.last_base_name = name
        return self.generator.send(self.last_base_name)

    def _MakeName(self, name, index=1):
        '''_MakeName is called by the Generator. 

        Generator will always have a name and an index to pass to _MakeName. 
        Over ride this method to customize it.'''

        return name + self.sep + '%0.5d' % index

    def Generator(self):
        try_name = yield 'ready' # initialize
        index = 0
        while 1:

            if try_name not in self.used_names:
                self.used_names.append( try_name )
                sent_name = yield try_name
                try_name = sent_name
                continue

            try_name = self._MakeName( sent_name, index )
            while try_name in self.used_names:
                index += 1
                try_name = self._MakeName( sent_name, index )

            index = 0

Although this is not a memory effiecent way for huge in-memory datasets. If you wanted to use something like that then modify this to have the OS handle caching to a file… say via a named pipe.

Answered By: DevPlayer

Ids sometimes benefit from using some fields of the object you wanted to reference. This is a old style database technique.

for example if you have a app that keeps records for incoming customer phone calls, then possible use an id generated by time = some thing else

ident = '%s:%.4s:%.9s' % ( time.time(), time.clock(), agent.name )
# don't forget to time.clock() once to initialize it

only beaware the time.time() and time.clock() are individual computer return value unless on generated on the server. And if on the server make sure your server clock is set right; as always.

Answered By: DevPlayer

Trying the highest voted answer in python 3 you’ll run into an error since .next() has been removed.

Instead you could do the following:

import itertools

class BarFoo:

    id_iter = itertools.count()

    def __init__(self):
        # Either:
        self.id = next(BarFoo.id_iter)

        # Or
        self.id = next(self.id_iter)
        ...
Answered By: foxyblue

You could attach the count to the class as a class parameter, and then on init you’re able to copy this value to an instance parameter.

This makes count a class param, and id an instance param. This works because integers are immutable in python, therefore the current value is copied and not the attribute itself.

class Counter:
    count = 0

    @classmethod
    def incr(self):
        self.count += 1
        return self.count

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

assert [Counter().id for _ in range(3)] == [1, 2, 3]
Answered By: Rob

Hello this may be the Lengthy way, But Worked very Fluently for me. Hope it Helps.
I have done it using a External Text file named id.txt.

Just create an empty file named as above.
Then run this snippet. That will definitely work.

def id_generator():
with open("id.txt", "r") as f:
    f.seek(0)
    fc = f.read(1)
    if not fc:
        with open("id.txt", "w") as f1:
            f1.write("1")
        id = 1
    else:
        f.seek(0)
        fc = f.read(1)
        nv = int(fc) + 1
        with open("id.txt", "w") as f2:
            f2.write(str(nv))
        id = nv

return id

And to get the Value from this Snippet do this.

id = id_generator()

If any of the Reader find it useful, Please pass a Comment and Let me know if my work Paid off.

Hope it helps.
Thank You……

Answered By: Rahul Pandey
def create_next_id(cnt=0):
    def create_next_id_inner():
        nonlocal cnt
        cnt += 1
        return cnt - 1
    return create_next_id_inner
...
next_id = create_next_id()
...
my_data = {'id': next_id(), ...}
my_data2 = {'id': next_id(), ...}
...
Answered By: sergzach
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.