Skip to content

Instantly share code, notes, and snippets.

@treyhunner
Created March 1, 2022 18:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save treyhunner/75670b9e315f8993254844cbe88de363 to your computer and use it in GitHub Desktop.
Save treyhunner/75670b9e315f8993254844cbe88de363 to your computer and use it in GitHub Desktop.
The built-in super function, re-implemented
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>>"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment