0

How can I conditionally set an attribute in the __init__ of a class, from a **kwargs parameter?

I know that I can do:

default = 1

class foo():
    def __init__(self, X=None, Y=None):
        self.X = X if X else default  
        self.Y = Y if Y else default

F = foo()
f = foo(X=2, Y=3)

but I want to make this work with a variable-keyword parameter (**kwargs), in order to understand them properly.

These attempts did not work:

default = 1

class foo():
    def __init__(self, **kwargs):
        self.X = default or kwargs['X'] 
        self.Y = default or kwargs['Y']

F = foo()
f = foo(X=2, Y=3)

This way, only the default value is used even if other values are provided. If the order is switched (kwargs['X'] or default etc.), the code raises a KeyError if the keyword arguments are not provided.

default = 1

class foo():
    def __init__(self, **kwargs):
        self.X = value if kwargs["X"] is None else kwargs["X"]
        self.Y = value if kwargs["Y"] is None else kwargs["Y"]

F = foo()
f = foo(X=2, Y=3)

This still raises a KeyError for missing keyword arguments.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Hushm
  • 31
  • 1
  • 7
  • 2
    why do you want to use `**kwargs`? You *know* there are only two possible parameter and you know what their names are, so there is no good reason to use `**kwargs`. If you did want to use `**kwargs` you would need to check `if "X" in kwargs`, or use `.get`, but again, why use this? – juanpa.arrivillaga Jul 10 '23 at 15:55
  • 2
    `self.X = kwargs.get('X', value)` would work: the `dict.get` lets you give a default value – Demi-Lune Jul 10 '23 at 15:56
  • @Demi-Lune It worked fantastically, Thank You very Much. – Hushm Jul 10 '23 at 16:01
  • @juanpa.arrivillaga as stated above I am familiarizing myself with kwags. – Hushm Jul 10 '23 at 16:01
  • 1
    @Hushm then you should learn *when and why to use it*. `def __init__(self, X= None, Y= None):` is the ideal approach here. In some cases, you might use `kwargs`, when you know the name and number of paramters, e.g., in a cooperative multiple inheritance situations, but barring something like that, just use named parameters. Note, you probably want `MyClass(z=42)` to raise an error, since you weren't *expecting* a `z` argument etc etc – juanpa.arrivillaga Jul 10 '23 at 16:02
  • @juanpa.arrivillaga My philosophy is learn how to use it and then learn where to apply it. I mean _maybe just maybe_ I may use it in a very favorable position. I understand your thought train and it's enlightening. – Hushm Jul 10 '23 at 16:04
  • Once the function has been called, `kwargs` is a **perfectly ordinary** `dict` that you use **the same way as any other** dictionary. The issue - as determined by *considering the error message* - is that this dictionary *does not contain* the `X` and `Y` keys if they are not provided by the caller - so *of course* it will fail when the code tries to compare them to `None` or use them with an `or`. – Karl Knechtel Aug 24 '23 at 17:26
  • 1
    (Once you have done this analysis - which [is expected](https://meta.stackoverflow.com/questions/261592) - the problem immediately reveals itself as a duplicate.) – Karl Knechtel Aug 24 '23 at 18:02

1 Answers1

0
class foo():
    def __init__(self, **kwargs):
        self.X =  kwargs.get('X', value) # get() take key_name, default value this opetions 
        self.Y = kwargs.get('Y', value)


# or use normal params with default value 
class foo():
    # change dict to  any type
    def __init__(self, x = None, y = None):
        self.X =  x or value
        self.Y = y or value 


# or better use Keyword Argument with default value if you have x y only 

class foo():
    # change dict to  any type
    def __init__(self, x: dict = None, y: dict = None):
        self.X =  x or value
        self.Y = y or value 

you need also know when should use **kwargs

There are two common cases:

First: You are wrapping another function which takes a number of keyword argument, but you are just going to pass them along:

def my_wrapper(a, b, **kwargs):
    do_something_first(a, b)
    the_real_function(**kwargs)

Second: You are willing to accept any keyword argument, for example, to set attributes on an object:

class OpenEndedObject:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

foo = OpenEndedObject(a=1, foo='bar')
assert foo.a == 1
assert foo.foo == 'bar'

read this if you need know more

Why use **kwargs in python? What are some real world advantages over using named arguments?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Talaat Magdy
  • 199
  • 11