← Back to list

UNDERSTANDING type(), super(), AND SINGLETONS IN PYTHON

When you write:

Lokeshwar Reddy N · 2026-06-25 23:27 · 0 claps · 2.6 min read
#python #singleton #metaprogramming #python-tricks #programming
Open on Medium ↗
Wiki topics: 💻 · Programming

UNDERSTANDING type(), super(), AND SINGLETONS IN PYTHON

When you write:

class Foo(Bar):
  x = 1
  def greet(self):
    return "hi"

In the common case, this is equivalent to calling the builtin type directly. type isn’t just the function you use to check an object’s type (type(5) → <class ‘int’>), it’s also callable with three arguments to construct a class:

type(name, bases, namespace)

  • name: the class name, as a string
  • bases: a tuple of parent classes
  • namespace: a dict representing the class body (methods, attributes, everything)

The class statement above is, in the common case, equivalent to:

Foo = type("Foo", (Bar,), {
 "x": 1,
 "greet": lambda self: "hi"
 })

BUILDING TRUE SINGLETONS WITH new

By default, every time you “call” a class, Python creates a new object:

class Add:
  pass

Add() is Add() # False - two different objects

To force every call to return the exact same object, override new, the method responsible for actually allocating a new object in memory (separate from init, which only initializes an already-allocated object):

class Add:
  _instance = None
  def __new__(cls):
    if cls._instance is None:
      cls._instance = super().__new__(cls)
    return cls._instance
  def __repr__(self):
    return "Add"
add = Add() # call once, store the single instance

WHY super() SOMETIMES NEEDS TWO ARGUMENTS

Several similar classes (Add, Sub, Mul, Div) can be built dynamically with type() instead of being written out by hand, a pattern that shows up in plugin systems, ORMs generating model classes from a schema, or a small family of singleton instruction types like this one:

def make_singleton(name):
  def __new__(cls):
    if cls._instance is None:
      cls._instance = super().__new__(cls)
    return cls._instance
  return type(name, (object,), {
    "_instance": None,
    "__new__": __new__,
    })

Add = make_singleton("Add")()

Run this, and it breaks:

RuntimeError: super(): class cell not found

The new function worked fine a moment ago when written inside a normal class body. Here, it’s defined inside a regular function (make_singleton) and only attached to a class afterward, via type(). That difference is exactly what breaks super(), and understanding why requires knowing what super() actually does.

super() means “skip the current class, continue searching for this method one step further along.” To know what “further along” means, Python needs an ordered list of classes to search through, this list is called the MRO (Method Resolution Order). For:

class Dog(Animal):
  pass

the MRO is [Dog, Animal, object], inspectable directly via Dog.mro. When you call super().something(), Python looks at the MRO of the current class, finds where the current class sits, and continues the search from the next entry onward.

Zero-argument super() can only do this automatically because of compiler support: when a method is written literally inside a class body, the compiler quietly stores a hidden reference to that class, so super() always knows which class to skip past.

That’s precisely what’s missing in the dynamic version above. new is defined inside make_singleton, a plain function, never inside an actual class statement, so the compiler never inserts that hidden reference. super() has no idea what to skip past, and raises the RuntimeError.

The fix is the older, explicit two-argument form: super(X, Y), meaning “skip past X in the MRO, but search using Y’s MRO.” Rewriting new with this form fixes the dynamic version:

def __new__(cls, *args, **kwargs):
  if cls._instance is None:
    cls._instance = super(cls, cls).__new__(cls)
  return cls._instance

Here, the first cls tells super where to start in the MRO (skip past this class), and the second cls supplies the MRO to actually search through. super(cls, cls) skips past whatever class cls is at call time (Add, Sub, etc.) and continues searching that class’s own MRO from there, landing on object.new, which performs the actual memory allocation. Unlike the zero-argument form, this doesn’t rely on the compiler detecting a class body, so it works regardless of how or where the method ends up attached to a class.


메타데이터
post_id
ddccd87622bd
slug
understanding-type-super-and-singletons-in-python-ddccd87622bd
url
https://medium.com/@lnandanapalli/understanding-type-super-and-singletons-in-python-ddccd87622bd
canonical_url
https://medium.com/@lnandanapalli/understanding-type-super-and-singletons-in-python-ddccd87622bd
author_url
https://medium.com/@lnandanapalli
status
ok
fetched_at
2026-07-18 20:46:04