0

I have the following code:

from abc import ABC

class Employee(ABC):
  count = 0
  def __init__(self, name: str):
    self.name = name
    Employee.count += 1

class Engineer(Employee):
  count = 0
  def __init__(self, name: str):
    super().__init__(name)
    Engineer.count +=1
 

class Manager(Employee):
  count = 0
  def __init__(self, name: str):
    super().__init__(name)
    Manager.count += 1

Engineer(name='Alice')
Engineer(name='Bob')
Manager(name='Charlie')

print('number of employee', Employee.count)
print('number of engineer', Engineer.count)
print('number of manager', Manager.count)

This code works but it has code duplication (count logic is duplicated across all classes). Is there a way to avoid this code duplication (if possible a way that does not remove VS Code autocomplete)?

Vince M
  • 890
  • 1
  • 9
  • 21
  • 2
    Hint: "type(self)" returns the class object of "self". – Michael Butscher Aug 20 '22 at 09:52
  • 3
    Should the base class `count` also go up when the derived class counts go up? What is the intended purpose of maintaining these counts? – Karl Knechtel Aug 20 '22 at 09:53
  • @KarlKnechtel Yes it should go up. If I want to know "How many employees do i have", I do Employee.count (3 in this example). The intended purpose of maintaining these count is to know how many have been created (X being a class) – Vince M Aug 20 '22 at 10:00
  • "The intended purpose of maintaining these count is to know how many have been created " What I mean is, **why is it useful** to know this? What **problem will you solve** by knowing this? – Karl Knechtel Aug 20 '22 at 10:01
  • This is an example, giving the context about the real project would be long. But imagine I am developing a simulation. On this simulation, if the number of employee is higher than 100, I have to send a message to a tax service, if the number of engineer is higher than 30, i have to send another message to a tax serve, if the number of Manager is higher than 10, i have to send a message to another tax service. – Vince M Aug 20 '22 at 10:13
  • 2
    It looks correct to me, and also a [duplicate](https://stackoverflow.com/questions/8628123/counting-instances-of-a-class). Probably in a real application you should have a database and do the query on it. – decadenza Aug 20 '22 at 10:18
  • 2
    But why is that the responsibility of the class? More likely you should have something like `if len([e for e in all_employees if isinstance(e, Manager)]) > 10:`. – jonrsharpe Aug 20 '22 at 10:19
  • What if, when an engineer is created, he must check if he is the 30th engineer in a company, and in that that case, he is is responsible for calling the tax_service and should call it inside his constructor ? Is the use of class variable to count entities created so strange that I have to justify the use of it ? It is a very common example of class variable use case (the one beginners use to learn what class variables are), i do not understand why i need to justify the use of class variable & give context on real project... – Vince M Aug 20 '22 at 10:34

1 Answers1

1

UPDATE This would keep counters of any class and subclass, on the class itself. Again I would like to emphasise that holding dynamic data on a class level is probably not a great idea.

from abc import ABC
from inspect import getmro


class Employee(ABC):
    direct_count = 0
    direct_and_indirect_count = 0

    def __init__(self, name: str):
        self.name = name
        for index, cls in enumerate(getmro(type(self))):
            if cls == ABC:
                break
            elif index == 0:
                cls.direct_count += 1
                cls.direct_and_indirect_count += 1
            else:
                cls.direct_and_indirect_count += 1


class Engineer(Employee):
    def __init__(self, name: str):
        super().__init__(name)


class Manager(Employee):
    def __init__(self, name: str):
        super().__init__(name)


class SubManager(Manager):
    def __init__(self, name: str):
        super().__init__(name)


SubManager(name="submanager")
Manager(name="Charlie")
Engineer(name="Alice")
Engineer(name="Bob")

print("number of direct employee", Employee.direct_count)
print("number of direct + indirect employee", Employee.direct_and_indirect_count)

print("number of direct engineer", Engineer.direct_count)
print("number of direct + indirect engineer", Engineer.direct_and_indirect_count)

print("number of direct manager", Manager.direct_count)
print("number of direct + indirect manager", Manager.direct_and_indirect_count)

print("number of direct submanager", SubManager.direct_count)
print("number of direct + indirect submanager", SubManager.direct_and_indirect_count)

ORIGINAL ANSWER

I would recommend not using a class as holder of non-static information, but this would do what you like:

from abc import ABC


class Employee(ABC):
    count = 0
    employee_count = 0

    def __init__(self, name: str):
        self.name = name
        type(self).count += 1
        Employee.employee_count += 1


class Engineer(Employee):
    def __init__(self, name: str):
        super().__init__(name)


class Manager(Employee):
    def __init__(self, name: str):
        super().__init__(name)


Manager(name="Charlie")
Engineer(name="Alice")
Engineer(name="Bob")

print("number of employee", Employee.employee_count)
print("number of engineer", Engineer.count)
print("number of manager", Manager.count)
JarroVGIT
  • 4,291
  • 1
  • 17
  • 29
  • Thank you, do you think it is the only way ? I'm looking for a way to share the count logic across a classe inheritance tree, your answer works (with still a count duplication logic on Employee) for a 2 level inheritance tree but would still result in a code duplication logic on a more complex inheritance tree. For instance: if I create a DevOps and FrontEndDev class that both inherit from Engineer, I would still have to duplicate the code. Imagine a company with an inheritance class tree of depth 6, how would you do to keep count of all entities created (on every node of the class tree) ? – Vince M Aug 20 '22 at 11:31
  • I have added another solution for you, but I would strongly caution to use this kind of logic. It works, but this is not how you should design your software. – JarroVGIT Aug 20 '22 at 21:31