"""The built-in super "function" re-implemented in Python... because I can?"""
import inspect


_SENTINEL = object()


class super:

    """The built-in super "function" re-implemented."""

    def __new__(cls, type=_SENTINEL, obj=None):
        # super() or super(MyClass), but not super(MyClass, my_instance)
        if type is _SENTINEL or obj is None:
            frame = inspect.stack()[1].frame
            # super() with no args checks __class__ slot in outer frame
            if type is _SENTINEL:
                type = frame.f_locals["__class__"]
            # When no "self" specified, get first argument from outer frame
            if obj is None:
                first_arg = inspect.getargvalues(frame).args[0]
                obj = frame.f_locals[first_arg]
        # The built-in super object exposes these 3 dunder attributes below
        self = object.__new__(cls)
        self.__thisclass__ = type
        self.__self__ = obj
        if isinstance(obj, type):
            self.__self_class__ = obj.__class__
        elif issubclass(obj, type):
            self.__self_class__ = obj
        else:
            raise TypeError(
                "super(type, obj): obj must be an instance or subtype of type"
            )
        return self

    def __getattribute__(self, attr):
        # Looking up self.__attr_name__ would result in infinite recursion
        this = object.__getattribute__(self, "__thisclass__")
        self_class = object.__getattribute__(self, "__self_class__")
        obj = object.__getattribute__(self, "__self__")

        # Get the method resolution order from __thisclass__ onward
        this_mro_index = self_class.__mro__.index(this)
        method_resolution_order = self_class.__mro__[this_mro_index+1:]

        # Find method/attribute in first class after ours that has a match
        for cls in method_resolution_order:
            if attr in cls.__dict__:
                value = cls.__dict__[attr]
                # this is how self-binding works (methods are descriptors)
                if hasattr(value, "__get__"):
                    value = value.__get__(obj)
                return value

        # Should raise an attribute error
        raise object.__getattribute__(self, attr)

    def __repr__(self):
        this = object.__getattribute__(self, "__thisclass__")
        self_class = object.__getattribute__(self, "__self_class__")
        return f"<super: {this}, <{self_class.__name__} object>>"

94 views

Share

pym.dev/p/2f95p/

Need to share some Python code?

Create a new snippet