Can an __init__ automatically execute code after calling super? - python

I have something like this:
class SuperClass(object):
def __init__(self):
# initialization stuff
def always_do_this_last(self):
# cleanup stuff
class SubClass(SuperClass):
def __init__(self):
super().__init__()
# intermediate stuff
self.always_do_this_last()
Is it possible to automatically call that last line? Every subclass of SuperClass needs perform the cleanup.

Instead of overriding __init__, define a method that SuperClass.__init__ will call.
class SuperClass(object):
def __init__(self):
# do some stuff
self.child_init()
self.cleanup()
def cleanup():
...
def child_init(self):
pass
class SubClass(SuperClass):
def child_init(self):
...
You can define SuperClass.__init_subclass__ to ensure child_init is overriden, or use the abc module to make SuperClass.child_init an abstract method

One option could be to use a method that the subclasses could override without overriding __init__(). Maybe like this:
class SuperClass:
def __init__(self):
# initialization stuff
self.setup_subclass()
self.always_do_this_last()
def setup_subclass(self):
pass
def always_do_this_last(self):
# cleanup stuff
class SubClass(SuperClass):
def setup_subclass(self):
# intermediate stuff
Would that work for you?

You have 2 options:
Use a different method as your initializer and call always_do_this_last afterwards
class SuperClass(object):
def __init__(self):
self._init() # initialize
self.always_do_this_last() # clean up
def _init(self):
pass # initialization stuff
def always_do_this_last(self):
pass # cleanup stuff
class SubClass(SuperClass):
def _init(self):
super()._init()
# intermediate stuff
Use a metaclass
class CleanupMeta(type):
def __call__(cls, *args, **kwargs):
obj = super().__call__(*args, **kwargs)
obj.always_do_this_last()
return obj
class SuperClass(metaclass=CleanupMeta):
def __init__(self):
pass # initialization stuff
def always_do_this_last(self):
pass # cleanup stuff
class SubClass(SuperClass):
def __init__(self):
super().__init__()
# intermediate stuff

The other answers here are more than sufficient. I will add that you might want to have a look at the abstract base class if you are implementing a class that requires certain member functions to be implemented.
In the example below the parent requires the initialize and cleanup methods to be defined in each child (try removing one of them to verify an error is raised).
import abc
class SuperClass(object):
__metaclass__ = abc.ABCMeta
def __init__(self):
print("Instantiating Class")
self.initialize()
self.cleanup()
#abc.abstractmethod
def initialize(self):
pass
#abc.abstractmethod
def cleanup(self):
pass
class SubClass(SuperClass):
def __init__(self):
super(SubClass, self).__init__()
def initialize(self):
print("initializing...")
def cleanup(self):
print("... cleanup.")
a = SubClass()

Related

Decorating a Python class with a decorator as a class

Need some help to implement/understand how decorators as a class work in Python. Most examples I've found are either decorating a class, but implementend as a function, or implemented as a class, but decorating a function. My goal is to create decorators implemented as classes and decorate classes.
To be more specific, I want to create a #Logger decorator and use it in some of my classes. What this decorator would do is simply inject a self.logger attribute in the class, so everytime I decorate a class with #Logger I'll be able to self.logger.debug() in its methods.
Some initial questions:
What does the decorator's __init__ receive as parameters? I it would receive only the decorated class and some eventual decorator parameters, and that's actually what happens for most of the cases, but please take a look at the output below for the DOMElementFeatureExtractor. Why does it received all those parameters?
What about the __call__ method? What will it receive?
How can I provide a parameter for the decorator (#Logger(x='y'))? Will it be passed to the __init__ method?
Should I really be returning an instance of the class in the __call__ method? (only way I could make it work)
What about chaining decorators? How would that work if the previous decorator already returned an instance of the class? What should I fix in the example below in order to be able to #Logger #Counter MyClass:?
Please take a look at this example code. I've created some dummy examples, but in the end you can see some code from my real project.
You can find the output at the end.
Any help to understand Python classes decorators implemented as a class would be much appreciated.
Thank you
from abc import ABC, abstractmethod
class ConsoleLogger:
def __init__(self):
pass
def info(self, message):
print(f'INFO {message}')
def warning(self, message):
print(f'WARNING {message}')
def error(self, message):
print(f'ERROR {message}')
def debug(self, message):
print(f'DEBUG {message}')
class Logger(object):
""" Logger decorator, adds a 'logger' attribute to the class """
def __init__(self, cls, *args, **kwargs):
print(cls, *args, **kwargs)
self.cls = cls
def __call__(self, *args, **kwargs):
print(self.cls.__name__)
logger = ConsoleLogger()
setattr(self.cls, 'logger', logger)
return self.cls(*args, **kwargs)
class Counter(object):
""" Counter decorator, counts how many times a class has been instantiated """
count = 0
def __init__(self, cls, *args, **kwargs):
self.cls = cls
def __call__(self, *args, **kwargs):
count += 1
print(f'Class {self.cls} has been initialized {count} times')
return self.cls(*args, **kwargs)
#Logger
class A:
""" Simple class, no inheritance, no arguments in the constructor """
def __init__(self):
self.logger.info('Class A __init__()')
class B:
""" Parent class for B1 """
def __init__(self):
pass
#Logger
class B1(B):
""" Child class, still no arguments in the constructor """
def __init__(self):
super().__init__()
self.logger.info('Class B1 __init__()')
class C(ABC):
""" Abstract class """
def __init__(self):
super().__init__()
#abstractmethod
def do_something(self):
pass
#Logger
class C1(C):
""" Concrete class, implements C """
def __init__(self):
self.logger.info('Class C1 __init__()')
def do_something(self):
self.logger.info('something')
#Logger
class D:
""" Class receives parameter on intantiation """
def __init__(self, color):
self.color = color
self.logger.info('Class D __init__()')
self.logger.debug(f'color = {color}')
class AbstractGenerator(ABC):
def __init__(self):
super().__init__()
self.items = None
self.next_item = None
#abstractmethod
def __iter__(self):
pass
def __next__(self):
pass
def __len__(self):
pass
def __getitem__(self, key):
pass
class AbstractDOMElementExtractor(AbstractGenerator):
def __init__(self, parameters, content):
super().__init__()
self.parameters = parameters
self.content = content
#Logger
class DOMElementExtractor(AbstractDOMElementExtractor):
def __init__(self, parameters, content):
super().__init__(parameters, content)
def __iter__(self):
self.logger.debug('__iter__')
def __next__(self):
self.logger.debug('__next__')
def __len__(self):
self.logger.debug('__len__')
def __getitem__(self, key):
self.logger.debug('__getitem__')
class DOMElementFeatureExtractor(DOMElementExtractor):
def __init__(self, parameters, content):
super().__init__(parameters, content)
class DocumentProcessor:
def __init__(self):
self.dom_element_extractor = DOMElementExtractor(parameters={}, content='')
def process(self):
self.dom_element_extractor.__iter__()
a = A()
b1 = B1()
c1 = C1()
c1.do_something()
d = D(color='Blue')
document_processor = DocumentProcessor()
document_processor.process()
Output:
<class '__main__.A'>
<class '__main__.B1'>
<class '__main__.C1'>
<class '__main__.D'>
<class '__main__.DOMElementExtractor'>
DOMElementFeatureExtractor (<__main__.Logger object at 0x7fae27c26400>,) {'__module__': '__main__', '__qualname__': 'DOMElementFeatureExtractor', '__init__': <function DOMElementFeatureExtractor.__init__ at 0x7fae27c25840>, '__classcell__': <cell at 0x7fae27cf09d8: empty>}
A
INFO Class A __init__()
B1
INFO Class B1 __init__()
C1
INFO Class C1 __init__()
INFO something
D
INFO Class D __init__()
DEBUG color = Blue
DOMElementExtractor
DEBUG __iter__
Won't be a full answer, but I think it's helpful to review the basics of a decorator. This is what decorating looks like:
#Logger
class A:
# A's code
By definition, it's equivalent to doing this:
class A
# A's code
A = Logger(A) # Logger has to be callable because...it's called
Sources often say that decorators "modify", but that's really just the intended use. Technically, all you need is A to have a definition (so a function, method, or class) and Logger to be callable. If Logger returned "Hello, World", that's what A becomes.
Okay, let's pretend we didn't decorate A for a bit and think about what it would take for Logger(A) to be "modifying." Well, A is a class, and you call a class to create instances: A(*args). Therefore, Logger(A)(*args) must also be instances of A. But Logger(A) isn't the class A, it's an instance of Logger. Luckily, you can make instances callable by defining the __call__ method in its class. Logger's __call__ method calls the class stored in its cls attribute and returns the instance.
As for parameters in a decorator, it also helps to think about what it's equivalent to. You're interested in doing this:
#Logger(x='y')
class A:
# A code
So it's equivalent to this:
class A:
# A code
A = Logger(x = 'y')(A)
Note that Logger itself is not taking A as an argument. It's taking 'y' as an argument and returning another callable that takes A as an argument. So if Logger is a class, Logger(x = 'y') would be a Logger instance. Instances of a class can also serve as decorators if the class has a __call__ method!

Python decorator in class

I want to make decorator with class variables.
How can I make decorator to class function?
Because I have a lot of functions that need to lock.
I don't want to write down
with self.lock: or self.lock.release()
every function.
this is my code!
class Something:
self.lock = Lock( .. )
#decorator
def _lock(self, func):
def wrapper(*args, **kwargs):
self.lock.acquire()
func(*args, **kwargs)
self.lock.release()
return wrapper
#_lock
def some_func(self,):
#do something
Strongly recommend that if you want a way of using a decorator to make thread locking easier, go use the synchronised decorator from the wrapt package.
For details on the design and rational for it, read the following. There is way too much detail in posts to repeat here.
http://blog.dscpl.com.au/2014/01/the-missing-synchronized-decorator.html
http://blog.dscpl.com.au/2014/01/the-synchronized-decorator-as-context.html
http://wrapt.readthedocs.io/en/latest/examples.html
What it allows though is:
#synchronized # lock bound to function1
def function1():
pass
#synchronized # lock bound to function2
def function2():
pass
#synchronized # lock bound to Class
class Class(object):
#synchronized # lock bound to instance of Class
def function_im(self):
pass
#synchronized # lock bound to Class
#classmethod
def function_cm(cls):
pass
#synchronized # lock bound to function_sm
#staticmethod
def function_sm():
pass
For more fine grained locking within methods of a class, you can also use it like a context manager:
class Class(object):
#synchronized
def function_im_1(self):
pass
def function_im_2(self):
with synchronized(self):
pass
and:
class Class(object):
#synchronized
#classmethod
def function_cm(cls):
pass
def function_im(self):
with synchronized(Class):
pass
You would have to do it like this. It would only work for instance methods, not functions.
class Something:
self.lock = Lock( .. )
#decorator
def _lock(func):
def wrapper(self, *args, **kwargs):
self.lock.acquire()
r = func(self, *args, **kwargs)
self.lock.release()
return r
return wrapper
#_lock
def some_func(self):
#do something
The meaning of class function is not very clearly to me. So I shows two different locks, one for class methods, another one for instance methods, in the following code.
And we need to use try...finally around the calling of func inside the decorator
def synchronized(func):
""" Assumes that the first parameter of func has `_lock` property
"""
def wrapper(owner, *args, **kwargs):
owner._lock.acquire()
try:
return func(owner, *args, **kwargs)
finally:
owner._lock.release()
return wrapper
class Something(object):
_lock = Lock() # for class methods
def __init__(self):
self._lock = Lock() # for instance methods
#synchronized
def instance_method(self):
print 'instance method invoked...'
#classmethod
#synchronized
def class_method(cls):
print 'class method invoked...'

Multiple Mixins and properties

I am trying to create a mixin class that has it's own properties, but as the class has no init to initialize the "hidden" variable behind the property.
class Software:
__metaclass__ = ABCMeta
#property
def volumes(self):
return self._volumes
#volumes.setter
def volumes(self, value):
pass
class Base(object):
def __init__(self):
self._volumes = None
class SoftwareUser(Base, Software):
def __init__(self):
super(Base, self).__init__()
So above is the best that I have come up with to solve this but the reality is that the _volumes dosn't really belong in the base. I could add an init to the Software class but then the super call wont work on both mixins.
The second is that I will need multiple mixins dependent on the incoming call they will always need the base, but the mixins will change so I dont really want variables from mixins that aren't mixed in for that call.
Is there a way that i can have the mixin add it's variables to the class if it is mixed in perhaps dynamically call the init of the mixin class ?.
Any questions let me know.
Thanks
Yes, that's wildly overcomplicated. A class (including mixins) should only be responsible for calling the next implementation in the MRO, not marshalling all of them. Try:
class Software:
#property
def volumes(self):
return self._volumes
#volumes.setter
def volumes(self, value):
pass
def __init__(self):
self._volumes = None
super().__init__() # mixin calls super too
class Base(object):
def __init__(self):
other_vars = None
class SoftwareUser(Software, Base): # note order
def __init__(self):
super().__init__() # all you need here
Ok so here is what I came up with, I am open to other answers, if I have made this way over complicated.
class Software:
#property
def volumes(self):
return self._volumes
#volumes.setter
def volumes(self, value):
pass
def __init__(self):
self._volumes = None
class Base(object):
def __init__(self):
other_vars = None
class SoftwareUser(Base, Software):
def _bases_init(self, *args, **kwargs):
for base in type(self).__bases__:
base.__init__(self, *args, **kwargs)
def __init__(self, *args, **kwargs):
self._bases_init(*args, **kwargs)

Calling a parent's parent's method, which has been overridden by the parent

How do you call a method more than one class up the inheritance chain if it's been overridden by another class along the way?
class Grandfather(object):
def __init__(self):
pass
def do_thing(self):
# stuff
class Father(Grandfather):
def __init__(self):
super(Father, self).__init__()
def do_thing(self):
# stuff different than Grandfather stuff
class Son(Father):
def __init__(self):
super(Son, self).__init__()
def do_thing(self):
# how to be like Grandfather?
If you always want Grandfather#do_thing, regardless of whether Grandfather is Father's immediate superclass then you can explicitly invoke Grandfather#do_thing on the Son self object:
class Son(Father):
# ... snip ...
def do_thing(self):
Grandfather.do_thing(self)
On the other hand, if you want to invoke the do_thing method of Father's superclass, regardless of whether it is Grandfather you should use super (as in Thierry's answer):
class Son(Father):
# ... snip ...
def do_thing(self):
super(Father, self).do_thing()
You can do this using:
class Son(Father):
def __init__(self):
super(Son, self).__init__()
def do_thing(self):
super(Father, self).do_thing()

How to change method defaults in Python?

I'm building a class, Child, that inherits from another class, Parent. The Parent class has a loadPage method that the Child will use, except that the Child will need to run its own code near the end of the loadPage function but before the final statements of the function. I need to somehow insert this function into loadPage only for instances of Child, and not Parent. I was thinking of putting a customFunc parameter into loadPage and have it default to None for Parent, but have it default to someFunction for Child.
How do I change the defaults for the loadPage method only for instances of Child? Or am I going about this wrong? I feel like I may be overlooking a better solution.
class Parent():
def __init__(self):
# statement...
# statement...
def loadPage(self, pageTitle, customFunc=None):
# statement...
# statement...
# statement...
if customFunc:
customFunc()
# statement...
# statement...
class Child(Parent):
def __init__(self):
Parent.__init__(self)
self.loadPage.func_defaults = (self.someFunction) #<-- This doesn't work
For such things, I do it in a different way :
class Parent():
def loadPage(self, pageTitle):
# do stuff
self.customFunc()
# do other stuff
def customFunc(self):
pass
class Child(Parent):
def customFunc(self):
# do the child stuff
then, a Child instance would do the stuff in customFunc while the Parent instance would do the "standard" stuff.
Modifying your method as little as possible:
class Parent(object):
def __init__(self):
pass
def loadPage(self, pageTitle, customFunc=None):
print 'pageTitle', pageTitle
if customFunc:
customFunc()
class Child(Parent):
def __init__(self):
Parent.__init__(self)
def loadPage(self, pagetitle, customFunc = None):
customFunc = self.someFunction if customFunc is None else customFunc
super(Child, self).loadPage(pagetitle, customFunc)
def someFunction(self):
print 'someFunction'
p = Parent()
p.loadPage('parent')
c = Child()
c.loadPage('child')
I wouldn't try to do this with defaults. Straightforward class inheritance already provides what you need.
class Parent():
def __init__(self):
# statement...
# statement...
def loadPage(self, pageTitle):
# ... #
self.custom_method()
# ... #
def custom_method(self):
pass # or something suitably abstract
class Child(Parent):
def __init__(self):
Parent.__init__(self)
def custom_method(self):
# what the child should do do
Can the statements before the customFunc() call be exported to a function? and the same for the statements after this call.
If yes, then the parent class will just call these two functions, and the child class will have the customFunc() call between them.
So only the calls will be duplicated.
I may be overlooking a better solution.
Well, the best is probably to rely on an internal attribute, so you would have something like this:
class Parent(object):
def __init__(self):
self._custom_func = None
def load_page(self, page_title):
if self._custom_func:
self._custom_func()
class Child(Parent):
def __init__(self):
super(Parent, self).__init__()
self._load_page = some_function

Categories