Search code examples
pythoninitkeyword-argument

using **kwargs in __init__


I am running into the following problem:

class Student():
    def __init__(self,firstname, lastname, **kwargs):
        self.firstname = firstname
        self.lastname = lastname

student1 = Student("John" ,"Johnson" , {"Nationality": "Middle-Earth" , "Sports": ["volleyball","skiing"]})

I want to be able to create students that all have a first and a last name, but only some have information about nationality or sports. (Actually I am experimenting with reconstructing objects from json files.) So I want to turn kwarg key into instance variable names, like this pseudo-code

for key,value in kwargs:
   self.key = value

the above is pseude-code, but is there a way to do this in correct python? in other words: derive instance variable names from kwargs rather than all predefined.

I know this won't result in errorproof code etc..., but I want to know if it can be done.


Solution

  • You can use this in the __init__ method:

    for k, v in kwargs.items():
        setattr(self, k, v)
    

    However, this is not recommended for code than needs to be reusable. If there are typos in the keyword parameters, you won't catch them. Code-checking tools such as pylint will not recognize class attributes initialized like that. You can use the following pattern (especially if there are many kewyord arguments):

    class Student:
        def __init__(self, firstname, lastname, **kwargs):
            self.firstname = str(firstname)
            self.lastname = str(lastname)
            self.nationality = None
            self.sports = None
    
            for k, v in kwargs.items():
                if k in self.__dict__:
                    setattr(self, k, v)
                else:
                    raise KeyError(k)
    
    # this works
    s=Student('John', 'Doe', sports=['socker'])
    
    # this will give an error
    s=Student('John', 'Doe', sprots=['socker'])