Onyx logo

Previous topic

onyx.util.debugprint – Conditional printing for diagnostic purposes.

Next topic

onyx.util.versionset – Support for keeping track of a set of versions

This Page

onyx.util.singleton – Support for singleton objects

class onyx.util.singleton.Singleton

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
name
onyx.util.singleton.singletons()

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.