Implement child class level variable in abstract base class [duplicate] - python-3.x

This question already has answers here:
Counting instances of a class?
(7 answers)
How to count the number of instance of a custom class?
(1 answer)
Closed 4 years ago.
I have made an abstract base class using metaclass=ABCMeta to implement some default methods and properties that all its derived classes should have, something like this:
class BaseClass(metaclass=ABCMeta):
def __init__(self):
self.params = OrderedDict()
# some function all children must implement
#abstractmethod
def fn():
pass
# some property all children will have
#property
def n_params(self):
return len(self.params)
Now, I want to count how many instances of each derived class are created, because I want to print their name with a number. I can easily implement it like this:
class ChildClass(BaseClass):
# nr of instances of ChildClass
_n = 0
def __init__(self, mu, sigma, amp):
# initialize ABC
super().__init__()
ChildClass._n += 1
self.name = f'ChildClass {ChildClass._n}'
# must be overridden by each child class
#staticmethod
def fn(name):
# do stuff
print(f'hello {name}')
However, I need to implement this in each class that derives from BaseClass separately. It would be much more elegant to implement it in BaseClass itself.
I am looking for something that works like the opposite of super(), maybe something like this:
class BaseClass(metaclass=ABCMeta):
# instance counter for derived classes
child()._n = 0
def __init__(self):
self.params = OrderedDict()
child()._n += 1
Can I implement a class level variable ChildClass._n in BaseClass? And if so, how can I access that variable in BaseClass.__init__?
Edit:
To clarify: I already have a base class, from which I derive many other child classes. The fact that all these classes share many properties is one of the main reasons I decided to use a base class in the first place. It thus seems superfluous to need to implement something as simple as an instance counter separately in each derived class.
Accessing a child class variable seems to be already adressed here. The question that remains is, how do I define a class level variable in my abstract base class, that is different for each child class.

Related

How __init__ works for inheritance

I cant have 2 init methods in one class because of function overloading. However, why is it possible that when initializing a subclass, im able to define a new __init__ method, and use the super().__init__ method or the parentclass init method within the subclass __init__ method. i'm just a little confused by the concept of 2 __init__ methods functioning at the same time
class Employee:
emps = 0
def __init__(self,name,age,pay):
self.name = name
self.age = age
self.pay = pay
class Developer(Employee):
def __init__(self,name,age,pay,level):
Employee.__init__(self,name,age,pay)
self.level = level
I cant have 2 init methods in one class because of function overloading.
Partially true. You can't have 2 __init__ methods in the same class because the language lacks function overloading. (Libraries can partially restore a limited form of function overloading; see functools.singledispatchmethod for an example.)
i'm just a little confused by the concept of 2 init methods functioning at the same time
But you aren't trying to overload __init__. You are overriding __init__, providing a different definition for Developer than the definition it inherits from Employer. (In fact, Employer is overriding __init__ as well, using its own definition in place of the one it inherits from object.) Each class has only one definition.
In your definition of Developer.__init__, you are simply making an explicit call to the inherited method to do the initialization common to all instances of Employee, before doing the Developer-specific initialization on the same object.
Using super, you are using a form of dynamic lookup to let the method resolution order for instance of Developer decide what the "next" version of __init__ available to Developer to call. For single inheritance, the benefit is little more than avoiding the need to hard-code a reference to Employee. But for multiple inheritance, super is crucial to ensuring that all inherited methods (both the ones you know about and the ones you may not) get called, and more importantly, are called in the right order.
A full discussion of how to properly use super is beyond the scope of this question, I think, but I'll show your two classes rewritten to make the best use of super, and refer you to Python's super() considered super! for more information.
# Main rules:
# 1. *All* classes use super().__init__, even if you are only inheriting
# from object, because you don't know who will use you as a base class.
# 2. __init__ should use keyword arguments, and be prepared to accept any
# keyword arguments.
# 3. All keyword arguments that don't get assigned to your own parameters
# are passed on to an inherited __init__() to process.
class Employee:
emps = 0
def __init__(self, *, name, age, pay, **kwargs):
super().__init__(**kwargs)
self.name = name
self.age = age
self.pay = pay
class Developer(Employee):
def __init__(self, *, level, **kwargs):
super().__init__(**kwargs)
self.level = level
d1 = Developer(name="Alice", age=30, pay=85000, level=1)
To whet your appetite for the linked article, consider
class A:
def __init__(self, *, x, **kwargs):
super().__init__(**kwargs)
self.x = x
class B:
def __init__(self, *, y, **kwargs):
super().__init__(**kwargs)
self.y = y
class C1(A, B):
pass
class C2(B, A):
pass
c1 = C1(x=1, y=2)
c2 = C2(x=4, y=3)
assert c1.x == 1 and c1.y == 2
assert c2.x == 4 and c2.y == 3
The assertions all pass, and both A.__init__ and B.__init__ are called as intended when c1 and c2 are created.
The super() function is used to give access to methods and properties of a parent or sibling class
check out: https://www.geeksforgeeks.org/python-super/

Python pro way to make an abstract class allowing each child class to define its own attributes, Python3

I have to model several cases, each case is realised by a class. I want to make sure that each class must have 2 methods get_input() and run(). So in my opinion, I can write a CaseBase class where these 2 methods are decorated as #abstractmethod. Therefore, any child class has to implement these 2 methods. And this is exactly my goal.
However, due to the nature of my work, each case is for distinct subject, and it is not easy to define a fixed group of attributes. The attributes should be defined in the __init__ method of a class. That means I don't know what exactly attributes to write in the CaseBase class. All I know is that all children cases must have some common attributes, like self._common_1 and self._common_2.
Therefore, my idea is that I also decorate the __init__ method of CaseBase class by #abstractmethod. See my code below.
from abc import ABC, abstractmethod
from typing import Dict, List
class CaseBase(ABC):
#abstractmethod
def __init__(self):
self._common_1: Dict[str, float] = {}
self._common_2: List[float] = []
...
#abstractmethod
def get_input(self, input_data: dict):
...
#abstractmethod
def run(self):
...
class CaseA(CaseBase):
def __init__(self):
self._common_1: Dict[str, float] = {}
self._common_2: List[float] = []
self._a1: int = 0
self._a2: str = ''
def get_input(self, input_data: dict):
self._common_1 = input_data['common_1']
self._common_2 = input_data['common_2']
self._a1 = input_data['a1']
self._a2 = input_data['a2']
def run(self):
print(self._common_1)
print(self._common_2)
print(self._a1)
print(self._a2)
def main():
case_a = CaseA()
case_a.get_input(input_data={'common_1': {'c1': 1.1}, 'common_2': [1.1, 2.2], 'a1': 2, 'a2': 'good'})
case_a.run()
if __name__ == '__main__':
main()
My question: Is my way a good Python style?
I followed many Python tutorials about how to make Abstract class and child class. They all give examples where a fixed group of attributes are defined in the __init__ method of the base class. I also see some approach to use super().__init__ code in the child class to change the attributes defined in the base class or to add new attributes. But I am not sure if it is better (more pro) than my way.
Thanks.
You mostly used the abc module in python 3.10 correctly. but it doesn't make sense to decorate the constructor with #abstractmethod. It's unnecessary. Each class, derived or not, can and will have its own constructor. You can call super().__init__(args) within the child class to call the constructor of its immediate parent if you didn't want to duplicate its code but wanted to do further initialization in the child class constructor.

Python: why do I need super().__init__() call in metaclasses?

I have got one question: why do I need to call super().--init--() in metaclasses? Because metaclass is factory of classes, I think we don`t need to call initialization for making objects of class Shop. Or with using super().--init-- we initializing the class? (Because my IDE says, that I should call it. But without super().--init-- nothing happens, my class working without mistakes).
Can you explane me, why?
Thanks in advance!
class Descriptor:
_counter = 0
def __init__(self):
self.attr_name = f'Descriptor attr#{Descriptor._counter}'
Descriptor._counter += 1
def __get__(self, instance, owner):
return self if instance is None else instance.__dict__[self.attr_name]
def __set__(self, instance, value):
if value > 0:
instance.__dict__[self.attr_name] = value
else:
msg = 'Value must be > 0!'
raise AttributeError(msg)
class Shop():
weight = Descriptor()
price = Descriptor()
def __init__(self, name, price, weight):
self.name = name
self.price = price
self.weight = weight
def __repr__(self):
return f'{self.name}: price - {self.price} weight - {self.weight}'
def buy(self):
return self.price * self.weight
class Meta(type):
def __init__(cls, name, bases, attr_dict):
super().__init__(name, bases, attr_dict) # <- this is that func. call
for key, value in attr_dict.items():
if isinstance(value, Descriptor): # Here I rename attributes name of descriptor`s object.
value.attr_name = key
#classmethod
def __prepare__(metacls, name, bases):
return OrderedDict()
You don't "need" to - and if your code use no other custom metaclasses, not calling the metaclass'__init__.super() will work just the same.
But if one needs to combine your metaclass with another, through inheritance, without the super() call, it won't work "out of the box": the super() call is the way to ensure all methods in the inheritance chain are called.
And if at first it looks like that a metaclass is extremely rare, and combining metaclasses would likely never take place: a few libraries or frameworks have their own metaclasses, including Python's "abc"s (abstract base classes), PyQT, ORM frameworks, and so on. If any metaclass under your control is well behaved with proper super() calls on the __new__, __init__ and __call__ methods, (if you override those), what you need to do to combine both superclasses and have a working metaclass can be done in a single line:
CompatibleMeta = type("CompatibleMeta", (meta, type(OtherClassBase)), {})
This way, for example, if you want to use the mechanisms in your metaclass in a class using the ABCMeta functionalities in Python, you just do it. The __init__ method in your Meta will call the other metaclass __init__. Otherwise it would not run, and some subtle unexpectd thing would not be initialized in your classes, and this could be a very hard to find bug.
On a side note: there is no need to declare __prepare__ in a metaclass if all it does is creating an OrderedDict on a Python newer than 3.6: Since that version, dicitionaries used as the "locals()" while executing class bodies are ordered by default. Also, if another metaclass you are combining with also have a __prepare__, there is no way to make that work automatically by using "super()" - you have to check the code and verify which of the two __prepare__s should be used, or create a new mapping type with features to attend both metaclasses.

Check whether an instance of a class exists at runtime

I need to know how to check if an instance of a given class exists at runtime. So I need to make interactions between this instance and other classes. There is no relationship between these classes (composition, aggregation, and inheritance). So how do you get the reference for this class if it exists?
If you just want to know if (or how many times) a class has been instantiated, just keep a counter in the class:
class Foo:
counter = 0
def __init__(self):
Foo.counter += 1
Foo()
print(Foo.counter)
Alternatively, if you want to keep a handle on all the instances (by reference), instead store self:
class Foo:
classes = []
def __init__(self):
Foo.classes.append(self)
Foo()
print(Foo.classes[0])

Can python metaclasses inherit?

classes can inherit..
class Base:
def __init__(self,name):
self.name = name
class Derived1(Base):
def __init__(self,name):
super().__init__(name)
class Derived2(Base):
def __init__(self,name):
super().__init__(name)
Can a similar thing done for meta classes also?
I have a requirement where some of my classes will have to be both abstract base classes and also my own meta classes (say singleton types..)
Is it possible to do
class Singleton(type):
'''
implementation goes here..
'''
class AbstractSingleton(Singleton,ABCMeta):
'''
What code should go here??
'''
If its possible how to implement the AbstractSingleton class?
Yes, it is possible.
But first things first:
You should not be using metaclasses for creating singletons in Python.
Singletons are a simple concept, and just a custom __new__ method is enough - no need for a metaclass for that.
This simple 4 line normal class code can be used as a mixin, and will turn any derived classes into "singleton" classes - afer the first instance is created, no further instances are created, and the first instance is always returned:
class SingletonBase:
def __new__(cls, *args, **kw):
if not "instance" in cls.__dict__:
cls.instance = super().__new__(cls, *args, **kw)
return cls.instance
Now, if you'd have a real case for another metaclass and needed to combine that with ABCMeta or other metaclass, all you'd have to do is to create a third metaclass that inherits from both metaclasses - if both of them use super in a well behaved way, it would just work.
class SingletonMeta(type):
def __call__(cls, *args, **kw):
# You know - you _really_ should not be using metaclasses for singletons.
if not "instance" in cls.__dict__:
cls.instance = super().__call__(*args, **kw)
return cls.instance
class SingletonAbstractMeta(SingletonMeta, abc.ABCMeta):
pass
class SingleAbstractBase(metaclass=SingleAbstractMeta):
...
For sheer coincidence, earlier this week I used exactly this use case as an example of what can be achieved with a "meta meta class" in Python. By having a special "meta meta class" to the metaclass one wants to combine to another (I even use ABCMeta on the example), it can create the derived combined metaclass just by using the operator " + ", like in
class SingletonMeta(type, metaclass=MM):
...
class AbstractSingletonBase(metaclass=SingletonMeta + abc.ABCMeta):
# code here.
Check the answer here.

Resources