1

Question What is the proper way to wrap data in Python? Is the __init__ and self. boilerplate needed?

As a class Sometimes I wish to create classes that represent Data, but don't have complex computational mechanics inside. For example:

class DataClass:
  def __init__(self, a, b, c):
    self.a = b
    self.b = b
    self.c = c

  def sum(self):
    return self.a + self.b + self.c


dc = DataClass(1,2,3)

print(dc.sum())

I dislike this code, the mathematics of a + b + c is hard to read due to the self. and the constructor is boilerplate. Moreover, I would create this class whenever I noticed that a tuple or dict around a, b and c is getting messy. In particular, tuples are ugly when the returned values change in amount or order, as happend with cv2.findContours between opencv versions 3 and 4 (returning 3, respectively 2 values). Lastly, I often copy-paste boilerplate stuff, easily allowing for painful mistakes, as is the case here with self.a = b.

As a function Therefore I often do something like this:

def DataFunc(a, b, c):
  class Result:
    def sum(self):
      return a + b + c
  return Result()


df = DataFunc(1,2,3)

print(df.sum())

It works and it is significantly more transparent (imho), but also a bit weird w.r.t. types, i.e.

rf2 = ResultFunc(1,2,3)
print(type(rf) == type(rf2)) # -> False

Moreover, the data members are not accessible (rf.a, rf.b, etc).

As a class with an annotator Finally, one can create an annotator to add the constructor:

def add_constructor(*vars):
    def f(class_):
        def init(self, *args, **kwargs):
            unasigned = set(vars[len(args):]) - set(kwargs.keys())
            if len(unasigned) > 0:
                raise ValueError(f"Missing argument(s): {' '.join(unasigned)}")
            for name, value in zip(vars, args):
                setattr(self, name, value)
            for name, value in kwargs.items():
                setattr(self, name, value)

        setattr(class_, '__init__', init)
        return class_
    return f


@add_constructor('a', 'b', 'c')
class DataAnnot:
    def sum(self):
        return self.a + self.b + self.c

da = DataAnnot(1,2,c=3)
print(da.sum())
Herbert
  • 5,279
  • 5
  • 44
  • 69
  • 1
    I'm confused by this question, because you start off saying that these data classes "don't have complex computational mechanics inside," but in your example, you're trying to optimize (for readability) a function that actually is inside the data class. Either way, I would really avoid creating new ways of creating and using classes in Python. You'll spend a lot of time trying to "fix" Python instead of working on your actual problem, and the next person who comes along won't understand/appreciate your effort. I don't find "self.a + self.b ..." hard to read at all. – Willis Blackburn Oct 18 '19 at 12:46
  • Possible duplicate of [How to avoid explicit 'self' in Python?](https://stackoverflow.com/questions/1984104/how-to-avoid-explicit-self-in-python) – Willis Blackburn Oct 18 '19 at 12:47
  • @WillisBlackburn Your point is, accept some boilerplate to avoid an overly exotic language with features not everybody knows about. Fair enough. – Herbert Oct 18 '19 at 13:56
  • @WillisBlackburn My point is not to necessarily use classes, as the refereed question does. Anything that is concise in code would be suitable. In my opinion, boiler plate and duplicate code is a source of bugs and lacking readability. – Herbert Oct 18 '19 at 13:57

0 Answers0