I'm working on a Python script at work that is used to interact with XLS/XLSX/CSV spreadsheets. There are three main classes which are nested inside one another (not extending one another, the classes are literally inside the other class)
The three primary classes are explained below:
- The primary Workbookclass, which is a factory method for the XLS/XLSX/CSV classes. This is accessible externally
- The private __Worksheetclass within theWorkbookclass, which is used to open a specific spreadsheet or worksheet within the file itself. This is accessible only via theWorkbook.worksheet()method
- The private __Cellclass within the__Worksheetclass, which interacts with the cells themselves. This shouldn't be accessible externally, rather only accessible via the__Worksheetclass
Heres a simplified version of the class structures thus far:
class Workbook( object ):
    def __init__( self, file_name ):
        self.__file_name = file_name
    def worksheet( self, name ):
        return self.__Worksheet( self, name )
    class __Worksheet():
        def __init__( self, workbook, worksheet ):
            self.__workbook = workbook
        def cell( self, cell_id, data = None ):
            return self.__Cell( cell_id, data )
        class __Cell():
            def __init__( self, cell, data = None ):
                self.__cell = cell
                self.__data = data
            def setVal( self, data ):
                self.__data = data
            def __str__( self ):
                return self.__data
workbook = Workbook( 'test-file.csv' )
worksheet = workbook.worksheet( 'First Worksheet' )
cell_A1 = worksheet.cell('A1', 'Foo...')
print("Before - Cell A1: %s" % cell_A1) # => Before - Cell A1: Foo...
cell_A1.setVal('Bar...')
print("After - Cell A1: %s" % cell_A1)  # => Before - After - Cell A1: Bar...
So the question I have is - Is it considered bad practice to have a class within a class, within a class?
I'm somewhat new to Python, my experience is mostly PHP/JS/Perl. It doesn't seem too uncommon in Python to have a class within a class, but for some reason, nesting a class 3 levels deep just seems wrong. If it is, and there's a better way to do it, then that would be great.
I know the alternative is to not nest the classes, and just check if an instance of Workbook is given to Worksheet as a parameter. Then create a method in Workbook which just returns an instance if Worksheet, while handing self as one of the parameters used to initiate it.
Example:
class Workbook( object ):
    def __init__( self, file_name ):
        self.__file_name = file_name
    def worksheet( self, name ):
        return self.Worksheet( self, name )
class Worksheet( object ):
    def __init__( self, workbook, worksheet = 0 ):
        if not isinstance( workbook, Workbook ):
            raise Exception( 'Expected the workbook to be an instance of the Workbook class' )
        self.__workbook = workbook
    def cell( self, cell_id, data = None ):
        return self.Cell( cell_id, data )
class Cell( object ):
    def __init__( self, worksheet, cell, data = None ):
        if not isinstance( worksheet, Worksheet ):
            raise Exception( 'Expected the worksheet to be an instance of the Worksheet class' )
        self.__cell = cell
        self.__data = data
    def setVal( self, data ):
        self.__data = data
    def __str__( self ):
        return self.__data
# Example Usage One
workbook = Workbook( 'test-file.xls' )
worksheet = workbook.worksheet( 'First Worksheet' )
cell_A1 = worksheet.cell('A1', 'Foo...')
print("Before - Cell A1: %s" % cell_A1) # => Before - Cell A1: Foo...
cell_A1.setVal('Bar...')
print("After - Cell A1: %s" % cell_A1)  # => Before - After - Cell A1: Bar...
# Example Usage Two
workbook = Workbook( 'test-file.xlsx' )
worksheet = Worksheet( workbook, 'First Worksheet' )
cell_A1 = Cell( worksheet, 'A1', 'Foo...')
print("Before - Cell A1: %s" % cell_A1) # => Before - Cell A1: Foo...
cell_A1.setVal('Bar...')
print("After - Cell A1: %s" % cell_A1)  # => Before - After - Cell A1: Bar...
# Failed Example
worksheet = Worksheet( 'Not worksheet', 1 ) # => Exception, as expected
However, this alternative means that the Worksheet and Cell classes are accessible externally and can be initiated manually... but I guess thats not a terrible thing.
Let me know what you think the best route is! One of the comments on this post provides a link to another SO post, in which a user posts 3 advantages of nesting classes, the first of which is:
Logical grouping of classes: If a class is useful to only one other class then it is logical to embed it in that class and keep the two together. Nesting such "helper classes" makes their package more streamlined.
Which is exactly what I was thinking. I just thought it was somewhat awkward nesting them to 3 layers, as I've only done 2 before.
 
     
     
    