2

I have been a Java programmer for 4,5 years. Now I've switched to Python (and the main reason is that I'm now a freelancer and I work alone). I provide source code to my costumers and sometimes I have to motivated my design choices. Now to the question. I have to support my design choice:

class Base(object):
    def foo(self):
        self.dosomethig(self.clsattr1)
        self.dosomethig(self.clsattr2)

class Derived(Base):
    clsattr1 = value1
    clsattr2 = value2

Base class is always meant to be extended or derived.
My customer (a java programmer) argues that my approach is not elegant from an OO point of view. He claims that the following approach is better:

class Base(object):
    def __init__(self, clsattr1, clsattr2):
        self.clsattr1 = clsattr1
        self.clsattr2 = clsattr2

    def foo(self):
        self.dosomethig(self.clsattr1)
        self.dosomethig(self.clsattr2)

class Derived(Base):
    def __init__(self):
        super(Derived, self).__init__(value1, value2)

I realize that second approach is much more elegant than the first one, but I told him that the first approach is much more handy. I told him I do not see any issue, but he is not convinced. Is the first approach so bad? And why?

  • 4
    Launch the Python interpreter and type `import this`. – Apalala Jan 21 '11 at 02:23
  • 1
    Did you intend to add classattr1 and classattr2 inside of the __init__() method as self.classattr1 and self.classattr2? If not, you may have more problems than just elegance of your design. – Utku Zihnioglu Jan 21 '11 at 02:41
  • Semantically, the two are not the same - In the first case, the attributes are shared between all the instances of the class. In the second, they aren't. See [this answer](http://stackoverflow.com/questions/206734/why-do-attribute-references-act-like-this-with-python-inheritance/206765#206765) – André Paramés Jan 21 '11 at 02:53
  • The second implementation is 1) closer to what a Python programmer expects to see, and 2) gets much less complaining from `pylint` because it creates object properties in `__init__`. If the "base class is always meant to be extended or derived", you might want to think about *why* that is and see if metaclasses might be a better approach. Probably not, but it's worth exploring. Finally, remember that even if you intend for a base class to never be instantiated, someone may well do it by accident. – Mike DeSimone Jan 21 '11 at 03:17
  • @Apalala, @André Paramés Comparing the two approaches and rereading the Zen Of Python, I think I had some kind of enlightenment (really). I've learned a lot. The second approach is much more clear and avoids the issue raised by André Paramés. – ApprenticePython Jan 21 '11 at 03:26

3 Answers3

3

There does not seem to be any difference whatsoever. Don't waste your time arguing with him -- it might be quicker to just refactor your classes and take the money.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
0

If the Base class requires the values to do its thing, then it should be explicit.

In other programming languages the requirement could be set with abstract methods, one for each value, but that's not in the Python style, and simulating it would be cumbersome.

The most interesting difference between the examples is that the first code fragment tries to use class variables of the Base class, which would impact every derived class and all its instances. Which way should it be? Values per hierarchy? Per derived class? Per instance?

In the best pythonic style, if objects can set their own initialization values, then the Base class should use reasonable defaults for them, and provide a mechanism for subclasses and their instances to override them.

class Base(object):
    def __init__(self, attr1=DEFAULT1, attr2=DEFAULT2):
        self.attr1 = attr1
        self.attr2 = attr2
Apalala
  • 9,017
  • 3
  • 30
  • 48
  • almost there: i'd initialize class attributes in `Base` – Javier Jan 21 '11 at 02:38
  • @Javier No argument if indeed the design calls for class attributes, which is something unlikely. Let me explain: the class attributes would belong to the Base class, but the design would call for them to be overridden by all derived classes? Global effects that depend on the order in which modules are imported, or classes compiled or first instantiated? – Apalala Jan 21 '11 at 02:44
  • of course, class attributes are different from instance ones; but in this case (and many others where the subclass contributes mostly configuration) they're the cleaner and shorter solution. – Javier Jan 21 '11 at 03:12
-5

The second snippet could only be called 'elegant' when compared with Java. Not only it's almost twice as long, the use of '__' variables is a clear sign that the programmer is fighting against the language, and there are a lot of possible errors.

If you (or your client) wants Java, you know where to find it.

Javier
  • 60,510
  • 8
  • 78
  • 126
  • 4
    -1: the use of special methods is integral to Python and a powerful feature. It is not in any way "fighting against the language". Tell me, how would you initialize a class without `__init__`? – Rafe Kettler Jan 21 '11 at 02:26
  • they're special for a reason. It's crucial to know and use them; but once the code works, i always make an effort to remove as much of them as possible. It's a nice feature of Python the flexibility that allows it to be bend to your will; but when it works as its natural, everything is smoother. – Javier Jan 21 '11 at 02:29
  • The only "__" in it is the constructor "__init__". So that's not really fair to claim that its fighting against the language. – Winston Ewert Jan 21 '11 at 02:29
  • @javier I don't see your point. The point of these methods is to make using classes as natural as possible. It's one of the keys to making classes usable. – Rafe Kettler Jan 21 '11 at 02:30
  • defining `__init__` is usually unavoidable (unless you leave initialization to subclassing, as in the first example); but calling it is in many cases optional. In this case is totally gratuitous. – Javier Jan 21 '11 at 02:31
  • In the end it's true that avoiding '__'s is mostly an aesthetic rule of thumb; but making code so much longer and with so many more points of failure is just ugly. and for what? i think the objection is setting members in a subclass, that's strongly frowned up on Java (and to a certain extent in C++); but Python doesn't have privacy; there's no advantage in pretending there is. – Javier Jan 21 '11 at 02:35
  • @Javier is using a severe tone to discard the second solution, which could be close to the right one if the question had clearer requirements. – Apalala Jan 21 '11 at 02:38
  • As a general rule, calling your base class constructor is a good idea. – Winston Ewert Jan 21 '11 at 02:40
  • 3
    ["Any arguments are passed on to the `__init__()` method. If there is no `__init__()` method, the class must be called without arguments."](http://docs.python.org/reference/datamodel.html) -- While I mostly agree with you about the nearly painful use of `__methods__` and `__variables__` in Python, [`__init__()` is how constructors are built in Python](http://docs.python.org/tutorial/classes.html#class-objects). – sarnold Jan 21 '11 at 02:44