I want to use polymorphism and have a Movil class as my parent class, and while specific class is Car:
class Movil:
def __init__(self, name):
self._name = name
class Car(Movil):
def __init__(self, code):
super().__init__()
self.code = code
Since every Movil takes a name and every Movil takes a code and is a car, I expect to be able to pass both:
class Main(object):
def main(self):
a=Car('toyota','001')
if __name__ == "__main__":
Main().main()
But I am getting this error:
Traceback (most recent call last):
File "main", line 1, in <module>
File "main", line 3, in main
TypeError: __init__() takes 2 positional arguments but 3 were given
What is wrong with this code?
TLDR: Method parameters are not "inherited" when a child class overrides a parent method. The child class method must explicitly take and forward the parameter:
class Car(Movil):
def __init__(self, name, code):
super().__init__(name)
self.code = code
Inheritance only integrates attributes and methods of the base class into the child class. Notably, if the child class redefines an attribute or method, this hides ("shadows") the parent attribute/method completely.
For example, if Car would not define its own __init__ then Movil.__init__ would be used. Related and derived features – such as "the parameters of __init__" – are not themselves inherited: they only show up because they belong to the inherited attribute/method.
Since Car does define its own __init__, this shadows Movil.__init__ including its related features, such as the parameters.
In order for Car to take the original name parameter, it must be explicitly re-defined on Car.__init__:
class Car(Movil):
# v take `name` parameter of Movil.__init__
def __init__(self, name, code):
# v pass on `name` parameter to Movil.__init__
super().__init__(name)
self.code = code
As an alternative, variadic positional (*args) or keyword (**kwargs) parameters may be used to forward all unknown arguments:
class Car(Movil):
# v collect unknown arguments
def __init__(self, code, *args, **kwargs):
# v pass on unknown arguments to Movil.__init__
super().__init__(*args, **kwargs)
self.code = code
a = Car("001", "toyota")
b = Car(name="toyota", code="001")
Be mindful that variadic parameters make it difficult or impossible to replicate some patterns using positional-or-keyword parameters. For example, in the above example it is not possible to accept code as both a keyword or trailing positional argument, as is possible with the explicit definition.
Related
I have a semi-abstract Parent and Middle classes and some Grandchild fully implemented ones that inherit from each other, but while the Parent and Grandchild need some __init__ args, the middle one is just for shared implemented code:
class Parent:
def __init__(self, some_arg):
...
class Middle(Parent, ABC):
def do_something_any_middle_can_do():
...
class Grandchild(Middle):
def __init__(self, some_arg):
super().__init__(some_arg)
...
As you can see, the super().__init__(some_arg) in the Grandchild would call the default __init__ in the Middle, and not send some_arg to the Parent.
So far, I have thought to use **kwargs, but that requires the authors of any new Grandchild to explicitly name the args in super().__init__(some_arg=some_arg) and if they don't unexpected things might happen without a good error message:
class Parent:
def __init__(self, some_arg):
...
class Middle(Parent, ABC):
def __init__(self, **kwargs):
super().__init__(kwargs)
...
def do_something_any_middle_can_do():
...
class Grandchild(Middle):
def __init__(self, some_arg):
super().__init__(some_arg=some_arg)
...
As you can see, the super().__init__(some_arg) in the Grandchild would call the default __init__ in the Middle, and not send some_arg to the Parent.
That's not correct. There is no "default" __init__ method in Middle.
super().__init__ refers to the __init__ attribute of the next class in self's method resolution order that has a defined __init__ method. Since Middle.__init__ is not defined, that means Parent.__init__ is called immediately.
The advice given in Python's super considered super! is to use keyword arguments to avoid conflict between which classes define which positional parameters. (It's the responsibility of the class designer to know which keyword arguments each ancestor expects, and to resolve any conflicts between ancestors that use the same name for different purposes.)
class Parent:
def __init__(self, *, some_arg, **kwargs):
super().__init__(**kwargs)
...
class Middle(Parent, ABC):
def do_something_any_middle_can_do(self):
...
class Grandchild(Middle):
def __init__(self, **kwargs):
super().__init__(**kwargs)
...
g = Grandchild(some_arg=3)
Grandchild.__init__ doesn't need to "advertise" some_arg; it accepts it as an arbitrary keyword argument and passes it up the chain (via super().__init__). Eventually, some class (Parent, in this case) has a defined parameter to accept it; otherwise, the argument will be passed to object.__init__ where an exception will be raised.
I have few variables defined in a python file['apppythonfile.py'] under 'class appstack(cdk.Stack)' . I need to utilize or pass these variables in another class [ class GenStack(cdk.Stack) ] in another Python file ['Genpythonfile.py']. I tried below code to do it.
class GenStack(cdk.Stack):
def __init__(self, scope:cdk.Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, appstack, construct_id, **kwargs)
##tryed calling the 'SGnode' variable defined in appstack class
print (appstack.SGnode)
After executing this I got
TypeError: __init() takes from 1 to 3 positional arguments but 4 positional arguments ( and 1 keyword-only argument) were given
Instead I tried importing the whole file like this also
from apppythonfile import *
class GenStack(cdk.Stack):
def __init__(self, scope:cdk.Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope,construct_id, **kwargs)
# tried printing a variable from apppythonfile.py file
print (SGnode)
Then got
NameError: name 'SGnode' is not defined
SGnode is declared like below in apppythonfile.py
class appstack(cdk.Stack):
def __init__(self, scope:cdk.Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope,construct_id, **kwargs)
#Varibles defined for further use
vpcID=vpc-6895xxxxxx
vpc_declared=ec2.Vpc.from_lookup(self,"vpctouse",vpc_id=vpcID)
SGnode = ec2.SecurityGroup(self,"securitygroup of nodes", vpc=vpc_declared, securirt_group_name="prod-node-sg", allow_all_outbound=True)
I am new to Oops concepts and aws-cdk. How should I declare it in Genpythonfile.py to call the variables from different class and apppythonfile.py python file.
From what I understood , you want inherit all variables/methods from a class of a different file, right?
In this case :
from apppythonfile import appstack
class GenStack(appstack):
#your code
This is called Inheritance which allows you to define a class that inherits all the methods and properties from another class. In this case, the parent class is appstack whereas the child class is GenStack.
EDIT : It seems like that you forgot to add "__" after "__init" , it should
be
__init__
I'm trying to make it so that when a Boxer object is created there is no need to specify the breed as it is automatically set to Boxer
class Dog:
def __init__(self, name, age, breed):
# instance attributes
self.name = name
self.age = age
self.breed = breed
class Boxer(Dog):
super().__init__().breed = "Boxer"
def speak(self,sound = 'woof'):
print(f'{self.name} says {noise}')
hettie = Boxer('Hettie',5)
hettie.speak()
At the moment when I run this code I get this error:
RuntimeError: super(): no arguments
I tried initially to put the super() line inside the __init__() method but then I had an issue with not enough arguments when I called Boxer
def __init__(self):
super().breed = "boxer"
but if I added arguments I still had issues
def __init__(self,breed):
super().breed = "boxer"
You need to put your call to super()'s method inside the __init__ method.
def __init__(self, name, age): # Also provide all the parameters
super(Boxer, self).__init__(name, age, breed="Boxer")
Notice that you can optionally provide the classname Boxer and the instance self to super as arguments (if you don't it will be done automatically) and you also need to provide all the arguments passed in the __init__ method into the super().__init__ method. Because the constructor __init__ always returns None you cannot modify any of it's attributes like you did. You just have to set self.breed = "Boxer" or just pass in "Boxer" as the parameter to the cosntructor.
So I have a class (let's call it ParamClass) which requires a parameter for initialization, and that parameter is something that should be available to the user to configure via some option-setting interface.
ParamClass knows nothing about the configuration interface or how to read them. So I made another class, called Configurator, which does all of that. When a class inherits from Configurator and tells it what configuration keys to read, Configurator's __init__() method will read those keys and assign their values to the correct attributes in self.
The problem I run into, however, is that when I try to pass arguments to super(), including the parameters to be read by Configurator, those parameters have no value yet. But they are passed as constants in the argument list to the super(). Example shown below. MyClass.__init__() can't even get started because self.server_param doesn't exist yet.
class ParamClass:
"""Just some class that needs a parameter for init"""
def __init__(self, param1, **kwargs) -> None:
super().__init__(**kwargs)
self.value = param1
class Configurator:
"""Reads parameters from a configuration source and sets appropriate class
variables.
"""
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.server_param = 2
class MyClass(Configurator, ParamClass):
def __init__(self, **kwargs) -> None:
super().__init__(param1=self.server_param, **kwargs)
# <-- Gives AttributeError: 'MyClass' object has no attribute 'server_param'
MyClass()
The only way I can get this to work is to break MRO in Configurator.init() and force the order of initilization. This is bad for obvious reason - I plan to use Configurator throughout my code and can't break MRO with it.
class ParamClass:
"""Just some class that needs a parameter for init"""
def __init__(self, param1, **kwargs) -> None:
super().__init__(**kwargs)
self.value = param1
class Configurator:
"""Reads parameters from a configuration source and sets appropriate class
variables.
"""
def __init__(self, **kwargs) -> None:
# super().__init__(**kwargs)
self.server_param = 2
class MyClass(Configurator, ParamClass):
def __init__(self, **kwargs) -> None:
Configurator.__init__(self, **kwargs)
# <-- After this call, self.server_param is defined.
ParamClass.__init__(self, param1=self.server_param, **kwargs)
MyClass()
How do I accomplish configuration of parameters in while user super? How do I do this in a generalized way that doesn't require Configurator to know little details about ParamClass?
Note: In my particular case, I don't "own" the ParamClass() code. It is library code that I'm using.
I am confused even after checking many questions asked in SO. I have 2 different class (2 different script) & I want to inherit super class's __init__ method's parameters.
script1.py
class MainClass():
def __init__(self,params):
self.one=params['ONE']
self.two=params['TWO']
self.three=params['THREE']
self.four=params['FOUR']
self.five=params['FIVE']
def a():
#---------
#somecode
#Initializing other class's object to access it's method.
s=SubClass() #HERE I WANT TO PASS 'PARAMS' (WHICH IS A DICTIONARY)
s.method1(....)
script2.py
class SubClass(SuperClass):
def __init__(self,params):
#Here I want all the parameters inside the 'param' in super class.
#(one,two,three...., etc).
#By checking some SO questions, I changed class SubClass() -->
#class Subclass(SuperClass) & below line:
MainClass.__init__(self,params) #But technically I don't have anything
#in param in subclass.
def method1():
#some code...
Since sub class's param doesn't have anything, It gives me an error:
self.one=params['ONE']
TypeError: 'int' object has no attribute '__getitem__'
I am not getting:
How can I access all the parameters of super class to sub class in a simplest way? I don't want to pass individual arguments (like self.one, self.two..) to the sub class.
If I am calling third class inside SubClass -> method1 --> Call 3rd class same as passing 'params'. Is it possible?
Is this what you need?
script1.py
class MainClass():
def __init__(self,params):
# Save params for use by a
self.params = params
self.one=params['ONE']
self.two=params['TWO']
...
self.five=params['FIVE']
def a():
s=SubClass(self.params)
s.method1(...)
script2.py
class SubClass(SuperClass):
def __init__(self,params):
MainClass.__init__(self,params)
def method1():
#some code...
You can pass any and all the non-keyword arguments from the subclass's __init__()to the superclass's like this:
class SubClass(SuperClass):
def __init__(self, *params):
MainClass.__init__(self, *params)
...
This same idea will work for other methods, too.