Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python __init__ setattr on arguments?

It seems that often __init__ methods are similar to this:

def __init__(self, ivar1, ivar2, ivar3):
    self.ivar1 = ivar1
    self.ivar2 = ivar2
    self.ivar3 = ivar3

Is there someway to turn the arguments into a list (without resorting to *args or **kwargs) and then using setattr to set the instance variables, with the name of the parameter and the argument passed? And maybe slice the list, e.g. you'd need to at least slice it to [1:] because you don't want self.self.

(actually I guess it would need to be a dictionary to hold the name and value)

like this:

def __init__(self, ivar1, ivar2, ivar3, optional=False):
    for k, v in makedict(self.__class__.__init__.__args__): # made up __args__
        setattr(self, k, v)

Thanks!

Responding to Unknown's answer, I found this to work:

Class A(object):
    def __init__(self, length, width, x):
        self.__dict__.update(dict([(k, v) for k, v in locals().iteritems() if k != 'self']))

or

Class A(object):
    def __init__(self, length, width, x):
        self.__dict__.update(locals())
        del self.__dict__['self']

Not too bad..

like image 855
mk12 Avatar asked Sep 14 '09 02:09

mk12


People also ask

What does __ Setattr __ do in Python?

Python's magic method __setattr__() implements the built-in setattr() function that takes an object and an attribute name as arguments and removes the attribute from the object. We call this a “Dunder Method” for “Double Underscore Method” (also called “magic method”).

What is Setattr () and Getattr () used for?

Python setattr() and getattr() goes hand-in-hand. As we have already seen what getattr() does; The setattr() function is used to assign a new value to an object/instance attribute.

What is the usage of Setattr () function?

The setattr() function sets the value of the specified attribute of the specified object.

What is use of Setattr () method in inheritance?

What is the use of the setattr() method in inheritance? The use case of setattr() in inheritance is the same, i.e., to assign value to the attributes of an object.


6 Answers

Here you go. Yes this is an ugly evil hack. Yes the object needs a __dict__ variable. But hey, its a neat little one liner!

def __init__(self):
    self.__dict__.update(locals())

The constructor can take any type of arguments.

class test(object):
    def __init__(self, a, b, foo, bar=5)...

a = test(1,2,3)
dir(a)

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'foo', 'bar', 'self']

It will also include self, but you can easily delete that or make your own update function that ignores self.

like image 165
Unknown Avatar answered Sep 28 '22 19:09

Unknown


You could use inspect.getargspec and encapsulate it as a decorator. The lookup of optional and keyword arguments is a bit tricky, but this should do it:

def inits_args(func):
    """Initializes object attributes by the initializer signature"""
    argspec = inspect.getargspec(func)
    argnames = argspec.args[1:]
    defaults = dict(zip(argnames[-len(argspec.defaults):], argspec.defaults))
    @functools.wraps(func)
    def __init__(self, *args, **kwargs):
        args_it = iter(args)
        for key in argnames:
            if key in kwargs:
                value = kwargs[key]
            else:
                try:
                    value = args_it.next()
                except StopIteration:
                    value = defaults[key]
            setattr(self, key, value)
        func(self, *args, **kwargs)
    return __init__

You can then use it like this:

class Foo(object):
    @inits_args
    def __init__(self, spam, eggs=4, ham=5):
        print "Foo(%r, %r, %r)" % (self.spam, self.eggs, self.ham)
like image 41
Ants Aasma Avatar answered Sep 28 '22 18:09

Ants Aasma


There is no good way to get the arguments as a list if they are specified individually in the function signature. You can probably do something with inspect or frame hacks, but that will be uglier than simply spelling it out as you have done.

like image 36
Ned Batchelder Avatar answered Sep 28 '22 18:09

Ned Batchelder


Try inspect.getargspec:

In [31]: inspect.getargspec(C.__init__)

Out[31]: ArgSpec(args=['self', 'ivar1', 'ivar2', 'ivar3', 'optional'],

                 varargs=None, keywords=None, defaults=(False,))
like image 44
Cristian Ciupitu Avatar answered Sep 28 '22 18:09

Cristian Ciupitu


See if the new namedtuple (new in Python 2.6) from the collections module might work for you.

like image 40
PaulMcG Avatar answered Sep 28 '22 18:09

PaulMcG


You can do it using introspection of the arguments, but the code is going to be longer than the code you try to replace. Especially if you are handling kw, which you may have to do.

This short code works in most cases (improved from Unknowns example):

>>> class Foo:
...   def __init__(self, labamba, **kw):
...       params = locals().copy()
...       del params['self']
...       if 'kw' in params:
...           params.update(params['kw'])
...           del params['kw']
...       self.__dict__.update(params)

But it's an ugly hack, making code less readable for no particular reason except laziness, so don't do it. And also, how often do you really have classes that have more than 5-6 init parameters?

like image 28
Lennart Regebro Avatar answered Sep 28 '22 20:09

Lennart Regebro