9

Constructors in Python often look like this:

class SomeClass:
    def __init__(self, a, b = None, c = defC):
        self.a = a
        self.b = b or []
        self.c = c

Is there a shortcut for this, e.g. to simply define __init__(self,**kwargs) and use the keys as properties of self?

Tobias Kienzler
  • 25,759
  • 22
  • 127
  • 221
  • 1
    I know one shouldn't have to have so many different parameters in a constructor that can't be logically collected in a dictionary, this question is more out of curiosity – Tobias Kienzler Aug 30 '12 at 06:30

3 Answers3

9

One idiom I've seen is self.__dict__.update(locals()). If you run it right at the beginning of the method, this will update the object's dictionary with the arguments (since those are the only locals at the beginning of the method). If you pass in **kwargs you can do self.__dict__.update(**kwargs).

Of course, this is a fragile approach. It can lead to puzzling bugs if you accidentally pass in an argument that masks an existing attribute. For instance, if your class has a .doSomething() method and you accidentally pass doSomething=1 to the constructor, it will override the method and cause an error later if you try to call that method. For this reason it's better not to do this except in certain trivial cases (e.g., some sort of proxy object whose only purpose is to serve as a "bag" holding a few attributes).

BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • I see, so I should avoid this in most circumstances, and then at least use some function that yields only the `**kwargs` that are already in `self.__dict__`. Related: [Is `self.__dict__.update(**kwargs)` good or poor style?](http://stackoverflow.com/q/9728243/321973) – Tobias Kienzler Aug 30 '12 at 06:40
6

Yes:

class SomeClass:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
gecco
  • 17,969
  • 11
  • 51
  • 68
  • It would work, but as [BrenBarn](http://stackoverflow.com/a/12191118/321973) and [gnibbler](http://stackoverflow.com/a/12191200/321973) suggest, there can be very inconvenient side-effects – Tobias Kienzler Aug 30 '12 at 07:31
  • 1
    No! The inconvenient side effect does only occur if you chose the `locals()` solution. It does not appear when using `**kwargs`! – gecco Aug 30 '12 at 09:42
  • 1
    True, I referring to the possibility to do something like `brokenInstance = SomeClass(a_method = [])` when `a_method` was a method previously defined in `SomeClass`, i.e. one could accidentally modify class properties that aren't supposed to be modified. Some whitelist or blacklist mechanism would need to be implement to prevent that – Tobias Kienzler Aug 30 '12 at 10:17
6

One problem with

self.__dict__.update(locals())

is that it includes self, so you get self.self. It would be better to filter self out of locals()

eg.

vars(self).update((k,v) for k,v in vars().items() if k != 'self')

You can defend against accidentally overwriting methods with this variation

vars(self).update((k,v) for k,v in vars().items()
                   if k != 'self' and k not in vars(self))

If you don't want it to fail silently, you could also check beforehand like this

if any(k in vars(self) for k in vars()):
    raise blahblah
vars(self).update((k,v) for k,v in vars().items() if k != 'self')
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • Interesting point. So maybe something like `vars(self).update((k,v) for k,v in vars(self).items())` would make sure that only already existing properties can be set, and then use some code to warn (or even raise an exception) about the attempt to set a property that should not be set. Although I guess this would still allow to redefine class methods as [BrenBarn stated](http://stackoverflow.com/a/12191118/321973) – Tobias Kienzler Aug 30 '12 at 06:44
  • @Tobias, I added an example to avoid overwriting class methods – John La Rooy Aug 30 '12 at 06:49
  • Thanks! It's too early in the morning - I smiled when pronouncing `any(k` m-/ Ok, so with some slight modification one could even define default arguments. But this really indicates that one should stick to `self.var1 = var1` in most cases – Tobias Kienzler Aug 30 '12 at 07:05
  • 1
    @Tobias, yes usually explict is better. How else could you know to translate `None` into `[]` for `self.b`? – John La Rooy Aug 30 '12 at 07:17
  • That alone is probably the shortest reason to not do this. Although some `for k in kwargs.keys()` and a keyword white-/blacklist and [`pop`ing from a copy of `**kwargs`](http://stackoverflow.com/a/1098577/321973) might provide some flexibility and remain readable in some way. But then again, I can't think of a real-life application of more than, say, ten named arguments of one constructor, where it wouldn't be more adequate to define different constructors using `@classmethod`... – Tobias Kienzler Aug 30 '12 at 07:22