matthewgdv
matthewgdv

Reputation: 749

Issue with inner class scopes

so I've got a class structure with several levels of nesting of inner classes for namespacing purposes. It's no different from just having everything flat, but it's much cleaner and intuitive this way. Basically, we're using inner classes for better code organization.

I'm encountering some issues however:

I'll just post some example code rather than trying to explain them:

1)

class Namespace1:
    class Class1:
        pass  # base methods go here

        class Subclass1(Namespace1.Class1):
            pass  # extended methods go here

        class Subclass2(Namespace1.Class1):
            pass  # extended methods go here

        class Subclass3(Namespace1.Class1):
            pass  # extended methods go here

    class Class2:
        pass  # base methods go here

        class Subclass1(Namespace1.Class2):
            pass  # extended methods go here

        # etc.

The issue with subclassing here is that Namespace1.Class1 doesn't yet exist when you try to declare Namespace1.Class1.Subclass1, so it doesn't work. I'm assuming there's probably no way to fix this, but I'm mentioning this just in case there's some way to achieve this I'm not aware of.

So what I tried next was this:

class Namespace1:
    class Class1:
        pass  # base methods go here

    class Class1Subclass1(Class1):
        pass  # extended methods go here

    class Class1Subclass2(Class1):
        pass  # extended methods go here

    class Class1Subclass3(Class1):
        pass  # extended methods go here

    class Class2:
        pass  # base methods go here

    class Class2Subclass1(Class2):
        pass  # extended methods go here

        # etc.

Which works, but I really dislike it for several reasons:

So I thought of doing this:

2)

class Namespace1:
    class Class1:
        pass  # base methods go here

    class Class1_:
        class Subclass1(Class1):
            pass  # extended methods go here

        class Subclass2(Class1):
            pass  # extended methods go here

        class Subclass3(Class1):
            pass  # extended methods go here

    class Class2:
        pass  # base methods go here

    class Class2_:
        class Subclass1(Class2):
            pass  # extended methods go here

        # etc.

It's not 100% ideal (the nicest way would be something like example 1 above), but this should in theory be possible to do. By the time the interpreter gets to creating Namespace1.Class1_.Subclass1, Class1 should already exist. The issue I've got is that I don't know how to reference it, since I can't just reference it as Namespace1.Class1 (since Namespace1 doesn't yet exist), and I'm out of scope to reference it simply as Class1.

Does anyone have some idea of how I'd go about this?

And before anyone tells me to just use a module hierarchy to achieve this: I know it's an option, but I'd prefer to have it all in a single file in such a way that there's a single overview of it, since each of these classes is only about 3-5 lines long (only differing by a single method override). For this particular use-case it's good to see it all in one place, and the actual code I've got has like 3-4 levels of indentation with some classes having like 12+ subclasses. Navigating over 100 files (each of which is maybe 10 lines long) would be a nightmare.

Upvotes: 0

Views: 59

Answers (1)

blhsing
blhsing

Reputation: 106455

Nested classes are stored as attributes of the outer class, so instead of specifying the base class of each nested class directly in its declaration, which you can't do for reasons you've already described, you can use a class decorator on the outer class to convert such attributes into subclasses of the outer class by re-creating them with a call to the type function with the outer class added to each inner class as a base class:

def nested_subclasses(cls):
    for name, obj in vars(cls).items():
        if isinstance(obj, type):
            setattr(cls, name, type(name, (cls,), dict(vars(obj))))
    return cls

so that:

class Namespace1:

    @nested_subclasses
    class Class1:
        def foo(self):
            print('bar')

        class Subclass1:
            pass

Namespace1.Class1.Subclass1().foo()

outputs:

bar

Upvotes: 1

Related Questions