pawel
pawel

Reputation: 1069

Pack function arguments into a dictionary - opposite to **kwargs

I'm trying to do something opposite to what **kwargs do and I'm not sure if it is even possible. But knowing Python it probably is :-). I want to have all attributes clearly designed in my method (for auto completion, and ease of use) and I want to grab them all as, lets say a dictionary, and pass them on further.

class Foo(object):
   def __init__(self, a=1, b=2):
      inputs = grab_function_inputs_somehow()
      self.bar(**inputs)
   
   def bar(self, *args, **kwargs):
      pass

The normal thing to do is to assign each input into an object parameter but I don't want to do that for all classes. I was hoping for a way to wrap it to a method that can be inherited.

Upvotes: 5

Views: 1939

Answers (2)

Marcin
Marcin

Reputation: 238229

You can create the dict with variables using locals(). For example:

class Foo(object):
    def __init__(self, a=1, b=2):    
        inputs = locals()
        del inputs['self'] # remove self variable
        print(inputs)

   
f = Foo() 

Results in print out:

{'b': 2, 'a': 1}

Upvotes: 8

jedwards
jedwards

Reputation: 30210

It's possible, but requires a little tweak to your code:

class Foo(object):
    def __init__(self, **inputs):
        # Have to set your defaults in here
        inputs['a'] = inputs.get('a', 1)
        inputs['b'] = inputs.get('b', 2)
        # Now the rest of your code, as you expected
        self.bar(**inputs)

    def bar(self, *args, **kwargs):
        print("bar got: %s" % kwargs)


# No arguments, use defaults
Foo()           # bar got: {'a': 1, 'b': 2}
# Arguments provided
Foo(a=3, b=4)   # bar got: {'a': 3, 'b': 4}

So, instead of providing default arguments in the function definition, you're ensuring that the keys you expect exist, either with the provided argument, or with the defaults you pass as the second argument to <dict>.get().

Edit __init__ could also be written as:

def __init__(self, **inputs):
    # Have to set your defaults in here
    if 'a' not in inputs: inputs['a'] = 1
    if 'b' not in inputs: inputs['b'] = 2
    # Now the rest of your code, as you expected
    self.bar(**inputs)

# or

def __init__(self, **inputs):
    # Have to set your defaults in here
    args = {'a': 1, 'b':2}
    args.update(inputs)
    # Now the rest of your code, as you expected
    self.bar(**args)

Depending on the number of default arguments you have, the last option may be preferred.

Upvotes: -1

Related Questions