This is a feature I miss in several languages and wonder if anyone has any idea how it can be done in Python.
The idea is that I have a base class:
class Base(object):
def __init__(self):
self.my_data = 0
def my_rebind_function(self):
pass
and a derived class:
class Child(Base):
def __init__(self):
super().__init__(self)
# Do some stuff here
self.my_rebind_function() # <==== This is the line I want to get rid of
def my_rebind_function(self):
# Do stuff with self.my_data
As can be seen above, I have a rebound function which I want called after the Child.__init__ has done its job. And I want this done for all inherited classes, so it would be great if it was performed by the base class, so I do not have to retype that line in every child class.
It would be nice if the language had a function like __finally__, operating similar to how it operates with exceptions. That is, it should run after all __init__-functions (of all derived classes) have been run, that would be great. So the call order would be something like:
Base1.__init__()
...
BaseN.__init__()
LeafChild.__init__()
LeafChild.__finally__()
BaseN.__finally__()
...
Base1.__finally__()
And then object construction is finished. This is also kind of similar to unit testing with setup, run and teardown functions.
You can do this with a metaclass like that:
class Meta(type):
def __call__(cls, *args, **kwargs):
print("start Meta.__call__")
instance = super().__call__(*args, **kwargs)
instance.my_rebind_function()
print("end Meta.__call__\n")
return instance
class Base(metaclass=Meta):
def __init__(self):
print("Base.__init__()")
self.my_data = 0
def my_rebind_function(self):
pass
class Child(Base):
def __init__(self):
super().__init__()
print("Child.__init__()")
def my_rebind_function(self):
print("Child.my_rebind_function")
# Do stuff with self.my_data
self.my_data = 999
if __name__ == '__main__':
c = Child()
print(c.my_data)
By overwriting Metaclass.__call__ you can hook after all __init__ ( and __new__) methods of the class-tree have run an before the instance is returned. This is the place to call your rebind function. To understand the call order i added some print statements. The output will look like this:
start Meta.__call__
Base.__init__()
Child.__init__()
Child.my_rebind_function
end Meta.__call__
999
If you want to read on and get deeper into details I can recommend following great article: https://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/
I may still not fully understand, but this seems to do what I (think) you want:
class Base(object):
def __init__(self):
print("Base.__init__() called")
self.my_data = 0
self.other_stuff()
self.my_rebind_function()
def other_stuff(self):
""" empty """
def my_rebind_function(self):
""" empty """
class Child(Base):
def __init__(self):
super(Child, self).__init__()
def other_stuff(self):
print("In Child.other_stuff() doing other stuff I want done in Child class")
def my_rebind_function(self):
print("In Child.my_rebind_function() doing stuff with self.my_data")
child = Child()
Output:
Base.__init__() called
In Child.other_stuff() doing other stuff I want done in Child class
In Child.my_rebind_function() doing stuff with self.my_data
If you want a "rebind" function to be invoked after each instance of a type which inherits from Base is instantiated, then I would say this "rebind" function can live outside the Base class(or any class inheriting from it).
You can have a factory function that gives you the object you need when you invoke it(for example give_me_a_processed_child_object()). This factory function basically instantiates an object and does something to it before it returns it to you.
Putting logic in __init__ is not a good idea because it obscures logic and intention. When you write kid = Child(), you don't expect many things to happen in the background, especially things that act on the instance of Child that you just created. What you expect is a fresh instance of Child.
A factory function, however, transparently does something to an object and returns it to you. This way you know you're getting an already processed instance.
Finally, you wanted to avoid adding "rebind" methods to your Child classes which you now you can since all that logic can be placed in your factory function.
Related
I have a class inherited from project.task named ProjectTask
The class has a copy method that overrides the copy function from project.task it's named Task
I need to run the base copy function from my class instead of the one of the parents class
this is my class code:
#api.multi
#api.returns('self', lambda value: value.id)
def copy(self, default=None):
if default is None:
default = {}
if not default.get('name'):
default['name'] = self.name.id
return super(ProjectTask, self).copy(default) #<-- I don't want to call the inherited class method I want to call the base class method instead
This is the copy method from the base class (Task)
#api.multi
#api.returns('self', lambda value: value.id)
def copy(self, default=None):
if default is None:
default = {}
if not default.get('name'):
default['name'] = _("%s (copy)") % self.name
return super(Task, self).copy(default) # <-- I want to run this method from my class (ProjectTask) which is the child class
Any advice will be more than welcome
With the parent class implementation you show, calling it with your own default should do what you want, as it will just pass it through to its own parent with no changes. (At least, that's true with the bare method code, I don't know what the odoo decorators do to change things.)
But if you really do need to skip over it for some non-obvious reason, you probably can do it. Generally speaking, these approaches will only work as intended if you don't expect your class to ever be used with multiple inheritance. If your MRO gets complicated, then you really want to be doing the normal thing with super and making all your methods play nicely together.
One option for skipping an inherited method is to directly name the class you want your call to go to (i.e. your grandparent class).
class Base():
def foo(self):
print("Base")
class Parent(Base):
def foo(self):
print("Parent")
super().foo() # super() in Python 3 is equivalent to super(Parent, self)
class Child(Parent):
def foo(self):
print("Child")
Base.foo(self) # call Base.foo directly, we need to pass the self argument ourselves
Another option would be to change the argument you give to super to name the parent class instead of your own class. Usually that's a newbie error, but if that's really what you want, it's allowed (though I'd strongly recommend adding a comment to the code explaining that you really do want that behavior!
class Child(Parent):
def foo(self):
print("Child")
super(Parent, self).foo() # Note: Deliberately skipping over Parent.foo here!
A final note: If you find yourself wanting to skip a parent class's implementation of some of its methods, perhaps you should reconsider if you should really be inheriting from it at all. It may be that you really want to be inheriting from the same base class as it instead, and skipping the middle class altogether. Obviously, this has its own limitations (maybe some library code does type checking for that class), but if you find yourself fighting the inheritance machinery, it may be that you're doing things the hard way, and there's an easier alternative.
I've attempted to create a Python interface class hierachy that looks something like:
class Axis(object, metaclass=ABCMeta):
def __init__(self):
# Do stuff...
class LinearAxis(Axis, metaclass=ABCMeta):
#abstractmethod
def move_linear(self, move_um):
pass
def __init__(self):
# Do stuff...
Axis.__init__(self)
class RotationalAxis(Axis, metaclass=ABCMeta):
#abstractmethod
def move_rotate(self, move_degree):
pass
def __init__(self):
# Do stuff...
Axis.__init__(self)
class XAxis(LinearAxis, metaclass=ABCMeta):
def __init__(self):
# Do stuff...
LinearAxis.__init__(self)
So basically an interface sort of like that with a bunch more functions everywhere and stuff in the constructors etc...
Then I go to derive off my interface:
class AnAxis(Axis):
def __init__(self):
# Do stuff...
Axis.__init__(self)
class AnLinearAxis(AnAxis, LinearAxis):
def move_linear(self, move_um):
pass
def __init__(self):
# Do stuff...
AnAxis.__init__(self)
LinearAxis.__init__(self)
class AnRotationalAxis(AnAxis, RotationalAxis):
def move_rotate(self, move_degree):
pass
def __init__(self):
# Do stuff...
AnAxis.__init__(self)
RotationalAxis.__init__(self)
class AnXAxis(AnLinearAxis, XAxis):
def __init__(self):
# Do stuff...
AnLinearAxis.__init__(self)
XAxis.__init__(self)
I'm trying to work out how to call the constructors properly. The way I have it, I'm pretty sure I call the interface constructors many times... So it's wrong... Is there a preferred way to do it? (Perhaps I don't call constructors in the interface classes, or I only call the interface constructor at the end up my implementation class.)
Also, I've never coded in this style and am open to better ways to code this.
You're probably looking for the super() function.
Calling super().something() calls the method something() of the parent class. It makes sure (using __mro__) to call the parent classes' method only once.
i.e. your code will look like this:
class AnLinearAxis(AnAxis, LinearAxis):
def move_linear(self, move_um):
pass
def __init__(self):
# Do stuff...
super().__init__()
Keep in mind you do not need to pass self or the metaclass. The metaclass passes by the inheritance. Also, you do not need to call super more than once. Super will call all of the parent classes' methods automatically.
Regarding the interface, it looks good but there's no need to pass metaclass=ABCMeta if the class you're inheriting from already has it. The metaclass is passed on by inheritance.
So I have a parent class:
class Parent(Object):
def function(self):
do_something()
And many child classes:
class Child1(Parent):
def function(self):
do_something_else_1()
class Child2(Parent):
def function(self):
do_something_else_2()
...
I would like to ensure that the parent function() is always called before the children's function(), so that every call to function() also calls do_something() no matter the class. Now, I know I can do something like:
class Child1(Parent):
def function(self):
super(Child1, self).function()
do_something_else_1()
class Child2(Parent):
def function(self):
super(Child2, self).function()
do_something_else_2()
...
but I would rather not do that for every child class, because these child classes are being generated on the fly, and because these child classes themselves are being extended further. Instead, I would like to do something that looks like
class Child1(Parent):
#call_parent
def function(self):
do_something_else_1()
class Child2(Parent):
#call_parent
def function(self):
do_something_else_2()
...
And write a decorator to accomplish the same task.
I have two questions:
Is this even a good idea? Am I using decorators and function overriding in their intended way?
How would I go about writing this decorator?
Is this even a good idea? Am I using decorators and function overriding in their intended way?
This question is complex to answer without knowing the details about your system.
Just from the abstract example it looks OK, but replacing the explicit and clear super() call with something like #call_parent is not a good idea.
Everyone knows or can easily find out what super() does and decorator will only cause the confusion.
How would I go about writing this decorator?
Don't write the decorator, instead you can use the template method:
class Parent(Object):
def function(self):
do_something()
do_something_in_child()
def do_something_in_child():
pass
Now in child classes you only override the do_something_in_child, the function stays only in the Parent, so you are sure your do_something() is always called.
class Child1(Parent):
def do_something_in_child(self):
do_something_else_1():
class Child2(Parent):
def do_something_in_child(self):
do_something_else_2():
class Child3(Parent):
# no override here, function() will do the same what it does in Parent
pass
Im not well versed on Python but you could so something like:
# Function in childredn. Overrides parent one.
def function(self):
# child code
super().function() #however it is used
# more child code
If that is not plausible, take a look on template method design pattern.
# Function in parent. Do not override this one
def function(self):
# your parent code
function_do_something()
# more code if you need it
# function in parent. Children overryde this one
def function_do_something():
...
And, you can allways let function_do_something() be void, in order to only execute your father constructor.
I need to write a base class (in this example, class A) that will likely, but not always, be subclassed. I want to call the 'Run' method from the base class only if its not subclassed, else only call the the 'Run' method from the subclasses.
This is what I have, it seems to work but I'm wondering if there is an easier or more Pythonic way.
class A(object):
def __init__(self):
pass
def Run(self):
print "Calling A.Run()"
class B(A):
def __init__(self):
A.__init__(self)
pass
def Run(self):
print "Calling B.Run()"
subs = A.__subclasses__()
if subs: inst = [i() for i in subs]
else: inst = [A()]
[each.Run() for each in inst]
What you have looks correct, except that most programmers would look at the B.Run method and think: "Oh, he forgot to call super. Let me just add that..." but I'm sure you'd explain what you're doing in your real implementation :)
If you're worried about something like slicing in C++, then be reassured. What you have is good.
As for making it "easier", I'm not sure how you could simplify it aside from removing A's empty __init__ function and removing pass from B's __init__.
I want to call the 'Run' method from the base class only if its not subclassed, else only call the the 'Run' method from the subclasses.
This is what happens by default, the most specific method is called.
If you really don't want the base method to be available from subclasses, you can write something like this:
class C(object):
def Run(self):
if self.__class__ != C:
raise NotImplementedError("You need to override this method")
I have a class hierarchy where __init__ in class Base performs some pre-initialization and then calls method calculate. The calculate method is defined in class Base, but it's expected to be redefined in derived classes. The redefined calculate will use some of the attributes that are only available in class Derived:
class Base:
def __init__(self, args):
# perform some pre-initialization
...
# now call method "calculate"
self.calculate()
class Derived(Base):
def __init__(self, args, additional_attr):
super().__init__(args)
# do some work and create new instance attributes
...
self.additional_attr = additional_attr
This is not going to work because calculate method in class Derived will be invoked before self.additional_attr is assigned.
I can't move super().__init__(args) call to the end of the __init__ method because some of the work it does has to happen before processing additional_attr.
What to do?
Perhaps you shouldn't have the calculate() call in your constructor then. If you can't construct a derived object by allowing the base constructor to complete first, then you must be doing something wrong IMHO. A sensible approach would be to move that call out of the constructor and perhaps create a factory method to make that call automatically. Then use that method if you need precalculated instances.
class Base(object):
def __init__(self, args):
# perform some initialization
pass
def calculate(self):
# do stuff
pass
#classmethod
def precalculated(cls, args):
# construct first
newBase = cls(args)
# now call method "calculate"
newBase.calculate()
return newBase
class Derived(Base):
def __init__(self, args, additional_attr):
super(Derived, self).__init__(args)
# do some work and create new instance attributes
self.additional_attr = additional_attr
#classmethod
def precalculated(cls, args, additional_attr): # also if you want
newDerived = cls(args, additional_attr)
newDerived.calculate()
return newDerived
newBase = Base('foo')
precalculatedBase = Base.precalculated('foo')
newDerived = Derived('foo', 'bar')
precalculatedDerived = Derived.precalculated('foo', 'bar')
This is bad design, IMHO, and you're obusing the object system of Python. Consider that in other OO languages like C++, you don't even have control over the creation of base classes. The derived class's constructor calls the base constructor before your code runs. Such behavior is almost always expected of well-behaved class hierarchies, and changing it can only lead to problems.
Sure, you can do some patching (such as assigning self.additional_attr before the call to super's constructor, or other tricks), but the better way would be to change your design so that it won't require such hacks. Since you've presented an abstract example here, it's hard to give more comprehensive design advice.
In order for something like this to work, you need to design a protocol that allows the base and derived class(es) to cooperate with each other to accomplish the object initialization task:
class Base:
def __init__(self, args, *additional_args):
# perform some pre-initialization
# ...
# perform any futher initialization needed by derived classes
self.subclass_setup(*additional_args)
# now call method "calculate"
self.calculate()
def subclass_setup(self, *args):
pass
class Derived(Base):
def __init__(self, args, additional_attr):
super().__init__(args, additional_attr)
def subclass_setup(self, additional_attr):
# do some work and create new instance attributes
# ...
self.additional_attr = additional_attr
Can you pass the additional_attr as a parameter to __init__ method of base class and propogate it from there to calculate method?
Say something like:
class Base(object):
def __init__(self, args,additional_attr):
print 'Args for base class:%s' %(args)
self.calculate(additional_attr)
class Derived(Base):
def __init__(self, args, additional_attr):
super(Derived,self).__init__(args,additional_attr)
def calculate(self,val):
print 'Arg for calculate:%s' %(val)
self.additional_attr = val
>>> d = Derived(['test','name'],100)
Args for base class:['test', 'name']
Arg for calculate:100
This is roundabout way, but with no information about what the pre-initialisation steps are, it is hard to say whether the above approach would help you.