3

I have two data structures, Frontier (a queue) and Explored (a set). I want to implement a custom __contains__ method that they share:

class Custom_Structure:
    def __contains__(self, item):
        # TODO         

class Frontier(Custom_Structure):
    def __init__(self):
        self.queue = Queue.Queue()

class Explored(Custom_Structure):
    def __init__(self):
        self.set = set()

I understand the theory of inheritance and abstract classes (or at least, I think I do!). It seems that making Custom_Structure an abstract class is appropriate here, because I don't intend it to be instantiated; it is only there so that Frontier and Explored can share the __contains__ method. I don't need any abstract methods, but that's ok because I've read that an abstract class doesn't necessarily need abstract methods.

If I was doing this in C# I'd just add the abstract keyword, and I wouldn't be asking this question. But in Python (2.7) it seems I need to do something like this:

from abc import ABCMeta

class Custom_Structure:
    __metaclass__ = ABCMeta
    ...

Ok, it's still not that big a deal, but it's not very readable, and it just feels like overkill with not much benefit. I'm tempted to just leave my code as it is.

So my question is: Why bother going to the trouble of making Custom_Structure abstract? (So what if someone instantiates the base class?)

I've found the following questions already but they don't quite answer the question:

Why use Abstract Base Classes in Python? The answer here doesn't seem to apply because I don't have any abstract methods, so there is no contract between the base class and subclasses.

What is the main advantage of making a class abstract Same

Abstract class with no abstract methods This has the answer 'to prevent instantiation', but why is that such a problem?

What's the point in having an abstract class with no abstract methods? Same

Why use an abstract class without abstract methods? Same. I understand the theoretical concept of why it shouldn't be instantiated, but what's the big deal practically?

I've read various questions and answers about the theory behind abstract classes - my question is not about what they are and how they work (although if I'm mistaken please tell me), it's more 'what is the benefit' in this simple case?

Community
  • 1
  • 1
andydavies
  • 3,081
  • 4
  • 29
  • 35
  • IMHO, the first question linked is the more relevant because it focuses on Python language which is dynamically typed while many of the others focus on Java or C# which are statically typed. And it has 2 good answers the accepted one and a second one even more upvoted which IMHO cover the question. Said differently, if none of the reasons exposed in both answers is relavant for your problem, you do not need ABCMeta. But that's no more than my opinion... – Serge Ballesta Mar 22 '17 at 16:20
  • Python is one of the languages where abstract classes do need abstract methods. Your `Custom_Structure` wouldn't be abstract even with `ABCMeta`. (Also, you should have your classes explicitly extend `object` on Python 2.) – user2357112 Mar 22 '17 at 16:28
  • just a note, there's already an abstract base class for that in the `collections` module. it's called `Container`. check it out. – acushner Mar 22 '17 at 17:02
  • 1
    @acushner: The questioner's `__contains__` is not abstract; the `Custom_Structure` class is there to provide a shared implementation. Not a design I'd recommend, but it's what the questioner is doing. – user2357112 Mar 22 '17 at 17:12
  • @user2357112 which way would you recommend? (May be off-topic but I'd be interested to know the best approach.) – andydavies Mar 22 '17 at 17:15
  • @andydavies: Well, first, from the code you've posted, it doesn't look like it actually makes sense for these classes to have the same `__contains__` implementation. Second, if these classes are just going to delegate `__contains__` to `self.set` or `self.queue`, that's simple enough that I'd just write it twice even if the code was the same, because putting it in a shared superclass doesn't actually make things simpler or less error-prone. [cont] – user2357112 Mar 22 '17 at 17:21
  • If you still want a shared implementation, I'd personally use a module-level helper function that both classes call, to simplify the inheritance hierarchy. Other people may have different opinions about this. – user2357112 Mar 22 '17 at 17:25
  • (Aside: `Queue.Queue` is specifically designed for inter-thread communication. It's not a good general-purpose queue for use within a thread; `collections.deque` is better for that.) – user2357112 Mar 22 '17 at 17:26
  • @user2357112 as soon as I started to implement `__contains__` I saw what you meant. Doesn't make sense at all for them to share the implementation! (I think the question still stands though, just a bad example.) – andydavies Mar 23 '17 at 12:47

1 Answers1

3

Interesting question. Let me begin by saying that the very fact that this piece of ugliness is the way abstract classes are created in python2...

from abc import ABCMeta

class Custom_Structure:
    __metaclass__ = ABCMeta

... already indicates that the Python Dictators didn't think marking classes as explicitly abstract was all that important.

That said, making a class abstract is a useful way of adding metadata to the code. It's a way for the author of a class to communicate with future users of a module.

What's being said? Two things:

  1. An abstract class is incomplete, and should not be instantiated. An instance of this class has no concrete meaning, because the definition is partial. It lacks the full set of information necessary to make the object work.

  2. An abstract method is the missing piece of the definition. By making a method abstract, you're stating that for the class to make sense, this specific part of the full description must be provided. Not only that, but by providing an interface you're conveying the expected behavior of the missing piece, whatever the internal details end up being.

Of course, all of this can be the case in practice without any additional specification for the class or its methods, but then inheritors end up guessing how to use the object, or have to read the documentation in detail.

If you instantiate an implicitly abstract class, sooner or later an error will occur. Could end up producing a NotImplementedError, or a KeyError when something's missing. This mistake is easy to make, and will arise down the road when trying to use the functionality of the object.

If you try to instantiate an explicitly abstract class, the error is immediate. No dice are thrown, and the problem is not postponed to whenever the class is used in one way or another. A missing implementation for abstract method won't cause an error when and if the method is used, but immediately when the coding mistake is made.

Summing up, making a class and some of its methods explicitly abstract makes the reality of the code adhere to the documentation. It moves the responsibility of knowing the rules from the inexperienced user to the author. It's a way of having the language runtime enforce in practice the theoretical rules and constraints of usage.

salezica
  • 74,081
  • 25
  • 105
  • 166