porgarmingduod
porgarmingduod

Reputation: 7888

Python style question: Support class as inner class?

I like to use the structures built into the language to organize my code. But there is one situation where I can't stay consistent, simply because I don't see a definitive best way of doing it. It's regarding support classes, that is, classes that are exclusively used internally by another class: Do I make them inner classes, or separate classes.

Inner classes:

class Complicated:
    class Utility1:
        pass
    class Utility2:
        pass
    pass

Separate classes:

class Complicated:
    pass

class Utility1:
    pass

class Utility2:
    pass

Inner classes has the advantage of being scoped inside the only class that uses them. But the problem is that I get less space to write code due to indentation.

Outer classes have neither the advantage nor the disadvantage. I am tired of always spending some mental energy whenever I write support classes, wondering about this silly issue.

My question is whether anyone with a substantial python experience on their back can advise as to whether there is a best practice regarding this? Even if the answer is that "it depends", it is appreciated if it comes with someone more experienced than myself.

Upvotes: 3

Views: 1680

Answers (4)

John Moore
John Moore

Reputation: 181

Python Inner Classes

Python inner classes are possible to create and use. Unlike other languages, such as Java, Python inner classes don't automatically give you access to the outer class attributes and methods.

The work around for this problem: Define all Inner Classes with a parent parameter. This parameter can be used to access the outer class attributes and methods.

Python Example Class with Inner Class

class Outer:
    def __init__(self):
        self.myVar = 100
        self.inner = self.Inner(self)
        
    def getDoubleVar(self):
        return(self.myVar * 2)

    #Define inner class
    class Inner:
        def __init__(self, parent):
            self.parent = parent

        #Use the parent variable to access parent class attributes/methods
        def printOuterVar(self):
            print(self.parent.myVar)

        def callOuterMethod(self):
            return(self.parent.getDoubleVar())


    

#Create Instance of Outer Class
outer = Outer()

#Display myVar
print("Display myVar")
print(outer.myVar)


#Execute Outer Method
print("\nExecute Outer Method")
print("val = outer.getDoubleVar()")
val = outer.getDoubleVar()
print("val: {0}".format(val))

#Execute Inner Method
print("\nExecute Inner Method")
print("outer.inner.printOuterVar()")
outer.inner.printOuterVar()

#Execute Inner Method That Calls Outer Method
print("\nExecute Inner Method That Calls Outer Method")
val = outer.inner.callOuterMethod()
print("val = outer.inner.callOuterMethod()")
print("val: {0}".format(val))

#Create Instance of Inner Class Separately
print("\nInstantiate Inner Class Separately")
#Note that you provide the current outer instance as the parent parameter
print("Note that you provide the current outer instance as the parent parameter")
print("myInner = outer.Inner(outer)")
myInner = outer.Inner(outer)

#Call Inner Method
print("\nCall Inner Method")
print("myInner.printOuterVar()")
myInner.printOuterVar()
print("finished")

Upvotes: 0

Sven Marnach
Sven Marnach

Reputation: 602635

I would suggest

class Complicated:
    pass

class _Utility1:
    pass

class _Utility2:
    pass

and put all this in a module of its own. The leading _ indicates that the utility classes are meant for internal use only (and for what it's woth, they won't get imported by from module import *; but I don't like this anyway).

Edit: Citing from PEP 8:

Classes for internal use have a leading underscore in addition.

Upvotes: 7

user79758
user79758

Reputation:

Whether I use an inner class or outer class largely depends on whether I expect to override the type, either in a subclass or on a per-instance basis:

class Foo(object):
    normalize = str

    def process(self, input):
        # Do some stuff in here...
        return self.normalize(output)

class UniFoo(Foo):
    normalize = unicode

class TotallyMungedFoo(Foo):
    class normalize(object): ...

If I don't expect to need (or explicitly don't want) behavior like this, I use classes in the same module, but only include the "important" one in my __all__ declaration.

Upvotes: 0

Andrew Skirrow
Andrew Skirrow

Reputation: 3451

I use the single underscore as a prefix to classes that are internal to a python packages. I think this is a common python pattern:

class Complicated:
    pass

class _Utility1:
    pass

class _Utility2:
    pass

Upvotes: 1

Related Questions