Counting instances of a class?
Asked Answered
W

9

43

I've been cleaning up some code from a module I'm extending and I can't seem to find a way to Pythonify this code:

global_next_id = 1

class Obj:
  def __init__(self):
    global global_next_id
    self.id = global_next_id

    global_next_id += 1

This code uses a global id to keep track of instances of a class (I need the variable self.id internally as well, and it needs to be a number).

Can anyone suggest a way to Pythonify this code?

Winglet answered 25/12, 2011 at 3:55 Comment(2)
Why do you need to "keep track of instances of a class" with a global id?Barramunda
It's for a PDF library I am modifying. Each PDF Obj needs to be printed out with its respective ID.Winglet
T
83

You could consider using a class attribute to provide a counter. Each instance needs only to ask for the next value. They each get something unique. Eg:

from itertools import count

class Obj(object):
  _ids = count(0)

  def __init__(self):
    self.id = next(self._ids)
Trihedral answered 25/12, 2011 at 3:59 Comment(8)
I completely overlooked class-wide variables... Thanks for the itertools solution!Winglet
I wish I could claim sole credit - I saw it somewhere here on another post. It's elegant and efficient.Trihedral
What happens when an instance get deleted, does the count went down?Purdum
@NegativeZero - No, there's nothing in this that keeps track of the instances after they're deleted. It simply numerically increases the counter for each new instance as it is created. You'd have to implement some sort of registration mechanism for instances, then deal with reused ids in some way to avoid collisions. That sounds like overkill when the goal is just to number instances as they're created.Trihedral
@Trihedral - I see. I thought the goal is to count the number of instances that are currently associated with a class. Yeah, if the goal is to count the instances that are ever created, then the solution given is fine.Purdum
In Python 3.6 at least, self._ids.next() does not work (object has no attribute next). Instead, use next(self._ids). This also works in Python 2.7.Blatman
@yucer - According to https://mcmap.net/q/391272/-is-itertools-thread-safe, count is atomically thread safe, yes. The same can't be said for all of itertools, but count is implemented in a way that should be.Trihedral
Rather than assume count is thread-safe, you can always just wrap access to next(self._ids) with an explicit lock.Literality
H
31

This should do the job:

class Obj:
    _counter = 0
    def __init__(self):
        Obj._counter += 1
        self.id = Obj._counter
Hymeneal answered 2/12, 2017 at 17:28 Comment(2)
Most pythonic answer.Leukemia
I was wondering how it would handle the initialization to zero of _counter. It looks like it only initializes it to zero for for the very first instance of this class created, and NOT for the next instances created. This is like a static variable in C or C++, including static class variables in C++.Garthgartner
P
8

Here is a way to count instances without descendant classes sharing the same id/count. A metaclass is used to create a separate id counter for each class.

Uses Python 3 syntax for Metaclasses.

import itertools

class InstanceCounterMeta(type):
    """ Metaclass to make instance counter not share count with descendants
    """
    def __init__(cls, name, bases, attrs):
        super().__init__(name, bases, attrs)
        cls._ids = itertools.count(1)

class InstanceCounter(object, metaclass=InstanceCounterMeta):
    """ Mixin to add automatic ID generation
    """
    def __init__(self):
        self.id = next(self.__class__._ids)
Pannier answered 3/5, 2015 at 21:2 Comment(1)
Note that in the InstanceCounter class, the incrementing can simplified to self.id = next(self._ids).Redemption
S
6

I found the following solution:

class Obj:
    counter = 0

    def __init__(self):
        type(self).counter += 1

    def __del__(self):
        type(self).counter -= 1

It's better to use type(self).counter instead of Obj.counter

Sarisarid answered 27/7, 2018 at 15:7 Comment(2)
Why is it better to use type(self).counter instead of Obj.counter ?Junejuneau
@W Stokvis Below is written about __del__(), "The __del__() method is a known as a destructor method in Python. It is called when all references to the object have been deleted i.e when an object is garbage collected." So, refering through variable "self" or type(self) is risky. Obj.counter is safer option.Laguna
P
1

Generator?

def get_next_id():
    curr_id = 1
    while True:
        yield curr_id
        curr_id += 1
Phlegm answered 25/12, 2011 at 3:59 Comment(0)
N
1

You can use dir() function, which returns all properties and functions in the current script, to count the numbers of instances of a certain class.

len([i for i in dir() if isinstance(eval(i), ClassName)])
Nipper answered 16/5, 2022 at 3:50 Comment(1)
Thanks. Benjamin Wang. It worked fantastically, and a one-liner too. Excellent! I'm so glad I scrolled down because as someone learning about classes, I didn't really understand the other verbose answers - as amazing and helpful as they obviously are.Bogosian
K
0
class InstanceCounter(object):
  # the instance counter
  counter = 0

  def __init__(self, val):
    self.val = all
    # incrementing every time an instance is created
    InstanceCounter.counter += 1

  def set_val(self, val):
    self.val = val

  def get_val(self, val):
    return self.val

  # accessing the instance counter should be done through a class method

  @classmethod
  def get_counter(cls):  
    return cls.counter

# See the instance counter as it increments as new instances are created
a=InstanceCounter(5)
print(a.get_counter())
b=InstanceCounter(7)
print(a.get_counter(), b.get_counter())
c=InstanceCounter(9)
print(a.get_counter(), b.get_counter(), c.get_counter())
Krak answered 3/5, 2020 at 17:1 Comment(0)
T
-1

This Will do the job

class YourClass:
    counter = 0
    def __init__(self):
        YourClass.counter += 1 
    def PrintData(self):
        print(self.counter)
Tann answered 10/11, 2023 at 12:29 Comment(0)
E
-1

This could work as well if you want to count the number of instances in each class of specific variables. You can import pandas and read the data into the object.

object.variableName.value_counts()

This code will output the count of instances of each class within the specified variableName

Engen answered 16/4 at 17:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.