it might be a silly question since I'm new to Python.
However, I hope someone can explain this because I try to find lots of resource but still hardly understand the mechanism behind.
So I create a Parent Class and a Child Class. Parent Class have set_name, get_name method
In Child Class, I defined a new method call Turn_Uppercase which calling get_name then uppercase the name. And when using Turn_Uppercase Method, I have to filled in Child Class Name, otherwise it would not work.
Can someone explain the mechanism here!
Let's me explain in code:
So first I create a Parent Class with get_name and set_name method.
class Parent:
def __init__(self, text_input):
self.__name = ""
self.name = text_input
#property #this is get_name
def name(self):
return self.__name
#name.setter #this is set_name
def name(self, text_input: str):
if isinstance(text_input, str): # check input has to be string
self.__name = text_input
else:
print('Wrong type of data')
Then I create a Child Class with new method called Turn_uppercase
class Child_to_parent(Parent):
def __init__(self):
pass
def turn_uppercase(self):
return self.name.upper()
Now I put in some object and use Turn_uppercase Method, I have to fill in Child Class Name
test1 = Child_to_parent
test1.name = "abcdef" # using parent method
print(f'{test1.turn_uppercase(Child_to_parent)}') # using child method
When using parent method through property name, I don't need to declare Child Class Name, but when it comes to use Turn_uppercase Method then I have to.
Why it works this way?
This line makes all the difference
test1 = Child_to_parent
You are not creating an object here, instead merely assigning a reference to the class itself. What you must be doing is
test1 = Child_to_parent() #>Create the object!
test1.name = "abcdef"
print(f'{test1.turn_uppercase()}')
Now why it works with the class? It's because you attached a attribute to the class called name. The method you called used the class as argument and evaluated the class attribute!
Read this answer for a better understanding!
Related
I'm rather new to class inheritance and need some assistance
I have a problem, where I want to override a parent class method, after it has been called from another inherited parent class method.
The basic concept looks something like this:
class Parent:
"""Parent class, that defines the logical workflow"""
def __init__(self):
pass
def outer_method(self):
# This method is called from the sub_classes
# everything in here is the same for all sub_classes
self.__inner_method(self)
def __inner_method(self):
# This method is called from self.outer_method()
# Everything in here will be handled differently by each sub_class
# And will therefore be overridden
pass
class Child(Parent):
"""Sub_class, that inherits from the Parent class"""
def __init__(self):
super().__init__()
def __inner_method(self):
# this should override Parent.__inner_method()
super().__inner_method()
print('Do some custom operations unique to this Sub_class')
The idea here is, that the Child class calls outer_method which then calls __inner_method, which I want to be overridden by the child class.
But that doesn't work.
When I run this script,
def main():
MyChild = Child()
MyChild.outer_method()
if __name__ == "__main__":
main()
what happens is that instead of calling Child.__inner_method(), Parent.__inner_method() is called.
How can I get the Child class to override the inner method of the parent class, after it has been called from the inherited outer method?
The reason of the problem is the name you choose, python apply an special treatment to a class members if its name start with __ but doesn't end with that, called name mangling, the reason it does that is in order to get python version of private variables/methods, so your __inner_method got renamed to _Parent__inner_method as result and any call to __inner_method withing the parent class gets modify to be a call to this renamed method, and because the same happens to the child class its end with a _Child__inner_method of it own which of course messed up with the inheritance mechanism if this isn't desired.
The solution is simple, rename all __inner_method to _inner_method.
A single _ is the convention for private stuff when you don't want the name mangling, with __ is for when you want it to be extra private if you will...
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.
is it possible to achieve type inheritance without overwriting class methods? Take for example this code:
class Parent:
def special_method(self, name):
print("hello, {}!".format(name))
class Child:
def __init__(self, injected_parent):
self.parent = injected_parent
def special_method(self):
self.parent.special_method("Homer Simpson")
parent = Parent()
child = Child(parent)
child.special_method()
# hello, Homer Simpson!
Works as expected, but I want the type of child to be Parent and not Child:
print(type(child))
<class '__main__.Child'>
One way I've seen it done is to extend Child with:
class Child:
def __init__(self, injected_parent):
self.parent = injected_parent
self.__class__ = Parent
...
However, then child's special_method gets overwritten:
parent = Parent()
child = Child(parent)
child.special_method()
# TypeError: special_method() missing 1 required positional argument: 'name'
Any way to make child have type Parent without side-effects?
It seems like what you want to do is just regular inheritance, but as #juanpa.arrivillaga pointed out, you are using composition instead. What you are trying to do by changing the __class__ and overloading a method will not work. Here is an example using inheritance which will allow you to overload the method while still having access to the original method and to have child be an instance of parent which seems to meet your requirements:
class Parent:
def special_method(self, name):
print("hello, {}!".format(name))
class Child(Parent):
def special_method(self):
super().special_method("Homer Simpson")
child = Child()
Now, child will still have a type of Child, but with classes what you want to be checking instead is whether it is an instance of the superclass using isinstance:
>>> child.special_method()
hello, Homer Simpson!
>>> type(child)
<class '__main__.Child'>
>>> isinstance(child, Parent)
True
I have a class that in principle carries all the information about it in its class body. When instantiated, it receives additional information that together with the class attributes forms a regular instance. My problem now lies in the fact that I need to implement a method which should be called as class method when it is called from a class object but should be called as regular instance method when called from an instance:
e.g. something like
class MyClass(object):
attribs = 1, 2, 3
def myMethod(self, args):
if isclass(self):
"do class stuff"
else:
"do instance stuff"
MyClass.myMethod(2) #should now be called as a class method, e.g. I would normally do #classmethod
MyClass().myMethod(2) #should now be called as instance method
Of course I could declare it as staticmethod and pass either the instance or the class object explicitly, but that seems rather unpythonic and also user unfriendly.
If the methods are to behave differently, you could simply change which one is exposed by that name at initialization time:
class MyCrazyClass:
#classmethod
def magicmeth(cls):
print("I'm a class")
def _magicmeth(self):
print("I'm an instance")
def __init__(self):
self.magicmeth = self._magicmeth
You can define a decorator that works like a regular method when called on an instance, or class method when called on a class. This requires a descriptor:
from functools import partial
class anymethod:
"""Transform a method into both a regular and class method"""
def __init__(self, call):
self.__wrapped__ = call
def __get__(self, instance, owner):
if instance is None: # called on class
return partial(self.__wrapped__, owner)
else: # called on instance
return partial(self.__wrapped__, instance)
class Foo:
#anymethod
def bar(first):
print(first)
Foo.bar() # <class '__main__.Foo'>
Foo().bar() # <__main__.Foo object at 0x106f86610>
Note that this behaviour will not be obvious to most programmers. Only use it if you really need it.
I want the Parent class to have a checking mechanism to ensure all its subclasses to set an actual value to the attribute name. I found something here.
class Parent(object):
#name = None
def __init__(self):
if self.name == None:
raise NotImplementedError('Subclasses must define name')
class Child1(Parent):
pass
class Child2(Parent):
name = 'test'
class Child3(Parent):
def __init__(self):
self.name = 'test'
class Child4(Parent):
def __init__(self):
pass
#obj1 = Child1() # Expected output: NotImplementedError: Subclasses must define bar
obj2 = Child2()
obj3 = Child3()
obj4 = Child4() # I want the NotImplementedError is raised here as well, but it doesn't
The problem is as long as there is an __init__ method in the subclass, it overwrites the Parent class and the raise NotImplementedError is no longer in effect.
My current working solution is:
class Child5(Parent):
def __init__(self):
self.name = 'test'
super().__init__()
obj5 = Child5()
which seems to work, but I wonder if it's a proper implementation, or if it may have some hidden pitfalls, and also if I should learn to use/implement #abstractproperty instead of this solution?
Here, you need to understand when you parent class constructor gets called. Note that while creating child class objects, if child class has a constructor it is called by default. It is up to us whether we want to call parent class constructor as well and this shall be done by us. However if child class doesn't have a constructor, then the base class constructor is called.
So with your Child1(), parent constructor is called by default so it raises the exception.
In your Child2() as well parent constructor is called. However do note here that name variable is static and can even be accessed as Child2.name. And thus no exception is raised.
Your Child3 class has a constructor has a constructor thus parent constructor is never called and thus check for presence of name is actually never made. So you do need to add following line to to child constructor.
super().__init__()
This call shall be made after declaring name if constructor defines name. And this is what you have done in your Child5 class.
For exactly the same reason as above, exception was not captured in Child4. Following will check this condition in Child4:
class Child4(Parent):
def __init__(self):
super().__init__()
You can check when constructor is being called, by simply adding a unique print statement(such as print(1), print(2), and so on) in each constructor (preferably at the beginning).