Daniel V.
Daniel V.

Reputation: 383

Is there any way to remove a nested class in Python?

The script below tries to remove the Inner1 nested class from the class Outer. I get the clear error "TypeError: can't delete __class__ attribute". Dead end?

def Loader(cls):
    for key in dir(cls):
        value = getattr(cls, key)
        if isinstance(value, type):
            delattr(cls, key)
    return cls

@Loader
class Outer:
    class Inner1:
        X = 1

print(Outer.Inner1.X)

Upvotes: 0

Views: 471

Answers (1)

Jordan Brière
Jordan Brière

Reputation: 1070

The problem is that the following line:

if isinstance(value, type):

Is matching everything that inherits from type. If you wish to only delete that inner classes that are explicitly defined in the decorated class, you could use something like this:

from inspect import isclass

def Loader(cls):
    # vars returns the __dict__ attributes, so only names that are explicitely defined in cls.
    for key, value in vars(cls).copy().items():

        # If it is a class, delete it
        if isclass(value):
            delattr(cls, key)

    return cls

@Loader
class Outer:
    class Inner1:
        X = 1

print(Outer.Inner1.X) # AttributeError: type object 'Outer' has no attribute 'Inner1'

An alternative, would be to make your decorator a class and tell it specifically the names you want to delete. E.g.

class Loader:
    def __init__(self, *args):
        # Store the given names to delete
        self.names = args

    def __call__(self, cls):
        for name in self.names:
            try:
                delattr(cls, name)
            except AttributeError:
                pass

        return cls

@Loader('Inner1', 'Inner2', 'etc')
class Outer:
    class Inner1:
        X = 1

print(Outer.Inner1.X) # AttributeError: type object 'Outer' has no attribute 'Inner1'

That said, I'm not sure why you would decorate a class and define inner classes that you will dynamically delete... why not... just not define them at all? :D

Upvotes: 1

Related Questions