4

As a Python programmer, I frequently declare classes similar to

class Foo:
  def __init__(self, attr1, attr2, attr3, attr4, attr5, attr6, attr7, attr8, attr9):
    self.attr1 = attr1
    self.attr2 = attr2
    ...
    self.attr9 = attr9

    # init your class

The problem is that I do that over and over, and it feels very inefficient. In C++ the way to do this would be

class Foo {
  public:
    Foo(int, int, int, int, int);
    
  private:
    int attr1, attr2, attr3, attr4, attr5;
    
};

Foo::Foo(int attr1, int attr2, int attr3, int attr4, int attr5) : 
    attr1(attr1), attr2(attr2), attr3(attr3), attr4(attr4), attr5(attr5) {
    // init your class
}

Which I feel like it's slightly more efficient. My question is: is there a standard way to avoid having to attribute the arguments to the class for every argument? I can think of ways to program it, for example

def attribute_setter(*arg_names):
  def wrapper(func):
    def setter_init(self, *args, **kwargs):
      for i, name in enumerate(arg_names):
        if i < len(args):
          setattr(self, name, args[i])
        elif name in kwargs:
          setattr(self, name, kwargs[name])
      func(self, *args, **kwargs)
    return setter_init
  return wrapper

class Foo:
  @attribute_setter('attr1', 'attr2', 'attr3', 'attr4', 'attr5', 'attr6', 'attr7', 'attr8', 'attr9')
  def __init__(self, *args, **kwargs):
    # init your class
    pass

Which yields

>>> foo = Foo(1, 2, 3, 4, 5, attr6=6, attr7=7, attr8=8, attr9=9)
>>> foo.attr1
1
>>> foo.attr9
9

With a lot more error handling, this decorator might become safe to use, but my concern is the readability of the code and making sure I'm not violating any good practice principle.

2
  • why do you need a decorator for setting instance variables? maybe usedataclass or namedtuple Commented Feb 19, 2022 at 21:32
  • C++ isn't really any more "efficient"; the typing is just spread out across more syntactic constructs. I would be more concerned about the fact that you have so many separate arguments to your class. Commented Feb 19, 2022 at 22:10

1 Answer 1

9

This is exactly what the dataclasses module is for. You use it like this:

from dataclasses import dataclass


@dataclass
class Foo:
  attr1: str
  attr2: int
  attr3: bool
  # ...etc...

That gives you a class with an implicit __init__ method that takes care of all the parameter setting for you. You also get things like a useful __repr__ and __str__ predefined:

>>> myvar = Foo(attr1="somevalue", attr2=42, attr3=False)
>>> myvar
Foo(attr1='somevalue', attr2=42, attr3=False)
>>> print(myvar)
Foo(attr1='somevalue', attr2=42, attr3=False)
Sign up to request clarification or add additional context in comments.

1 Comment

Dataclasses are the best. I think it's also worth mentioning that while they give you all those nice behaviors by default, they're all customizable as well. Loads of options for when the stock setup isn't quite what you need. Particularly useful is __post_init__, for when you want the convenience of the supplies initializer but also want to do further setup afterward.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.