57

In Python, say I have some class, Circle, that inherits from Shape. Shape needs x- and y-coordinates, and, in addition, Circle needs a radius. I want to be able to initialize Circle by doing something like,

c = Circle(x=1., y=5., r=3.)

Circle inherits from shape, so I need to use named arguments to __init__, because different classes require different constructors. I could manually set x, y, and r.

class Shape(object):
    def __init__(self, **kwargs):
        self.x = kwargs['x']
        self.y = kwargs['y']

class Circle(Shape):
    def __init__(self, **kwargs):
        super(Circle, self).__init__(**kwargs)
        self.r = kwargs['r']

or, I could have the attributes of my Circle set automatically using self.__dict__.update(kwargs)

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

class Circle(Shape):
    def __init__(self, **kwargs):
        super(Circle, self).__init__(**kwargs)

The advantage of this is that there's less code and I don't need to maintain boilerplate like self.foo = kwargs['foo']. The disadvantage is that it isn't obvious which arguments are needed for Circle. Is this considered a cheat or is this good style (as long as the interface to Circle is well-documented)?


Thanks, everyone, for your thoughtful responses. The self.__dict__.update(**kwargs) hack has been useful for me in experimenting with organizing my code, but I'll make sure that I replace that with properly passing arguments explicitly and doing clear error checking in production code.

3

4 Answers 4

33
class Shape(object):
    def __init__(self, x=None, y=None):
        self.x = x
        self.y = y

class Circle(Shape):
    def __init__(self, r=None, **kwargs):
        super(Circle, self).__init__(**kwargs)
        self.r = r

And this is it. Don't use **kwargs when you don't really need them.

Is this considered a cheat or is this good style (as long as the interface to Circle is well-documented)?

When you have a choice between writing a simple, understandable code and headache code + nice docstrings, you actually don't have any choices, you just go and write simple, self-documented code:)

4
  • there are plenty of perfectly reasonable times to use **whatever especially when you are overloading other classes Commented Mar 15, 2012 at 21:27
  • @StevenRumbalski Yeah, I had a filling that something is missed:) Commented Mar 15, 2012 at 21:36
  • 2
    For this solution, be aware that all classes in this chain of inheritance will not have access to the keyword values that are stripped from **kwargs in this way (in the child classes). For example, Shape cannot define an r parameter and expect to get it, without modifying the __init__ method of Circle. Commented Mar 15, 2012 at 23:10
  • I prefer this solution because it keeps me from having to mention x explicitly in every __init__() all the way down the class hierarchy, while ensuring that the right arguments are present at the right level. Commented Mar 16, 2012 at 4:46
20

I would say that the first method is definitely preferable, because explicit is better than implicit.

Consider what would happen if you made a typo when initializing a Circle, something like Circle(x=1., y=5., rr=3.). You want to see this error immediately, which would not happen with __dict__.update(kwargs).

19

If you wish to assign automatically, I suggest the following approach:

def __init__(self, **kwargs):
    for key, value in kwargs.iteritems():
        setattr(self, key, value)

which, in terms of style, is somewhere in between writing it explicitly and hacking it yourself using self.__dict__.

4
  • 1
    I'd much rather see self.__dict__.update(kwargs) than this (probably less efficient and) longer way to achieve the same result.
    – Chris Lutz
    Commented Mar 15, 2012 at 21:47
  • 6
    This would have the advantage of working with descriptors. Working directly with __dict__ is a bad idea for that reason alone. Commented Mar 15, 2012 at 21:50
  • 3
    Could you help me understand why this works better with descriptors?
    – Eli Rose
    Commented May 29, 2014 at 4:14
  • 3
    My understanding, after reading docs.python.org/3/howto/descriptor.html , is that Chris Morgan (~2) is promoting Simeon's answer because it would work better with classes BASED on your class which DO over-ride the default get/set methods and thus become data descriptors. Using setattr would be compatible with their over-rides, while using the dict directly would bypass any such overrides. Commented Oct 8, 2016 at 3:53
4

If you wanted it to be more obvious you could have Circle.__init__ perform some sanity checks on the arguments. Presumably you'd check to make sure all the arguments are there, and possibly raise errors for meaningless arguments.

You could probably even make a decorator or helper function in Shape to do this for you. Something like this:

class Circle(Shape):
    def __init__(self, **kwargs):
        self.check(kwargs, 'x', 'y', 'r')
        super(Circle, self).__init__(**kwargs)

.check would be implemented in Shape and essentially just verifies that all the arguments are in kwargs, and possibly that no extra ones are (sorry, no code for that one - you can figure it out on your own). You could even have subclasses overload it to check for optional arguments, which you may want to handle differently than other arguments (i.e. give them a default value that wouldn't otherwise be assigned in Shape.__init__.

Otherwise, if you document your interface, and it works the way it's documented, it's always alright. Anything else you do to make it work the way we "expect" it to (throwing exceptions for incorrect arguments) is a bonus.

2
  • How do you know that ".check is implemented in Shape". Are you the OP's teacher? Commented Mar 15, 2012 at 21:39
  • 2
    @StevenRumbalski - I'd be the youngest computer science teacher ever if I was. What I meant was that the OP could implement Shape.check and then use it in all the subclasses. I'll clarify that.
    – Chris Lutz
    Commented Mar 15, 2012 at 21:44

Not the answer you're looking for? Browse other questions tagged or ask your own question.