How can I pass **kwargs in __init__() constructor in python? - python

I'm trying to write a program where I am trying to pass **kwargs in init() method. After that
when I m trying to make a instance variable inside the constructor(init() method ) , I cant able to make . How can I do this ?
Here is my code :
class Student:
def __init__(self,**kwargs):
self.name = name
self.age = age
self.salary = salary
def show_name(self):
print("Name is : " + self.name)
def show_age(self):
print("Age is : " + str(self.age))
def show_salary(self):
print(f"Salary of {self.name} is : " + str(self.salary))
st = Student('John',25,15000)
st2 = Student('Doe',25,1500000)
st.show_salary()
st2.show_salary()

**kwargs expects arguments to be passed by keyword, not by position. Once you do that, you can access the individual kwargs like you would in any other dictionary:
class Student:
def __init__(self, **kwargs):
self.name = kwargs.get('name')
self.age = kwargs.get('age')
self.salary = kwargs.get('salary')
def show_name(self):
print("Name is : " + self.name)
def show_age(self):
print("Age is : " + str(self.age))
def show_salary(self):
print(f"Salary of {self.name} is : " + str(self.salary))
st = Student(name='John', age=25, salary=15000)
st2 = Student(name='Doe', age=25, salary=1500000)
st.show_salary()
st2.show_salary()
If you want to pass these arguments by position, you should use *args instead.

kwargs is created as a dictionary inside the scope of the function. You need to pass a keyword which uses them as keys in the dictionary. (Try running the print statement below)
class Student:
def __init__(self, **kwargs):
#print(kwargs)
self.name = kwargs["name"]
self.age = kwargs["age"]
self.salary = kwargs["salary"]
def show_name(self):
print("Name is : " + self.name)
def show_age(self):
print("Age is : " + str(self.age))
def show_salary(self):
print(f"Salary of {self.name} is : " + str(self.salary))
st = Student(name = 'John',age = 25, salary = 15000)
st2 = Student(name = 'Doe',age = 25,salary = 1500000)
st.show_salary()
st2.show_salary()

Though you can do this as some of the answers here have shown, this is not really a great idea (at least not for the code you are showing here). So I am not going to answer the subject line question you have asked, but show you what the code you seem to be trying to write should be doing (and that is not using kwargs). There are plenty of places where using kwargs is the best solution to a coding problem, but the constructor of a class is usually not one of those. This is attempting to be teaching, not preaching. I just do not want others coming along later, seeing this question and thinking this is a good idea for a constructor.
The constructor for your class, the __init__(), generally should be defining the parameters that it needs and expects to set up the class. It is unlikely that you really want it to take an arbitrary dictionary to use as its parameter list. It would be relatively rare that this is actually what you want in your constructor, especially when there is no inheritance involved that might suggest you do not know what the parameters are for some reason.
In your __init__() itself you clearly want the parameters name, age and salary, yet without them in the parameter list it is not clear to the caller that you do. Also, your usage of it does not seem to imply that is how you expect to use it. You call it like this:
st = Student('John',25,15000)
and so you do not even seem to want named parameters.
To handle the call structure you have shown the __init__() would look like this:
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
If you want to be be able to call it without some parameters such that it uses defaults for the ones left out, then it should be like this:
def __init__(self, name=None, age=None, salary=None):
self.name = name
self.age = age
self.salary = salary
It seems very unlikely that the kwargs approach is really what you want here, though obviously you can code it that way as other answers have shown.
Perhaps you are just trying to figure out how to use kwargs, and that is fine, but a different example would be better if that is the case.

Related

Problem regarding inheritance in python classes

I am trying to inherit methods from one a base class to another class, specifically the __str__ method, where I only need to change it a little by adding a note.
Something like this.
class Person:
def __init__(self, name=None):
self.name = name
def __str__(self):
return ' Name: ' + str(self.name)
And my second class.
class AnnotatedPerson(Person):
def __init__(self, name=None, note=None):
super().__init__(name=None)
self.note = note
def __str__(self):
return ' Name: ' + str(self.name) + 'Note:' + str(self.note)
However this is not working, when printing print(AnnotatedPerson.__str__()) on an object I only get the part note and the name part in the __str__ method is set to None for objects that do have a value.
What is my mistake?
This is the problem:
class AnnotatedPerson(Person):
def __init__(self, name=None, note=None):
super().__init__(name=None)
You are passing None as the value of the name argument, instead of passing through the argument you got. You just need:
super().__init__(name)
This is an important point. When you say
...(name=name,...)
The first one is the name of an argument in the function being called. The second one is the name of a local variable in the function doing the calling. So:
...(name=None,...)
doesn't mention the local variable at all. It just forces a value into the function being called.
You've been pass None to variables, soo you have no results from them, below the code i'm using using the string repr from Person.
class AnnotatedPerson(Person):
def __init__(self, name=None, mother=None, father=None, born=None, died=None, note=None) -> None:
super().__init__(name, mother, father, born, died) # pass the arguments directly
self.note=note
def __str__(self) -> str:
return f'{super().__str__()} Note: {self.note}' # Here you have to pass only string representation from super().__str__() and add the note variable
if __name__ == "__main__":
obj1 = AnnotatedPerson("Gerson", "Maria", "José", "19/12/1975","02/02/2022","Died like a wild horse")
print(obj1)
#Name: Gerson Note: Died like a wild horse
Here a thread with a semelhant question:
python multiple inheritance passing arguments to constructors using super

Trouble understanding classes in python

I am a beginner in python and I am having trouble understanding classes. I have a task that requires me to create a class that should return a students information e.g. name, id, age and marks. I have made a class but am having trouble with the output and keep on getting an attribute error:
print("Student: " + self.name + " ID: " + self.ID + " Age: " + self.age + " Mark: " + self.mark)
AttributeError: 'Student' object has no attribute 'age'
I was wondering if someone could explain what I am doing wrong here as I am quite lost.
Rest of the code:
import random
class Student:
def __init__(self, name, ID):
self.name = name
self.ID = ID
def setAge(self, age):
self.age = age
self.age = random.randint(0, 100)
def setMarks(self, marks):
self.marks = marks
self.marks = random.randint(0, 100)
def Display(self):
print("Student: " + self.name + " ID: " + self.ID + " Age: " + self.age + " Mark: " + self.mark)
student = Student("John", "ID123")
student.Display()
You didn't call student.setMarks() and student.setAge() , so marks and age attributes are not created in object yet.
The solution is to call these two methods before calling student.Display()
Python objects are a container that has attributes you can set. If you don't set an attribute but try to read it, you get an AttributeError, meaning that the attribute you are looking for does not exist.
Currently, student = Student(...) calls Student.__init__, which assigns the name and ID attribute of the object. You never call student.setAge or student.setMarks, so your object's age and marks attributes are never set and can not be accessed.
It is traditional to assign default values in the __init__ method if you want to generally avoid unexpected crashes like that.
Another thing is that rather having getter and setter methods, as Java would, for example, Python encourages the use of properties. Properties are objects in the class template that can be accessed like a normal attribute, but allow you to run arbitrary code in place of the access and assignment operators.
Putting all that together, you could write something like
class Student:
def __init__(self, name, ID, age=None, marks=None):
self.name = name
self.ID = ID
self.age = random.randint(0, 100) if age is None else age
self.marks = random.randint(0, 100) if marks is None else marks
#property
def marks(self):
return self._marks
#marks.setter
def marks(self, value):
# Example of a check you could do
if not isinstance(value, int):
raise TypeError('Marks must be an integer')
In your example, you try to access the variable, before the assignment. You would actually have to call student.setAge and student.setMarks with arguments.
On the other note, in your function setAge you instantly overwrite the value, so consider removing either first or second assignment:
def setAge(self, age):
self.age = age # first assignment
self.age = random.randint(0, 100) # second assignment
The age attribute is not set because it was never assigned:
student = Student("John", "ID123")
student.setAge(30) # John is 30 years old.
student.Display()
def __init__() works like a constructor. Try to include age attribute inside the __init__() function, call setAge() from __init__(), or setAge() explicitly before calling Display().
Good luck :)
A simpler version for your code:
import random
class Student:
def __init__(self, name, ID):
self.name = name
self.ID = ID
self.age = random.randint(0, 100)
self.marks = random.randint(0, 100)
def Display(self):
print("Student: " + str(self.name) + " ID: " + str(self.ID) + " Age: " + str(self.age) + " Mark: " + str(self.marks))
student = Student("John", "ID123")
student.Display()

Python3 Class method inputs; clean solution

I'am using more class based programs, however in some cases it's not handy to provide all self.paramets into a class.
In those cases I want to use a regular input into a function in a class. I figured out a way to achieve both inputs, let me show this in following script:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def myfunc(a):
if (type(a) == str):
name = a
else:
name = a.name
print("Hello my name is " + name)
p1 = Person("John", 36)
p1.myfunc()
print("---------------------")
Person.myfunc("Harry")
Output:
Hello my name is John
---------------------
Hello my name is Harry
First, the name is initialized by the classes self.params.
Second, the name is provided in the method within the class as a string.
So a type check is necessary.
However I don't think this is a clean approach, because when I have >30 methods I need to implement these type checks again, including upcoming type-error results.
Does anyone know a better approach?
The simplest solution is to implement a __str__ method for your class. This method will be called whenever something tries to convert an instance of the class to a string.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return self.name
p = Person('Jane', 25)
print('Hello', p)
'Hello Jane'

Arguments to __init__ when using a subclass and super

I'm spending a nice Saturday looking a bit deeper into Python objects, and had a simple question that isn't mindblowing but kinda curious.
Say I have a base class and a subclass as follows:
class Person:
def __init__(self, first, last):
self.firstname = first
self.lastname = last
def __str__(self):
return self.firstname + " " + self.lastname
class Employee(Person):
def __init__(self, first, last, staffnum):
super().__init__(first, last)
self.staffnumber = staffnum
x = Person("Marge", "Simpson")
y = Employee("Homer", "Simpson", "1007")
I'm looking at the Employee class.
Given:
we are using super() and it has arguments in it,
why in python does the __init__still require us to type first, last?
Shouldn't this be inferred from our use of super? Seems like extra repetitive typing. What's the reasoning for the author having to do this?
The reason is so you can further customize subclasses. In your example, when you still want to input first and last it is more of a hassle because you have to type it twice, like this:
class Employee(Person):
def __init__(self, first, last):
super().__init__(first, last)
emp1 = Employee("Bob", "Jones")
However, you might want to autofill some of those values. In this example maybe a FamilyMember class where the last name is common.
class FamilyMember(Person):
def __init__(self, first):
super().__init__(first, last="Erikson")
fm1 = FamilyMember("Paul")
In this case you would only need to input the first variable for a FamilyMember and the last variable would be automatically filled in.

Object with __init__ in python

I have written some python code:
class key(object):
def __init__(self,name):
object.__init__(self,age)
this.name = name
this.age = age
def somefunction(self):
print "yay the name is %d" % self.name
baby = key('radan',20)
baby.somefunction()
When I create an instance of key with baby = key('radan',20), I got a TypeError. I don't know why I am getting this error. Is it because of object.__init__(self,age)?
If yes, please help me in explaining why we use object.__init__(self,age) and what the purpose of that is and help me solve this code.
Some pointers:
class Key(object): # in Python, classes are usually named with a starting capital
def __init__(self, name, age): # name all your arguments beside self
self.name = name # use self.name, not this.name
self.age = age
def somefunction(self):
print "yay the name is %s" % self.name
baby = Key('radan',20)
baby.somefunction()
# output: yay the name is radan
Actually, you can can name the self instance parameter whatever you like in Python (even this), but it makes the code harder to read for other people, so just use self.
You don't have to use object.__init__(self, name, age) here. If you remove that line and implement the changes above, your code will work just fine.
Your code contains several errors:
class key(object):
def __init__(self, name, age): # where's your age?
self.name = name # no need to call object.__init__
self.age = age # there is no such thing called "this", use self
def somefunction(self):
print "yay the name is %s" % self.name # use %s as the comment says
baby = key('radan', 20)
baby.somefunction()
output:
>>> baby = key('radan', 20)
>>> baby.somefunction()
yay the name is radan
When you do baby = key('radar', 20) you are actually passing three arguments: the instance, the name and the age. However your initialiser is defined to take exactly two arguments so you get a TypeError.
self is the argument implicitly passed when referring to an instance of an object.
For your __init__ function, I would just do:
def __init__(self, name, age):
self.name = name
self.age = age
So we can assign the arguments passed as attributes to the current instance, conventionally called self.
It makes no sense here to call object.__init__ at all, just remove that line.
Apart from that, everything works fine (except use %s instead of %d).
Testing:
>>> baby = key('radan', 20)
>>> baby.somefunction()
yay the name is radan

Categories

Resources