Python Forum
How do I instantiate a class with **kwargs?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How do I instantiate a class with **kwargs?
#1
Consider the following:

class TestClass():

    def __init__(self, param_1 , **kwargs):
        self.param_1 = param_1
        #?? how do I initialise the **kwargs??
        #self.**kwargs = **kwargs gives a SyntaxError
    
    def do_something(self):
        pass

x = TestClass(param_1 = 'a', param_2 = 'b', param_3 = 'c')
print(x.param_1) #a
print(x.param_2) #AttributeError : TestClass object has no attribute 'param_2'
How do I get the final line to work?

Thanks in advance
Reply
#2
Consider not doing that. What is your use case for a class that creates different instance attributes based on the named arguments passed to __init__()?

But if you want to follow this path that likely leads to disaster:
class BadIdea:
    def __init__(self, param_1, **kwargs):
        self.param_1 = param_1
        for key, value in kwargs.items():
            setattr(self, key, value)


class AlsoBadIdea:
    def __init__(self, param_1, **kwargs):
        self.param_1 = param_1
        for key, value in kwargs.items():
            self.__dict__[key] = value


x = BadIdea(param_1="a", param_2="b", param_3="c")
print(x.__dict__, x.param_2)

y = AlsoBadIdea(param_1="a", param_2="b", param_3="c")
print(y.__dict__, y.param_3)
Output:
{'param_1': 'a', 'param_2': 'b', 'param_3': 'c'} b {'param_1': 'a', 'param_2': 'b', 'param_3': 'c'} c
If you want an object that holds arbitrary values that can be referenced by name, us a dictionary. if you want a dictionary that supports attribute-like syntax, look at AttrDict.

https://pypi.org/project/attrdict/

Or do you just want optional arguments?
class TestClass:
    def __init__(self, param_1, param_2=None, param_3=None):
        self.param_1 = param_1
        self.param_2 = param_2
        self.param_3 = param_3

    def __str__(self):
        
        return f"<TestClass param_1={self.param_1} param_2={self.param_2} param_3={self.param_3}>"


print(TestClass(1))
print(TestClass(1, 2))
print(TestClass(1, 2, 3))
Output:
<TestClass param_1=1 param_2=None param_3=None> <TestClass param_1=1 param_2=2 param_3=None> <TestClass param_1=1 param_2=2 param_3=3>
Reply
#3
(Feb-15-2023, 04:21 PM)deanhystad Wrote: Consider not doing that. What is your use case for a class that creates different instance attributes based on the named arguments passed to __init__()?

Actually this (using *args and/or **kwargs) is quite common pattern. Of course one does not simply take whatever you throw in. But it is quite useful, e.g. in inheritance.

https://stackoverflow.com/q/1098549/4046632
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#4
(Feb-15-2023, 05:22 PM)buran Wrote:
(Feb-15-2023, 04:21 PM)deanhystad Wrote: Consider not doing that. What is your use case for a class that creates different instance attributes based on the named arguments passed to __init__()?

Actually this (using *args and/or **kwargs) is quite common pattern. Of course one does not simply take whatever you throw in. But it is quite useful, e.g. in inheritance.

https://stackoverflow.com/q/1098549/4046632
For inheritance, sure:
class A:
    def __init__(self, a, b, c=None):
        self.a = a
        self.b = b
        self.c = c


class B(A):
    def __init__(self, a, b, d=None, **kwargs):
        super().__init__(a, b, **kwargs)
        self.d = d

    def __str__(self):
        return f"<B a={self.a} b={self.b} c={self.c} d={self.d}>"


x = B(1, 2, c=3, d=4)
print(x)
Output:
<B a=1 b=2 c=3 d=4>
Notice that B doesn't really do anything with **kwargs other than pass them along to the __init__() method for A. Also be aware that B() only allows 2 positional arguments. I should write it like this:
class B(A):
    def __init__(self, a, b, *, d=None, **kwargs):d
This lets the user know only the first two arguments are positional.

But that is not what is what the OP is asking about. At least that is not my interpretation. I think the OP wants named arguments in the __init__() method to become attributes in the new instance. That is not very useful because the class creates instances that don't have the same attributes. It does not define an interface. How can you use instances of this class if you don't know what they do or know.
Reply
#5
You can do
class TestClass():
 
    def __init__(self, param_1 , **kwargs):
        self.param_1 = param_1
        vars(self).update(kwargs)
     
    def do_something(self):
        pass
 
Reply
#6
(Feb-15-2023, 04:21 PM)deanhystad Wrote: Consider not doing that. What is your use case for a class that creates different instance attributes based on the named arguments passed to __init__()?

But if you want to follow this path that likely leads to disaster:
class BadIdea:
    def __init__(self, param_1, **kwargs):
        self.param_1 = param_1
        for key, value in kwargs.items():
            setattr(self, key, value)


class AlsoBadIdea:
    def __init__(self, param_1, **kwargs):
        self.param_1 = param_1
        for key, value in kwargs.items():
            self.__dict__[key] = value


x = BadIdea(param_1="a", param_2="b", param_3="c")
print(x.__dict__, x.param_2)

y = AlsoBadIdea(param_1="a", param_2="b", param_3="c")
print(y.__dict__, y.param_3)
Output:
{'param_1': 'a', 'param_2': 'b', 'param_3': 'c'} b {'param_1': 'a', 'param_2': 'b', 'param_3': 'c'} c
If you want an object that holds arbitrary values that can be referenced by name, us a dictionary. if you want a dictionary that supports attribute-like syntax, look at AttrDict.

https://pypi.org/project/attrdict/

Or do you just want optional arguments?
class TestClass:
    def __init__(self, param_1, param_2=None, param_3=None):
        self.param_1 = param_1
        self.param_2 = param_2
        self.param_3 = param_3

    def __str__(self):
        
        return f"<TestClass param_1={self.param_1} param_2={self.param_2} param_3={self.param_3}>"


print(TestClass(1))
print(TestClass(1, 2))
print(TestClass(1, 2, 3))
Output:
<TestClass param_1=1 param_2=None param_3=None> <TestClass param_1=1 param_2=2 param_3=None> <TestClass param_1=1 param_2=2 param_3=3>

Thank you for your feedback and guidance.

I am actually trying to extend the dateutil.rrule class. Specifically I am trying to generate the last Monday and Friday (for example) of each month, alternately. So for example, last Monday of Jan 2023 ( 30 Jan) followed by last Friday of Feb 2023 ( Feb 24), then last Monday of March ( 27 March) etc. To the best of my knowledge neither rrule nor rruleset provides a convenient method to do this (please tell me if I am wrong!) , so I have to try and create a custom class that will allow me to do this using rrule in some way (I am thinking composition, but I am still experimenting so I don't have a concrete implementation yet).

Unfortunately rrule has so many parameters, not all needs to be supplied and not all can be supplied in one call, so I figured the easiest way is to use **kwargs on top of whatever other arguments I need. Like I said I am still experimenting around (so I don't have a concrete implementation yet), and during the experiment I thought this class may need to inspect the parameters supplied to rrule, hence the question.

If you have any broad tips (not asking for actual code) on how to approach this problem, it would be much appreciated.

Thanks to all others that helped.
Reply
#7
Buran was right, you wanted to use kwargs to pass along arguments to a superclass. I totally misread your intentions. Sorry.

Using **kwargs for subclassing works great. Just declare you new named arguments and pass **kwargs to the superclass.
class MyRrule(dateutil.rrule):
    def __init__(self, new_arg=None, **kwargs):
        super().__init__(**kwargs)
        self.new_arg = new_arg
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  kwargs question Jeff_t 8 2,974 Feb-16-2022, 04:33 PM
Last Post: Jeff_t
  Misunderstanding kwargs? Donovan 2 2,280 Aug-04-2020, 08:09 PM
Last Post: Donovan
  **kwargs question DPaul 10 4,340 Apr-01-2020, 07:52 AM
Last Post: buran
  Unpacking dictionary from .xlsx as Kwargs etjkai 5 2,863 Dec-27-2019, 05:31 PM
Last Post: etjkai
  opts vs kwargs Skaperen 4 2,466 Nov-30-2019, 04:57 AM
Last Post: Skaperen
  Python 3.5 Instantiate and manipulate object with multiprocessing jipete 1 4,864 Dec-28-2016, 12:46 AM
Last Post: micseydel
  create dictionary from **kwargs that include tuple bluefrog 2 4,882 Oct-26-2016, 10:24 PM
Last Post: Larz60+
  How do you use *args and **kwargs? Gengar 3 19,708 Sep-20-2016, 04:22 PM
Last Post: Ofnuts

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020