rjbez
rjbez

Reputation: 812

Is it possible get a dictionary of passed in parameters similar to kwargs(python)?

I'm currently creating an object like this:

class Obj(object):
    def __init__(self,**kwargs):
        params = ['val1','val2','val3','val4',...]
        for p in params:
            setattr(self,p,kwargs.get(p,None))

I'm doing this so I don't have to do this:

class Obj(object):
    def __init__(self,val1=None,val2=None,val3=None,val4=None,...):
        self.val1=val1
        self.val2=val2
        self.val3=val3
        self.val4=val4
        ...

My question is, can you do a mix of the two? Where I can define the expected parameters yet still loop the parameters to set the attributes? I like the idea of defining the expected parameters because it is self documenting and other developers don't have to search for what kwargs are used.

I know it seems pretty petty but I'm creating an object from some XML so I'll be passing in many parameters, it just clutters the code and bugs me.

I did google this but couldn't find anything, probably because dictionary and kwargs together point to kwarg examples.

UPDATE: To be more specific, is it possible to get a dictionary of passed in parameters so I don't have to use kwargs at all?

Sudo code:

class Obj(object):
    def __init__(self,val1=None,val2=None,val3=None,val4=None,...):
        for k,v in dictionary_of_paramters.iteritems():
            setattr(self,k,v)

Upvotes: 3

Views: 254

Answers (6)

Javier
Javier

Reputation: 62593

You can use the inspect module:

import inspect

def myargs(val1, val2, val3=None, val4=5):
        print inspect.currentframe().f_locals

it shows all the locals available on the current stack frame.

myargs('a','b')
==>  {'val3': None, 'val2': 'b', 'val1': 'a', 'val4': 5}

(note: it's not guaranteed to be implemented on all Python interpreters)

edit: i concur that it's not a pretty solution. what i would do is more like:

def _yourargs(*names):
        "returns a dict with your named local vars"
        alllocs = inspect.stack()[1][0].f_locals
        return {n:alllocs[n] for n in names}

def askformine(val1, val2, val3=None, val4=5):
        "example to show just those args i'm interested in"
        print _yourargs('val1','val2','val3','val4')

class Obj(object):
        "example inserting some named args as instance attributes"
        def __init__(self, arg1, arg2=4):
                 self.__dict__.update(_yourargs('arg1','arg2'))

edit2 slightly better:

def pickdict(d,*names):
    "picks some values from a dict"
    return {n:d[n] for n in names}

class Obj(object):
    "example inserting some named args as instance attributes"
    def __init__(self, arg1, arg2=4):
        self.__dict__.update(pickdict(locals(),'arg1','arg2'))

Upvotes: 2

miku
miku

Reputation: 188034

A kind of an ugly workaround: Inject extra arguments into kwargs and use it where you want to loop over all keyword arguments (PS this is an example usage of the inspect module, but not recommended for production use):

#!/usr/bin/env python

import inspect

def inject_defaults(func):
    """ injects '__defaults' key into into kwargs, 
        so it can be merged with kwargs in the decorated method """

    args, varargs, varkwargs, defaults = inspect.getargspec(func)
    have_defaults = args[-len(defaults):]
    defaults_dict = dict(zip(have_defaults, defaults))

    def fun(*args, **kwargs):
        kwargs['__defaults'] = defaults_dict
        return func(*args, **kwargs)

    return fun

@inject_defaults
def f1(a,b,c, x=1, **kwargs):
    kwargs.update(kwargs['__defaults'])
    del kwargs['__defaults']

    for k, v in kwargs.items():
        # here, x, y and z will appear
        print(k, v)

f1(1, 2, 3, y=3, z=2)
# prints
# ('y', 3)
# ('x', 1)
# ('z', 2)

Upvotes: 0

georg
georg

Reputation: 214969

If you want to obtain the args dict at the very top of your method, before you define any locals, this is as simple as:

class Obj(object):
    def __init__(self,val1=None,val2=None,val3=None,val4=None):
       kwargs = dict(locals())

To read this dict later on, some introspection magic is required:

class Obj(object):
    def __init__(self,val1=None,val2=None,val3=None,val4=None):

       # feel free to add more locals

       loc = dict(locals())
       fun = sys._getframe().f_code
       kwargs = {x:loc[x] for x in fun.co_varnames[:fun.co_argcount]}

You can also make the latter reusable by adding a function like this:

def getargs():
    f = sys._getframe(1)
    return {x:f.f_locals[x] for x in f.f_code.co_varnames[:f.f_code.co_argcount]}

and then:

class Obj(object):
    def __init__(self,val1=None,val2=None,val3=None,val4=None):

       # feel free to add more locals

       kwargs = getargs()

This is cpython-specific, I guess.

Upvotes: 1

Mark Hildreth
Mark Hildreth

Reputation: 43071

Although you won't be able to get the parameters without using kwargs or the inspect module (see other answers), you can do something like this...

class Obj(object):
    def __init__(self, **kwargs):
        self.__dict__.update(**kwargs)

Every object has a dictionary that stores all of the attributes, which you can access via self.__dict__. Then you're just using update to set all of the attributes in that object's internal dictionary.

See this question on some discussion of this method.

Upvotes: 1

Ned Batchelder
Ned Batchelder

Reputation: 375584

There is no nice way to get a dictionary of all the arguments to a function. The **kwargs syntax only collects up the extra keyword arguments, not the ones that match explicit parameters in the function definition.

Upvotes: 1

GautamJeyaraman
GautamJeyaraman

Reputation: 545

Yes you can mix the two. See below:

def method(a, b=1, *args, **kwargs):
    '''some code'''

This is valid. Here:

'a' is a required argument
'b' is a default argument
'args' will have all the non-keyword arguments and
'kwargs' will have all the keyword arguments.

Example:

method(1,2,3,4,5,test=6,test1=7)

This call will have:

a=1
b=2
args=(3,4,5)
kwargs={'test':6,'test1':7}

Upvotes: 0

Related Questions