How to call super of enclosing class in a mixin in Python? - python

I have the following code, in Django:
class Parent(models.Model):
def save(self):
# Do Stuff A
class Mixin(object):
def save(self):
# Do Stuff B
class A(Parent, Mixin):
def save(self):
super(A, self).save()
# Do stuff C
Now, I want to use the mixin without blatting the behaviour of the save in Parent. So I when I save, I want to do stuff C, B, and A. I've read Calling the setter of a super class in a mixin however I don't get it and having read the super docs it doesn't seem to answer my question.
THe question is, what do I put in the mixin to make sure it does stuff B and doesn't stop Stuff A from happening?

How about calling super in your mixin class?
class Parent(object):
def test(self):
print("parent")
class MyMixin(object):
def test(self):
super(MyMixin, self).test()
print("mixin")
class MyClass(MyMixin, Parent):
def test(self):
super(MyClass, self).test()
print("self")
if __name__ == "__main__":
my_obj = MyClass()
my_obj.test()
This will give you the output as:
$ python test.py
parent
mixin
self

The best practice for calling the implementation from the superclass is to use super():
class Mixin(object):
def save(self):
super(Mixin, self).save()
# Do Stuff B here or before super call, as you wish
What is important is that you call super() in each class (so that it propagates all the way) but not the topmost (base) class, because its superclass does not have save().
Note that when you call super(Mixin, self).save(), you don't really know what the super class would be once it is executed. That will be defined later.
Unlike some other languages, in python, the end class will always have a linear list of classes from which it inherits. That is called MRO (Method Resolution Order). From MRO Python decides what to do on super() call. You can see what the MRO is for your class this way:
>>> A.__mro__
(<class '__main__.A'>, <class '__main__.Parent'>, <class '__main__.Model'>, <class '__main__.Mixin'>, <type 'object'>)
So, A's super is Parent, Parent's super is Model, Model's super is Mixin and Mixin's super is object.
That is wrong, so you should change A's parents to:
class A(Mixin, Parent):
Then you'd have a better MRO:
>>> A.__mro__
(<class '__main__.A'>, <class '__main__.Mixin'>, <class '__main__.Parent'>, <class '__main__.Model'>, <type 'object'>)

#Click2Death answer is correct, however, when you call super().test() inside your mixin class most IDE will claim that test is unresolved, which is correct.
Here is how to make your IDE happy and your code better.
class Parent(object):
def test(self):
print("parent")
class MyMixin(object):
def test(self):
super_test = getattr(super(), 'test')
if super_test and callable(super_test):
super_test()
print("mixin")
class MyClass(MyMixin, Parent):
def test(self):
super().test()
print("self")
if __name__ == "__main__":
my_obj = MyClass()
my_obj.test()
This is Python 3 code, to make it working with Python 2 you need to pass two arguments to the super(MyClass, self) call

Django Example (Python 3+)
To expand on Vladimir Prudnikov's answer in a Django context, the template view mixin class could be structured as shown below.
from django.views.generic.base import View
class MyViewMixin:
def dispatch(self, request, *args, **kwargs):
super_dispatch = getattr(super(), 'dispatch')
if super_dispatch and callable(super_dispatch):
return super_dispatch(request, *args, **kwargs)
raise RuntimeError('MyViewMixin must be used as part of a '
'multiple inheritance chain that includes a Django template-view')
class MyCustomView(MyViewMixin, View):
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)

Related

Is there a magic method in python for when a class is being evaluated?

I have a mixin class that is supposed to be used as a interface only when it is used with other classes, for example:
class Mother():
pass
class Child(Mother):
pass
class Mixin():
def __init__(self):
assert isinstance(self, Mother), 'Mixin can only be used with Mother implementations'
super().__init__()
class Implementation(Mixin, Child):
pass
Implementation()
The above works, but only when Implementation is being instantiated, can I somehow have the above assert be evaluated on code execution?
This is important so that the application won't run if someone implemented a class wrongfully.
(I'm not sure I worded the title correctly)
Actually, it won't work even if "when Implementation is being instantiated" - it'll find the relation to Mother class via Child class (Implementation inherits Child ---> Child inherits Mother),
thereby isinstance(self, Mother) treats Implementation as derived from Mother class due to inheritance chain (considered as mro (method resolution order))
use __init_subclass__ hook instead:
class Mother():
pass
class Child(Mother):
pass
class Mixin():
def __init_subclass__(cls, **kwargs):
assert isinstance(cls, Mother), 'Mixin can only be used with Mother'
super().__init_subclass__(**kwargs)
class Implementation(Mixin, Child):
pass
Implementation()
Throws:
Traceback (most recent call last):
File ..., in __init_subclass__
assert isinstance(cls, Mother), 'Mixin can only be used with Mother'
AssertionError: Mixin can only be used with Mother
But if you need to allow Mixin to be applied to Mother class and its subclasses - use issubclass call instead:
class Mixin():
def __init_subclass__(cls, **kwargs):
assert issubclass(cls, Mother), 'Mixin can only be used with Mother and its subclasses'
super().__init_subclass__(**kwargs)
The hook will be applied on class declaration phase (before potential instantiation)
You can also use metaclass, it is powerful and can help you understand python class.
class Mother():
pass
class Child(Mother):
pass
class Meta(type):
def __new__(meta_cls, name, bases, dct):
if name != "Mixin" and all([not issubclass(b, Mother) for b in bases]):
raise Exception('Mixin can only be used with Mother')
cls = super().__new__(meta_cls, name, bases, dct)
return cls
class Mixin(metaclass=Meta):
pass
class Implementation(Mixin, Child):
pass

suppress calling __init__() of parent class

I just noticed some unintended behaviour then tested it in an interpretor (Python 3.5.3):
>>> class SomeClass:
... def __init__(self):
... print("nothing important")
...
>>> a = SomeClass()
nothing important
>>> class SomeOtherClass(SomeClass):
... pass
...
>>> b = SomeOtherClass()
nothing important
>>>
I thought you needed to directly call the parents __init__(). What is the simplest way to write or instantiate the child class such that it does not run the __init__() from the parent class?
You can by defining an __init__ method in the child class:
class SomeOtherClass(SomeClass):
def __init__(self):
pass
I want some methods from the parent, just not that the init runs
Then your design is wrong. If you only care about code reuse but not proper subtyping (as defined by Liskov), proper designs are either composition/delegation or (probably the best in your case) multiple inheritance with mixin classes:
class CommonMixin():
def method1(self):
pass
def method2(self):
pass
class SomeClass(CommonMixin, SomeBaseClass):
def __init__(self):
print("nothing important")
class SomeOtherClass(CommonMixin, SomeOtherBaseClass):
pass

super function in Multiple inheritance in python

I have written this code in python 3.4 and used classes. I have implemented multiple inheritance in this code with super() function. And i want to call the init() function of library class. But i am unable, can anyone tell me the mistake?
code
class college:
def __init__(self, rollno):
print("Roll no:", rollno)
class library:
def __init__(self, libcardno):
print("Library card no:", libcardno)
class student(college, library):
def __init__(self, name):
print("Name:", name)
super().__init__(5560)
super().__init__(60)
output
>>> obj = student("John")
Name: John
Roll no: 5560
Roll no: 60
Just understand the question, it's not a duplicate of another one.
You can directly call __init__ method of respective parent class.
class student(college, library):
def __init__(self, name):
print("Name:", name)
college.__init__(self,5560)
library.__init__(self,60)
Short:
You can't and you don't need to use super() to call __init__ with different form. Of course there is a way to do, but I do not recommend that.
Long:
Python uses the list of classes to inherit from. You can see that list with
ClassName.__mro__. mro stands for Method Resolution Order
Think about you have three classes, ClassA, ClassB, ClassC and ClassC extends ClassA, and ClassB (If you are using python2, ClassA and ClassB musts extend from object)
class ClassC(ClassA, ClassB):
Then, ClassC.__mro__ will be like below:
(<class '__main__.ClassC'>, <class '__main__.ClassA'>, <class '__main__.ClassB'>, <class 'object'>)
So python inherits ClassC, ClassA, ClassB order. And it assumes that methods have same form in this list. This is why you can't use different forms of __init__ call using super().
If you need to use super() and there is no option, you can consider passing argument as **kwargs.
class ClassA():
def __init__(self, **kwargs):
super(ClassA, self).__init__(**kwargs)
print("Class A Initialized:", kwargs['a'])
class ClassB():
def __init__(self, **kwargs):
# super(ClassA, self).__init__(**kwargs)
# if you call above, you'll get type error, because above will call object.__init__ which takes only 1 argument
print("Class B Initialized:", kwargs['b'])
class ClassC(ClassA, ClassB):
def __init__(self, **kwargs):
super(ClassC, self).__init__(**kwargs)
print("Class C Initialized:", kwargs['c'])
ClassC(a="A", b="B", c="C")
And you can get below output :
Class B Initialized: B
Class A Initialized: A
Class C Initialized: C
But still, I recommend that do not use this form of inheritance, because it is very complicated

Refer to a class outside its method?

I need to deliver something like this in my program
class the_class_name(Parent):
the_attribute = self.parent_class_method()
#the parent class method will return a value
#but I cannot use self here since there's no self
How can I carry this out? Is there any other alternative that can do the job for me?
I have tried using __init__ like this:
def __init__(self):
Parent.__init__(self)
self.attribute = self.the_method()
But then I have problem creating the object, it won't receive any parameters that the Parent class normally receives anymore
Sounds like you are looking for __init__:
class TheClassName(Parent):
def __init__(self):
# Set attribute to the result of the parent method
self.attribute = super(TheClassName, self).the_method()
EDIT
If your parent class has parameters in it's own __init__ function, include them in the child class:
class Parent(object):
def __init__(self, foo, bar):
...
#classmethod
def the_method(cls):
...
class TheClassName(Parent):
def __init__(self, foo, bar):
super(TheClassName, self).__init__(foo, bar)
self.attribute = super(TheClassName, self).the_method()
I don't quite understand why you don't just call the parent method on your child object when you need the value though.
There is no self at that point of the creation of the subclass, nor is there an instance of the Parent class. That means the only Parent class methods you could call would have to be either static or class methods.
To demonstrate:
class Parent(object):
#staticmethod
def static_method():
return 42
#classmethod
def class_method(cls):
return 43
class TheClassName(Parent):
the_attribute = Parent.static_method()
another_attribute = Parent.class_method()
print(TheClassName.the_attribute) # -> 42
print(TheClassName.another_attribute) # -> 43
You must use class methods, declared with the #classmethod decorator, or a #staticmethod. The #classmethod decorator is preferable so that inheritance is handled correctly, i.e. the method is invoked on the derived class (a bit of a technicality, if you are still learning this).
class Alpha(object):
#classmethod
def method1(cls):
return 'method1 has been called on {}'.format(cls)
class Beta(Alpha):
def __init__(self):
self.myattr = Beta.method1()
print(Beta().myattr)
method1 has been called on class <'__main__.Beta'>
Use
super(ClassName, self).methodname(arg)
inside a method
def child_method(self, arg):
super(ClassName, self).methodname(arg)
You cannot use self outside a method.

When running a method from a Python superclass, how can I know the name of the child class that invoked it?

Let's say I have this parent class:
class BaseTestCase(unittest.TestCase):
#classmethod
def setUpClass(cls):
# I want to assign the name of the class that called
# the super class in a variable.
cls.child_class_name = ??
# Do some more stuff...
And I have this class that inherits from the BaseTestCase class above:
class MyTestCase(BaseTestCase):
#classmethod
def setUpClass(cls):
# Call SetUpClass from parent (BaseTestCase)
super(cls, cls).setUpClass()
# Do more stuff...
Since many classes can inherit from the same parent class. How can I know the name of the class that invoked the parent class in a given time?
I hope my question make sense. :S
cls.__name__ is always the name of the current class, because cls is bound the actual class object on which the class method was called.
In other words, cls is not a reference to the class on which the method is defined.
Note that you should not use super(cls, cls)! That'll lead to infinite recursion if you were to create a derived class from MyTestCase! Use the actual class, always:
class MyTestCase(BaseTestCase):
#classmethod
def setUpClass(cls):
# Call SetUpClass from parent (BaseTestCase)
super(MyTestCase, cls).setUpClass()
# Do more stuff...
Demo:
>>> class Foo(object):
... #classmethod
... def spam(cls):
... print(cls.__name__)
...
>>> class Bar(Foo):
... #classmethod
... def spam(cls):
... super(Bar, cls).spam()
...
>>> Bar.spam()
Bar
>>> Foo.spam()
Foo

Categories