Bases: object
Singleton provides a simple way to instantiate singleton objects.
Their intended semantics are for use in ‘is’ comparisons. As a convention, we suggest that you give singletons names which include the modules where they’re instantiated so as to indicate their provenance.
>>> x = Singleton('onyx.util.singleton.name0')
>>> x
onyx.util.singleton.Singleton('onyx.util.singleton.name0')
Singletons can be tested for equality with ‘is’ (and also with the slower and weaker ‘==’ if you prefer)
>>> xx = Singleton('onyx.util.singleton.name0')
>>> xx is x
True
>>> xx == x
True
>>> y = Singleton('foo')
>>> y
onyx.util.singleton.Singleton('foo')
>>> y is x
False
>>> y == x
False
Use the name property to get the name of a singleton
>>> x.name
'onyx.util.singleton.name0'
>>> y.name
'foo'
Singleton objects work with copy.deepcopy():
>>> x is copy.deepcopy(x)
True
Here’s a regression test for names with quotes in them. It’s also a stress test of the repr functionality.
>>> name = "ain't.your.typical.prov'nance"
>>> z = onyx.util.singleton.Singleton(name)
>>> z.name
"ain't.your.typical.prov'nance"
>>> eval(repr(z)) is z
True
Singleton objects can also be pickled, but the pickle.Protocol must be at least 2 for this to be successful.
>>> import pickle
>>> import cStringIO
>>> a = Singleton('a')
>>> pickled_a = cStringIO.StringIO()
>>> pickle.dump(a, pickled_a, pickle.HIGHEST_PROTOCOL)
>>> a_string = pickled_a.getvalue()
>>> pickled_a.close()
>>> pickled_a2 = cStringIO.StringIO(a_string)
>>> c = pickle.load(pickled_a2)
>>> pickled_a2.close()
>>> c is a
True
Returns an object that is used to ensure that, for each set of equivalent immutable objects a client works with, only a single item from that set is used. The use case occurs frequently, e.g. in a system that generates numerous tuples, many of which are equivalent (they are equal), and you want to ensure that you just use one of them, e.g. for the fast and strong ‘is’ equivalence test.
The object is callable. When it is called with a given instance of an immutable object, it returns an object (and always the same object) that is equal to the given immutable object.
Note that while this usage does mean that only one instance from the set is retained, the numerous instances are still constructed, and they are then discarded by use of the callable. In order to prevent such construction from happening at all, typically you must define a class that overrides __new__ and that keeps a cache of constructed objects, and that uses the cache to avoid constructing new instances.
The callable will raise an ‘unhashable type’ TypeError if it is called with a mutable object. Thereafter the callable is unusable and will raise StopIteration when it is called.
Examples:
Two instances of an equivalent tuple are not (usually) the same object
>>> a = tuple(xrange(20))
>>> b = tuple(range(20))
>>> a == b
True
>>> a is b
False
Using instances fed through a singletons callable means you get the same object and use ‘is’ comparisons
>>> s = singletons()
>>> a1 = s(a)
>>> a1 == a
True
>>> b1 = s(b)
>>> b1 == b
True
>>> a1 is b1
True
>>> a1 is s(tuple(i for i in range(20)))
True
Any number of different immutables can be so cached
>>> s(frozenset(xrange(100))) is s(frozenset(range(100)))
True
>>> f = s('foobarbaz')
>>> f is s('foobarbaz')
True
Calling it with a mutable object ends its useful life, so don’t do that.
>>> s(['abc'])
Traceback (most recent call last):
...
TypeError: unhashable type: 'list'
>>> f is s('foobarbaz')
Traceback (most recent call last):
...
StopIteration
If this behavior undesired, just call hash() with the candidate object immediately prior to feeding it to the singletons and you will get the TypeError without destroying the singletons object.