Changing instance attributes of a parent class from a child class - python

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.

Related

How to handle parameters with polymorphism in python?

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.

Is 'self' keyword Mandatory inside the class Methods?

I am python Begineer and i learned that first parameter inside the method should be contain some 'self' keyword but i found the following program runs without self keyword can you explain about this below is my code...
class Student(object):
def __init__(self,name,age):
self.name = name
self.age = age
def get_biggest_number(*age):
result=0
for item in age:
if item > result:
result= item
return result
Sam = Student("Sam",18)
Peter = Student("Peter",20)
Karen = Student("Karen",22)
Mike = Student("Michael",21)
oldest= Student.get_biggest_number(Sam.age,Peter.age,Karen.age,Mike.age)
print (f"The oldest student is {oldest} years old.")
Code you've posted has indentation errors within it, you should first indent methods and it's content, meaning that, methods are within class. On the other hand, self refers to instance, which calls specific method and gives access to the all instance data. For example
student1 = Student('name1', 20)
student2 = Student('name2', 21)
student1.some_method(arg1)
in the last call, behind the scenes student1 is passed for self parameter of the method, meaning that all student1's data is available through self argument.
What you are trying is to use staticmethod, which has no data of the instance and is aimed to logically group class related functions without explicit instance, which does not require self in method definition:
class Student:
...
#staticmethod
def get_biggest_number(*ages):
# do the task here
On the other hand, if you would like to track all student instances and apply get_biggest_number method automatically work on them, you just have to define class variable (rather than instance variable) and on each instance __init__ append new instance to that list:
class Student:
instances = list() # class variable
def __init__(self, name, age):
# do the task
Student.instances.append(self) # in this case self is the newly created instance
and in get_biggest_number method you just loop through Student.instances list which will contain Student instance and you can access instance.age instance variable:
#staticmethod
def get_biggest_number():
for student_instance in Student.instances:
student_instance.age # will give you age of the instance
Hope this helps.
You shouldn't mistake classmethod with instance methods.
In python you can declare a method inside a class as classmethod.
This method takes a reference to the class as the first argument.
class Student(object):
def __init__(self,name,age):
self.name = name
self.age = age
def get_biggest_number(self, *age):
result=0
for item in age:
if item > result:
result= item
return result
#classmethod
def get_classname(cls):
# Has only access to class bound items
# gets the class as argument to access the class
return cls.__name__
#staticmethod
def print_foo():
# has not a reference to class or instance
print('foo')
self in python refers to the instance of the class that is created. Something like this in C# and Java. However there's some differences but in short: when you don't use self as input of a method, actually you're saying that this method does not need any instance, that means this method is a static method and will never use any of class attributes.
In your example we can call get_biggest_number method with not even one instance, for example you can call this method just like this:
Student.get_biggest_number(20,30,43,32)
and the output will be 43.
The self keyword is used to represent an instance (object) of the given class. ... However, since the class is just a blueprint, self allows access to the attributes and methods of each object in python.
class ClassA:
def methodA(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
let say ObjectA is an instance of the class.
Now when ObjectA.methodA(arg1, arg2) is called, python internally converts it as:
ClassA.methodA(ObjectA, arg1, arg2)
The self variable refers to the object itself and the code becomes as:
class ClassA:
def methodA(ObjectA, arg1, arg2):
ObjectA.arg1 = arg1
ObjectA.arg2 = arg2

Should super always be at the top of an __init__ method, or can it be at the bottom?

In most Python examples, when super is used to call a parent class's constructors, it appears at the top.
Is it bad form to have it at the bottom of an init method?
In the examples below, super is at the bottom of A's constructor, but at the top of B's constructor.
class A:
def __init__(self):
# Do some stuff
b = result_of_complex_operation()
super(A, self).__init__(b)
class B:
def __init__(self):
super(A, self).__init__(b)
# Do some stuff
This totally depends on the use case. Consider this.
class Foo():
def __init__(self):
print(self.name)
#property
def name(self):
return self.__class__.__name__
class Bar(Foo):
def __init__(self, name):
self.name = name
super().__init__()
#property
def name(self):
return self.__name
#name.setter
def name(self, name):
self.__name = name
If you'd invoke super() before setting self.name within Bar.__init__ you'd get an AttributeError because the required name has not yet been set.
Is it bad form to have it at the bottom of an init method?
You're asking the wrong question. Regardless of whether it's bad from or not, there are valid use cases for moving the superclass initialization to the bottom of a sub-class's constructor. Where to put the call to the superclass's constructor entirely depends on the implementation of the superclass's constructor.
For example, suppose you have a superclass. When constructing the superclass, you want to give an attribute a certain value depending on an attribute of the subclasses:
class Superclass:
def __init__(self):
if self.subclass_attr:
self.attr = 1
else:
self.attr = 2
As you can see from above, we expect the subclasses to have the attribute subclass_attr. So what does this mean? We can't initialize Supperclass until we've given the subclasses the subclass_attr attribute.
Thus, we have to defer calling the superclass's constructor until we initialize subclass_attr. In other words, the call to super will have to be put at the bottom of a subclasses constructor:
class Subclass(Superclass):
def __init__(self):
self.subclass_attr = True
super(Superclass, self).__init__()
In the end, the choice of where to put super should not be based upon some style, but on what's necessary.

Interpreter throws a run time error when I keep constructor name same as class name?

class animal():
name = 'Tiger'
def animal(self,name):
self.name = name
def show(self):
print(self.name)
/*ins = animal()
ins.show()
# OUTPUT = Tiger
ins = animal('Lion')
Type Error: this constructor takes no arguments
*/
when I keep constructor name as init all works well.I am not getting why there is type error when constructor name is same as class name?
In Python, unlike C++/Java the constructor is not named after the class, you should use init to achieve constructor functionality. Read more about it here
More specifically:
C++ programmers may find it odd that Python classes don't have explicit constructors and destructors. Python classes do have something similar to a constructor: the init method.
As #Ishay mentioned, python don't have named constructor.
Following should solve your problem
class animal():
def __init__(self, name = 'Tiger'):
self.name = name
def show(self):
print(self.name)
ins = animal() #Class instance without animal name
ins.show()
Tiger
ins = animal('Lion') #Class instance with animal name
ins.show()
Lion

How remove a variable that appears in __init__?

If i have this:
class One(object):
def __init__(self, name):
self.name = name
I want to use One but altering the name name and relace it by other
The solution I supposed is inheriting:
class Two(One):
def __init__(self, other):
super(Two, self).__init__(other)
The idea is : How to delete or change the variable names that appears in __init__ ?
There is no relation at all between the name of the parameter passed to __init__ and the name of the instance variable that might eventuality be initialized by that argument. This is only a matter of convention than both are called the same.
Both code fragments below will perform exactly the same:
class One(object):
def __init__(self, name):
self.name = name
class One(object):
def __init__(self, xyz):
self.name = xyz
As about renaming an instance variable, you might do something like that, but this is (very) bad style and has (great) chances to break something in (the base class and/or in any client code that expects a proper One instance):
class Two(One):
def __init__(self, other):
super(Two, self).__init__(other)
self.other = self.name # <- no, seriously,
del self.name # <- don't do that !!!
You can't do what you want, not if you are calling One.__init__ from Two.__init__.
If you want to alter what attributes are set, simply don't call One.__init__() here. Set your own attributes instead:
class One(object):
def __init__(self, name):
self.name = name
class Two(One):
def __init__(self, other):
self.other = other
Now self.name will never be set. This most likely will break the rest of functionality in One, something you probably don't want to do. The rest of the methods in that class are likely to rely on certain attributes having been set.
In OOP terms, if Two is not a special kind of One object, don't inherit from One. If Two is a kind of One object, don't try to make it into something else.

Categories

Resources