While perusing some of the unanswered posts on Stack Overflow, I came across an interesting question which was asking how to mimic the behavior of CPython's small ints and some other built-ins like True
and False
. That is, there is enforced only one instance with each value. For example
x = 12
y = 12
x == y # True
x is y # Also True
You can contrast this with, for example, (sufficiently long) strings which don't have this property.
x = "Hello World"
y = "Hello World"
x == y # True
x is y # This is False
So how can we craft a way to get this property for custom classes? Let's go straight to the code.
from collections import namedtuple
def singleton_namedtuple(class_name, fields):
nt = namedtuple(class_name, fields)
class NT(nt):
_dict = {}
def __new__(cls, *args):
if args not in cls._dict:
print("making new {} w/ {}", class_name, args)
cls._dict[args] = super().__new__(cls, *args)
return cls._dict[args]
return NT
Use the function like this:
Variable = singleton_namedtuple('Variable', ['letter', 'index'])
a = Variable('one', 1)
b = Variable('two', 2)
c = Variable('two', 2)
print(a == b) # False
print(a is b) # False
print(b == c) # True
print(b is c) # True
The factory method singleton_namedtuple
creates a class based on a namedtuple
. Because the class is inheriting from tuple
, it is immutable which is a required property since immutability prevents distinct objects which are not equal from becoming equal and breaking the property of equality implying identity.
Overriding the __new__
method allows us to prevent the creation of a new object when an equivalent object already exists.
The biggest caveat here is that the objects in the namedtuple
must be hashable and immutable. Hashability is required for the code to run, and immutability is required to maintain equality implying identity.