Python class inside subclass inheriting from class inside parent class - python

The title is pretty much self explanatory, but I think this is better explained with an example.
class Dog():
def __init__(self, name):
self.name = name
def get_name(self):
return self.name
def get_color(self):
return body_color()
class personality_1():
def get_happiness(self):
return happiness_with_owner()
def get_sadness(self):
return sadness()
## A lot more personality methods here
class SocialDog(Dog):
# Override regular method
def get_color(self):
return face_color()
# I want to override the personality 1 class but not completely, just one method
class personality_2(>>>How to inherit from personality_1?<<<):
# Now, I would like to override just one method of personality 1:
def get_happiness(self):
return happiness_with_others()
Hopefully the logic is correct. I was trying to use super() with no success. Hopefully I can find a solution without using an explicit call to the parent class.
Any thoughts?
Thanks in advance!

To inherit from the class you specified, according to the code you provided, all that is required is to define the class personality_2 like this:
class SocialDog(Dog):
#...
class personality_2(Dog.personality_1):
#...
Now, as for your problem when trying to use super(), this might be because your base classes of Dog and Dog.personality_1 do not inherit from the python default class object which is required in order to use the super() method. See this answer for details. If that is what you are after, all you need to do is modify your class declarations for Dog and Dog.personality_1 (or whatever they ultimately derive from) to the following:
class Dog(object):
#...
class personality_1(object):
#...
Then you can treat SocialDog.personality_2 just like any other subclass. If you are using python 2, remember when using super() that you need to use the fully qualified name:
super(SocialDog.personality_2, self).super_class_method()
super(SocialDog.personality_2, self).super_class_field

Use the name of the outer class to reach the inner class:
class SocialDog(Dog):
class personality_2(Dog.personality_1):
# ...
That said, this is a very weird thing you're doing, hiding the personality classes inside the dog classes, then using them outside...
If a personality is that tightly coupled to a specific class like Dog or SocialDog, what makes personality_2 think it's safe to mess with the behaviour of personality_1? In fact, the personality methods should probably be Dog or SocialDog methods instead.
Or, if it doesn't really matter which dog gets which personality, why not leave the personality classes up at the module level, where they can be instantiated and inherited like any other class? Your various Dog-derived classes would then take an optional personality argument when created:
class WolfPersonality(DogPersonality):
# ...
class Wolf(Dog):
def __init__(self, personality=None):
if personality is None:
personality = WolfPersonality()
self.personality = personality
# ...
# Later...
wolf = Wolf()
wolf_puppy = Wolf(FriendlyDogPersonality())

Related

How to refer to subclass property in abstract shared implementation in abstract class method

For classes:
class Base(ABC):
def __init__(self, param1):
self.param1 = param1
#abstractmethod
def some_method1(self):
pass
# #abstractmethod
# def potentially_shared_method(self):
# ????
class Child(Base):
def __init__(self, param2):
self.param1 = param1
self.param2 = param2
def some_method1(self):
self.object1 = some_lib.generate_object1(param1, param2)
def potentially_shared_method(self):
return object1.process()
I want to move the potentially_shared_method to be shared in abstract calss, however it uses object1 that is initialized in some_method1 and needs to stay there.
If it's only potentially shared, it doesn't belong in the base class. You'd be breaking a few design principles.
What is a child class supposed to do for which the sharing doesn't make sense?
Also, you're introducing some temporal coupling; you can only call potentially_shared_method after some_method1 has been called. That's not ideal because the users of your class might not realize that.
Also, if the method is shared, you probably don't want it to be abstract in your base class; with an abstract method you're really only sharing the signature; but it seems you'll want to share functionality.
Anyway. Here's some options:
Using Python's multiple inheritance, move potentially_shared_method into a SharedMixin class and have those children who share it inherit from Base and from SharedMixin. You can then also move some_method1 into that SharedMixin class because it seems to me that those go together. Or maybe not...
Hide the access to object1 behind a getter. Make the getter have a dummy implementation in the base class and a proper implementation in those child classes who actually create an object1. Then potentially_shared_method can be moved to Base and just refer to the getter.

Multiple Inheritance -- super() vs Parent class names

When working with multiple inheritance, I'm trying to understand why it works if you refer to the parents' class names in the child class, but it does not work if you use super(). Any help is appreciated.
#DOES NOT WORK WITH super()
#Parent 1 class
class Husband:
def __init__(self,strength):
self.strength = strength
#Parent 2 class
class Wife:
def __init__(self,looks):
self.looks = looks
#Child class
class Son(Husband,Wife):
def __init__(self,strength,looks):
super().__init__(self,strength)
super().__init__(self,looks)
son = Son('Goliath','GQ')
print(son.strength,son.looks)
#WORKS WHEN CALLING PARENT CLASS NAMES
#Parent 1 class
class Husband:
def __init__(self,strength):
self.strength = strength
#Parent 2 class
class Wife:
def __init__(self,looks):
self.looks = looks
#Child class
class Son(Husband,Wife):
def __init__(self,strength,looks):
Husband.__init__(self,strength)
Wife.__init__(self,looks)
son = Son('Goliath','GQ')
print(son.strength,son.looks)
Super doesn't mean "parent", it means "next in line in the MRO of this object".
So if you make a class that you want to be inherited from, you should probably be calling super() in that parent class as well.
If self is a Son object, then within the body of the Husband.__init__(self) method, super() delegates to Wife, not to general object. You should only need one super().__init__ call within Son, and then the super call in Husband.__init__ can delegate to Wife.__init__. (I'm not sure subclassing is the correct relationship for this case: I wouldn't say a Son is-a Wife). You may also need to update the __init__ signatures to accept arbitrary **kwargs so that everything can be passed along in a compatible way.
Here's a blog post that I think is helpful: https://rhettinger.wordpress.com/2011/05/26/super-considered-super/
There's a video on YouTube with the same title "Super Considered Super" that I think is helpful as well.
In short, if your class has multiple inheritance, a plain call to super() always refer to the Husband class.
You may want to look for a concept called MRO (Method resolution order).

Why is Python super used in the child's init method?

According to Python docs super()
is useful for accessing inherited methods that have been overridden in
a class.
I understand that super refers to the parent class and it lets you access parent methods. My question is why do people always use super inside the init method of the child class? I have seen it everywhere. For example:
class Person:
def __init__(self, name):
self.name = name
class Employee(Person):
def __init__(self, **kwargs):
super().__init__(name=kwargs['name']) # Here super is being used
def first_letter(self):
return self.name[0]
e = Employee(name="John")
print(e.first_letter())
I can accomplish the same without super and without even an init method:
class Person:
def __init__(self, name):
self.name = name
class Employee(Person):
def first_letter(self):
return self.name[0]
e = Employee(name="John")
print(e.first_letter())
Are there drawbacks with the latter code? It looks so much cleanr to me. I don't even have to use the boilerplate **kwargs and kwargs['argument'] syntax.
I am using Python 3.8.
Edit: Here's another stackoverflow questions which has code from different people who are using super in the child's init method. I don't understand why. My best guess is there's something new in Python 3.8.
The child might want to do something different or more likely additional to what the super class does - in this case the child must have an __init__.
Calling super’s init means that you don’t have to copy/paste (with all the implications for maintenance) that init in the child’s class, which otherwise would be needed if you wanted some additional code in the child init.
But note there are complications about using super’s init if you use multiple inheritance (e.g. which super gets called) and this needs care. Personally I avoid multiple inheritance and keep inheritance to aminimum anyway - it’s easy to get tempted into creating multiple levels of inheritance/class hierarchy but my experience is that a ‘keep it simple’ approach is usually much better.
The potential drawback to the latter code is that there is no __init__ method within the Employee class. Since there is none, the __init__ method of the parent class is called. However, as soon as an __init__ method is added to the Employee class (maybe there's some Employee-specific attribute that needs to be initialized, like an id_number) then the __init__ method of the parent class is overridden and not called (unless super.__init__() is called) and then an Employee will not have a name attribute.
The correct way to use super here is for both methods to use super. You cannot assume that Person is the last (or at least, next-to-last, before object) class in the MRO.
class Person:
def __init__(self, name, **kwargs):
super().__init__(**kwargs)
self.name = name
class Employee(Person):
# Optional, since Employee.__init__ does nothing
# except pass the exact same arguments "upstream"
def __init__(self, **kwargs):
super().__init__(**kwargs)
def first_letter(self):
return self.name[0]
Consider a class definition like
class Bar:
...
class Foo(Person, Bar):
...
The MRO for Foo looks like [Foo, Person, Bar, object]; the call to super().__init__ inside Person.__init__ would call Bar.__init__, not object.__init__, and Person has no way of knowing if values in **kwargs are meant for Bar, so it must pass them on.

Overriding __init__ with parent classmethod in python

I want to do something like the following (in Python 3.7):
class Animal:
def __init__(self, name, legs):
self.legs = legs
print(name)
#classmethod
def with_two_legs(cls, name):
# extremely long code to generate name_full from name
name_full = name
return cls(name_full, 2)
class Human(Animal):
def __init__(self):
super().with_two_legs('Human')
john = Human()
Basically, I want to override the __init__ method of a child class with a factory classmethod of the parent. The code as written, however, does not work, and raises:
TypeError: __init__() takes 1 positional argument but 3 were given
I think this means that super().with_two_legs('Human') passes Human as the cls variable.
1) Why doesn't this work as written? I assumed super() would return a proxy instance of the superclass, so cls would be Animal right?
2) Even if this was the case I don't think this code achieves what I want, since the classmethod returns an instance of Animal, but I just want to initialize Human in the same way classmethod does, is there any way to achieve the behaviour I want?
I hope this is not a very obvious question, I found the documentation on super() somewhat confusing.
super().with_two_legs('Human') does in fact call Animal's with_two_legs, but it passes Human as the cls, not Animal. super() makes the proxy object only to assist with method lookup, it doesn't change what gets passed (it's still the same self or cls it originated from). In this case, super() isn't even doing anything useful, because Human doesn't override with_two_legs, so:
super().with_two_legs('Human')
means "call with_two_legs from the first class above Human in the hierarchy which defines it", and:
cls.with_two_legs('Human')
means "call with_two_legs on the first class in the hierarchy starting with cls that defines it". As long as no class below Animal defines it, those do the same thing.
This means your code breaks at return cls(name_full, 2), because cls is still Human, and your Human.__init__ doesn't take any arguments beyond self. Even if you futzed around to make it work (e.g. by adding two optional arguments that you ignore), this would cause an infinite loop, as Human.__init__ called Animal.with_two_legs, which in turn tried to construct a Human, calling Human.__init__ again.
What you're trying to do is not a great idea; alternate constructors, by their nature, depend on the core constructor/initializer for the class. If you try to make a core constructor/initializer that relies on an alternate constructor, you've created a circular dependency.
In this particular case, I'd recommend avoiding the alternate constructor, in favor of either explicitly providing the legs count always, or using an intermediate TwoLeggedAnimal class that performs the task of your alternate constructor. If you want to reuse code, the second option just means your "extremely long code to generate name_full from name" can go in TwoLeggedAnimal's __init__; in the first option, you'd just write a staticmethod that factors out that code so it can be used by both with_two_legs and other constructors that need to use it.
The class hierarchy would look something like:
class Animal:
def __init__(self, name, legs):
self.legs = legs
print(name)
class TwoLeggedAnimal(Animal)
def __init__(self, name):
# extremely long code to generate name_full from name
name_full = name
super().__init__(name_full, 2)
class Human(TwoLeggedAnimal):
def __init__(self):
super().__init__('Human')
The common code approach would instead be something like:
class Animal:
def __init__(self, name, legs):
self.legs = legs
print(name)
#staticmethod
def _make_two_legged_name(basename):
# extremely long code to generate name_full from name
return name_full
#classmethod
def with_two_legs(cls, name):
return cls(cls._make_two_legged_name(name), 2)
class Human(Animal):
def __init__(self):
super().__init__(self._make_two_legged_name('Human'), 2)
Side-note: What you were trying to do wouldn't work even if you worked around the recursion, because __init__ doesn't make new instances, it initializes existing instances. So even if you call super().with_two_legs('Human') and it somehow works, it's making and returning a completely different instance, but not doing anything to the self received by __init__ which is what's actually being created. The best you'd have been able to do is something like:
def __init__(self):
self_template = super().with_two_legs('Human')
# Cheaty way to copy all attributes from self_template to self, assuming no use
# of __slots__
vars(self).update(vars(self_template))
There is no way to call an alternate constructor in __init__ and have it change self implicitly. About the only way I can think of to make this work in the way you intended without creating helper methods and preserving your alternate constructor would be to use __new__ instead of __init__ (so you can return an instance created by another constructor), and doing awful things with the alternate constructor to explicitly call the top class's __new__ to avoid circular calling dependencies:
class Animal:
def __new__(cls, name, legs): # Use __new__ instead of __init__
self = super().__new__(cls) # Constructs base object
self.legs = legs
print(name)
return self # Returns initialized object
#classmethod
def with_two_legs(cls, name):
# extremely long code to generate name_full from name
name_full = name
return Animal.__new__(cls, name_full, 2) # Explicitly call Animal's __new__ using correct subclass
class Human(Animal):
def __new__(cls):
return super().with_two_legs('Human') # Return result of alternate constructor
The proxy object you get from calling super was only used to locate the with_two_legs method to be called (and since you didn't override it in Human, you could have used self.with_two_legs for the same result).
As wim commented, your alternative constructor with_two_legs doesn't work because the Human class breaks the Liskov substitution principle by having a different constructor signature. Even if you could get the code to call Animal to build your instance, you'd have problems because you'd end up with an Animal instances and not a Human one (so other methods in Human, if you wrote some, would not be available).
Note that this situation is not that uncommon, many Python subclasses have different constructor signatures than their parent classes. But it does mean that you can't use one class freely in place of the other, as happens with a classmethod that tries to construct instances. You need to avoid those situations.
In this case, you are probably best served by using a default value for the legs argument to the Animal constructor. It can default to 2 legs if no alternative number is passed. Then you don't need the classmethod, and you don't run into problems when you override __init__:
class Animal:
def __init__(self, name, legs=2): # legs is now optional, defaults to 2
self.legs = legs
print(name)
class Human(Animal):
def __init__(self):
super().__init__('Human')
john = Human()

Can #classmethod be substituted using #staticmethod and returning an instance of the class?

I was reading this tutorial on #classmethod and #staticmethod, I am not sure why is necessary to use #classmethod. I can have the same result using #staticmethod and returning an instance of the class, following the example of the tutorial:
class Pizza:
def __init__(self, ingredients):
self.ingredients = ingredients
#classmethod
def prosciutto(cls):
return cls(['mozzarella', 'tomatoes', 'ham'])
#staticmethod
def prosciutto2():
return Pizza(['mozzarella', 'tomatoes', 'ham'])
I was wondering if there is any difference between this two implementations:
p = Pizza.prosciutto()
p1 = Pizza.prosciutto2()
Can a static method that returns an instance of its own class be used instead of a class method without any disadvantage?
When there's only a single class, like in your example, then either a classmethod or a staticmethod (that names the class explicitly in its implementation) can work. But if you want to be able to extend the class in the future, you probably want to use classmethod.
Here's an example that builds on your code:
class ThinCrustPizza(Pizza):
pass
If you call ThinCrustPizza.prosciutto(), you'll get an instance of ThinCrustPizza, not the base Pizza class it inherited the method from. That's because cls in the classmethod will be the class you called it on, which is the child class.
But if you call ThinCrustPizza.proscutto2(), you'll get the same Pizza instance you would get from calling it on Pizza, since the implementation needed to refer to Pizza by name. Because it doesn't get the class passed in, it can't tell that it was called on a subclass.

Categories