tom
tom

Reputation: 2299

A constructor that only accepts named parameters matching its properties

I'd like to have a class in Python 2.7 where:

  1. I can pass to the constructor one or more named parameters, which initialize their corresponding properties
  2. If any of those named parameters is not a specified property of the class, an exception is raised
  3. Each property for which there is no named parameter passed at construction has a value of None

I have something working, but I suspect there must be an easier and/or more pythonic way - is there?

class Foo:
   bar = None
   baz = None
   def __init__(self, **kwargs):

      for k in kwargs:
         getattr(self, k)

      vars(self).update(kwargs)

then I can do

>>> f = Foo(bar = 3)
>>> f.bar
3
>>> not f.baz
True

and if I do

>>> f = Foo(bar = 3, bazz = 5) # 'bazz' is a typo

..then an Exception is raised, which is what I want, because it caught the 'bazz' typo.

So it seems I have something working, but I don't know what I don't know, so: is there a better /easier / more pythonic way?

Upvotes: 2

Views: 3002

Answers (1)

chepner
chepner

Reputation: 532053

First, your class should explicitly extend object in Python 2.

class Foo(object):

    def __init__(self, **kwargs):
        for arg in ["bar", "baz"]
            value = kwargs.pop(arg, None)
            setattr(self, arg, value)

        if kwargs:
            invalid_args = ", ".join(kwargs)
            raise ValueError("Invalid keyword argument(s): %s" % (invalid_args,)

In __init__, simply use pop to retrieve the expected keyword arguments and remove them from the dict. If any arguments remain, raise an error indicating which extra arguments were found.


Unless you have a lot of variables to set, I find it more readable to skip the loop and set each variable individually.

def __init__(self, **kwargs):
    self.bar = kwargs.pop("bar", None)
    self.baz = kwargs.pop("baz", None)
    if kwargs:
        ...

Upvotes: 2

Related Questions