Skip to content

Instantly share code, notes, and snippets.

@Wilfred
Created September 17, 2013 17:44
Show Gist options
  • Save Wilfred/6597856 to your computer and use it in GitHub Desktop.
Save Wilfred/6597856 to your computer and use it in GitHub Desktop.
Ensure no class gets instantiated twice with the same arguments.
import copy
def make_hash(obj):
"""Make a hash from an arbitrary nested dictionary, list, tuple or
set.
"""
if isinstance(obj, set) or isinstance(obj, tuple) or isinstance(obj, list):
return hash(tuple([make_hash(e) for e in obj]))
elif not isinstance(obj, dict):
return hash(obj)
new_obj = copy.deepcopy(obj)
for k, v in new_obj.items():
new_obj[k] = make_hash(v)
return hash(tuple(frozenset(new_obj.items())))
class CacheInstancesMetaclass(type):
# Based on http://stackoverflow.com/a/5961102
def __call__(cls, *args, **kwargs):
"""We can't just override __new__ on CacheInstances, because python
will call __init__ every time __new__ returns an instance,
even if we have already called __init__.
By using a metaclass we can override that behaviour.
"""
arg_hash = make_hash([args, kwargs])
if arg_hash in cls.instantiated:
return cls.instantiated[arg_hash]
else:
# Call type and go through normal instantiation.
new_instance = type.__call__(cls, *args, **kwargs)
# Save the created instance and return it.
cls.instantiated[arg_hash] = new_instance
return new_instance
class CacheInstances(object):
"""A class that can only be instantiated once for each set of
arguments passed to __init__.
>>> class Foo(CacheInstances): pass
>>> f1 = Foo(x=1)
>>> f2 = Foo(x=1)
>>> f3 = Foo(x=2)
>>> f1 is f2
True
>>> f2 is f3
False
"""
__metaclass__ = CacheInstancesMetaclass
# Class attribute, shared between all instances.
instantiated = {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment