Search code examples
python-3.xpythonconventions

Python 3 object construction: which is the most Pythonic / the accepted way?


Having a background in Java, which is very verbose and strict, I find the ability to mutate Python objects as to give them with fields other than those presented to the constructor really "ugly".

Trying to accustom myself to a Pythonic way of thinking, I'm wondering how I should allow my objects to be constructed.

My instinct is to have to pass the fields at construction time, such as:

def __init__(self, foo, bar, baz=None):
    self.foo = foo
    self.bar = bar
    self.baz = baz

But that can become overly verbose and confusing with many fields to pass. To overcome this I assume the best method is to pass one dictionary to the constructor, from which the fields are extracted:

def __init__(self, field_map):
    self.foo = field_map["foo"]
    self.bar = field_map["bar"]
    self.baz = field_map["baz"] if baz in field_map else None

The other mechanism I can think of is to have the fields added elsewhere, such as:

class Blah(object):

    def __init__(self):
        pass

...

blah = Blah()
blah.foo = var1

But as that feels way too loose for me.

(I suppose the issue in my head is how I deal with interfaces in Python...)

So, to reiterate the question: How I should construct my objects in Python? Is there an accepted convention?


Solution

  • The first you describe is very common. Some use the shorter

    class Foo:
       def __init__(self, foo, bar):
           self.foo, self.bar = foo, bar
    

    Your second approach isn't common, but a similar version is this:

    class Thing:
       def __init__(self, **kwargs):
           self.something = kwargs['something']
           #..
    

    which allows to create objects like

    t = Thing(something=1)
    

    This can be further modified to

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

    allowing

    t = Thing(a=1, b=2, c=3)
    print t.a, t.b, t.c # prints 1, 2, 3
    

    As Debilski points out in the comments, the last method is a bit unsafe, you can add a list of accepted parameters like this:

    class Thing:
        keywords = 'foo', 'bar', 'snafu', 'fnord'
        def __init__(self, **kwargs):
            for kw in self.keywords:
                setattr(self, kw, kwargs[kw])
    

    There are many variations, there is no common standard that I am aware of.