Adventures in code and computation.



Taskelot Journal: Reading up on Python ABCs

22 May 2015

Taskelot dev journal and notes for Friday, 22 May, 2015.

01:50 PM – Abstract Base Classes

Got a good night’s sleep and ready to get back to Taskelot. Been thinking about how we might be able to apply Abstract Base Classes (or ABCs) to our class hierarchy to help enforce interface implementation guarantees. Reading up on ABCs now:

04:15 PM – Cooking Dinner

Still reading, for reading has been slow today. Taking a break to being dinner preparations for my family. Red Kidney Bean soup is on the menu tonight! Never made it before, but I looked up a recipe or two to get the general outline of what might go into it, and I’m now ready. First step: quick soak the beans!

But this is a meal that will have several periods of down time—waiting for things to soak or cook—so I do intend to keep reading, and perhaps even utter some code, while cooking.

05:42 PM – Pondering Abstract Base Classes

Beans for dinner are cooking (having just completed their quick-soaking).

I’ve continued reading PEP 3119. As I read it I find that I don’t really like ABCs much. This is likely an indication that I don’t fully understand them, or perhaps that I’m seeking to mis-apply them. But I do see that they might be useful in some way. Here’s one way I might apply them to Taskelot to enforce a kind of interface requirement on sub-classes:

>>> import abc
>>> class TaskelotInterface(metaclass=abc.ABCMeta):
...     @abc.abstractmethod
...     def save(self, task):
...         pass
...
>>> class BadImplementation(TaskelotInterface):
...     pass
...
>>> bad = BadImplementation()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class BadImplementation with
abstract methods save
>>> class GoodImplementation(TaskelotInterface):
...     def save(self, task):
...         # pretend we do something useful here
...         pass
...
>>> good = GoodImplementation()
>>> good.save(object())

So what do we gain with this? Immediately, we no longer need to have to raise a NotImplementedError in the body of our abstract method. If a subclass fails to implement an abstract method we’ve defined, then a TypeError will be raised automatically upon any attempted instantiation of the errant subclass. So that also means bugs can get caught earlier (when the bad class is first instantiated, rather than just when a missing method is called and caught by the superclass raising the NotImplementedError).

Also based on my understanding of the PEP so far, this also allows the abstract method to provide some basic functionality which can be called by the subclass; the method must still be overridden to meet the requirements of the interface, but the overriding method may call up to the superclass’ version of the method to utilize whatever behaviour is available there. I can see some value of that in theory, though I don’t have a specific use for it yet for Taskelot. (That may change as I continue working on this problem).

It occurs to me as I work through ABCs that I’m not exactly sure what a metaclass is in general, nor how the term is being applied specifically in Python. I recall Django models having their kind of Meta attribute for defining a metaclass, but that did not utilize the abc package (AFAICT).

Back to reading (and cooking).

07:35 PM – Dinner Nearly Done

Cooked the ground sausage, added the onions and cooked them down a bit, and seasoned the beans. Added the tomato sauce to the beans then put the sausage/onion bit into the soup pot with the beans. Just now added organic coconut milk to the soup and am letting it simmer just a little bit longer. Just about ready!

Have kept reading through the PEP, and also found some information on metaclasses which I intend to read:

08:35 PM – Dinner Bombed

Didn’t turn out so great. One of my sons and myself liked it well enough, but the lady of the house and another son didn’t like it so well. Won’t make that again (at least, not in the same way).

09:23 PM – Finished Reading about ABCs

I finished reading PEP 3119 and the Python standard library documentation on ABCs. I need to absorb this a bit; I’ll sleep on it. Might work well enough as an interface method… but after reading about ABCs I’m even less likely to criticize Perl for being convoluted. Maybe I just don’t like it because I don’t understand it well enough, but ABCs in Python just seem like another unnecessary layer of indirection—one that tries to solve problems that might go away in a language with (possibly optional) static typing. OTOH, Python’s ABCs do seem to work well for its built-in data structures, so I suspect this is something which will grow on me as my understanding develops.

But besides learning more about Python’s ABCs, I also thought a bit more about my in-progress object model for Taskelot. Perhaps the way to simplify things is to distinguish between the singular Task class and a separate plural TaskSet. TaskSet would be an abstract class (right?) that would have concrete implementations providing the backend persistence driver. So I might end up with a little model like this:

      +------+             +------------+
      | Task |<--contains--|  TaskSet   |
      +------+             | (abstract) |
                           +------------+
                                 ^
                                 |
                             implements
                                 |
                         +-----------------+
                         | TaskSetInMemory |
                         +-----------------+
                                 |
                        drives/interfaces with
                                 |
                                 V
                       .----------------------.
                      ( some persistence layer )
                       '----------------------'

This allows a clean division between a single Task and a set of Tasks, and the set of tasks is the point of interface between the back end persistence and the rest of the system (and it can be swapped out by implementing the TaskSet ABC for any desired backend needed–not just the “native” back end that will come out of the box with Taskelot).