Skip to content

Instantly share code, notes, and snippets.

@wowkin2
Created May 14, 2016 17:20
Show Gist options
  • Save wowkin2/3af15bfbf197a14a2b0b2488a1e8c787 to your computer and use it in GitHub Desktop.
Save wowkin2/3af15bfbf197a14a2b0b2488a1e8c787 to your computer and use it in GitHub Desktop.
Python Singleton with parameters (so the same parameters get you the same object) with support to default arguments and passing arguments as kwargs (but no support for pure kwargs).
"""
Python Singleton with parameters (so the same parameters get you the same object)
with support to default arguments and passing arguments as kwargs (but no support for pure kwargs).
And implementation for MongoClient class from pymongo package.
"""
from pymongo import MongoClient
import inspect
class Singleton(type):
""" Simple Singleton that keep only one value for all instances
"""
def __init__(cls, name, bases, dic):
super(Singleton, cls).__init__(name, bases, dic)
cls.instance = None
def __call__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls.instance
class SingletonArgs(type):
""" Singleton that keep single instance for single set of arguments. E.g.:
assert SingletonArgs('spam') is not SingletonArgs('eggs')
assert SingletonArgs('spam') is SingletonArgs('spam')
"""
_instances = {}
_init = {}
def __init__(cls, name, bases, dct):
cls._init[cls] = dct.get('__init__', None)
def __call__(cls, *args, **kwargs):
init = cls._init[cls]
if init is not None:
key = (cls, frozenset(
inspect.getcallargs(init, None, *args, **kwargs).items()))
else:
key = cls
if key not in cls._instances:
cls._instances[key] = super(SingletonArgs, cls).__call__(*args, **kwargs)
return cls._instances[key]
class SingletonMongoClient(object):
""" Class based on Singleton type to work with MongoDB connections
"""
__metaclass__ = SingletonArgs
def __init__(self, url, db_name=None):
if db_name:
self.connection = MongoClient(url)[db_name]
else:
self.connection = MongoClient(url).get_default_database()
def connection(self):
return self.connection
def db_init(db_name=None):
url = 'mongodb://localhost:27017/'
c = SingletonMongoClient(url, db_name).connection
return c
def tests():
class A(object):
__metaclass__ = SingletonArgs
FOO = 'bar'
assert A() is A()
class B(object):
__metaclass__ = SingletonArgs
def __init__(self, key):
self.key = key
assert B('key1') is B('key1')
assert B('key1') is not B('key2')
class C(object):
__metaclass__ = SingletonArgs
def __init__(self, key=None):
self.key = key
assert C() is C()
assert C() is C(None)
assert C(None) is C(key=None)
assert C() is C(key=None)
assert C() is not C('key')
assert C('key') is C('key')
assert C('key') is C(key='key')
assert C('key1') is not C(key='key2')
assert C(key='key1') is not C(key='key2')
class D(object):
__metaclass__ = SingletonArgs
def __init__(self):
pass
class E(object):
__metaclass__ = SingletonArgs
def __init__(self):
pass
assert D() is not E()
@Haypierre
Copy link

Haypierre commented Feb 11, 2021

Hello, great code but it does not work when you pass a dict as a parameter to the object built with the metaclass Singleton since frozenset will not be able to hash the dict : TypeError: unhashable type: 'dict'

@Haypierre
Copy link

I've implemented this if it can help someone 👍

def unsure_hashable(iterable):
    return [x if isinstance(x, Hashable) else x.__str__() for x in iterable]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment