A special kind of class is the sequence class. You have seen several sequence classes, namely tuples, lists, dictionaries, and sets. Such classes contain a sequence of elements, that can be accessed using indices or keys. You can create such classes yourself, by overloading several methods that support changing or getting information on the elements of the class.

To demonstrate how these methods work, I have implemented a sequence class that implements a Mesostic Puzzle. In Dutch, such puzzles are known as “Filippines,” but outside The Benelux they are not well known. The puzzle consists of a list of questions, each of which is answered by one word. Of each answer, one letter is indicated as “special.” The special letters, in order of the questions, provide the solution to the puzzle.

I have defined each of the words for the puzzle as an instance of the class MesosticWord, which consists of the answer, the index of the special letter in the answer, and the question. The class Mesostic is the complete puzzle, i.e., it is a sequence of MesosticWords. I implemented the __len__(), __getitem__(), __setitem__(), and __delitem__() methods (the last two are not actually used in the code).

I also implemented two more methods, which demonstrate how the overloaded methods manage to do their job. display() displays the puzzle, and uses thereby the len() function and indices on the puzzle object itself. solution() displays the solution, also using len() and indices.

class MesosticWord:
    def __init__( self,  word, index, question ):
        self.word = word
        self.index = index
        self.question = question

class Mesostic:
    def __init__( self, name, words ):
        self.name, self.words = name, words
    def __len__( self ):
        return len( self.words )
    def __getitem__( self, n ):
        return self.words[n]
    def __setitem__( self, n, value ):
        self.words[n] = value
    def __delitem__( self, n ):
        del self.words[n]
    def display( self ):
        print( self.name )
        for i in range( len( self ) ):
            print( "{}. {}".format( i+1, self[i].question ), 
                end = "  " )
            for j in range( len( self[i].word ) ):
                if j == self[i].index:
                    print( "* ", end="" )
                else:
                    print( "_ ", end="" )
            print()
    def solution( self ):
        s = ""
        for i in range( len( self ) ):
            s += self[i].word[self[i].index]
        return s
    
puzzle = Mesostic( 
    "The Monty Python and the Holy Grail Mesostic Puzzle",
    [ MesosticWord( "ANTHRAX", 5, 
          "Sir Galahad's tale took place in the Castle" ),
      MesosticWord( "PERIL", 2, 
          "Sir Robin was thrown into the Gorge of Eternal" ),
      MesosticWord( "RABBIT", 5, 
          "Sir Bors was killed by a" ),
      MesosticWord( "SHRUBBERY", 1, 
          "The Knights of Ni!'s first demand was to get a" ),
      MesosticWord( "COCONUT", 5, 
          "A horse can be replaced by a" ),
      MesosticWord( "MINSTRELS", 5, 
          "They were forced to eat Robin's" ) ] )

puzzle.display()

Note: It would have been nicer if the stars, which indicate the special letters, were printed in a column. However, depending on the editor that you use, the display format does not always use a fixed letter width, so it is hard to organize that. You may implement a solution for this on your own, if you like (it is not relevant for the theme of the chapter).

Another important method that you can implement for sequence classes is __iter__(). This one will be discussed in Chapter 242.

When implementing a sequence class, you should also consider creating a suitable implementation of the __add__() method, and possibly a suitable implementation of the __mul__() method.

A Sentence is a list of words. A basic Sentence class is given below. Implement __len__(), __getitem__(), __setitem__(), and __contains__() methods for this class.

class Sentence:
    def __init__( self, words ):
        self.words = words
    def __repr__( self ):
        return " ".join( self.words )

s = Sentence( [ "There", "is", "only", "one", "thing", "worse", 
"than", "being", "talked", "about", 
"and", "that", "is", "not", "being", "talked", "about" ] )
print( s )
print( len( s ) )
print( s[5] )
s[5] = "better"
print( "being" in s )