Module with general purpose types.


Returns a function that converts arguments matching signature to canonical positional and keyword arguments. If possible, an argument is added to the list of positional arguments, otherwise to the keyword arguments dictionary. The returned arguments include default values.


signature (inspect.Signature) – The signature of a function to generate canonical arguments for.


A function that returns a tuple of a tuple of positional arguments and a dict of keyword arguments.

Return type:



Consider the following function.

>>> def f(a, b=4, *, c): pass

The argument_canonicalizer for f is generated as follows:

>>> canon = argument_canonicalizer(inspect.signature(f))

Calling canon with parameter b passed as keyword returns arguments with parameter b as positional argument:

>>> canon(1, c=3, b=2)
((1, 2), {'c': 3})

When calling canon without parameter b the default value is added to the positional arguments:

>>> canon(1, c=3)
((1, 4), {'c': 3})

Compute a stable hash of immutable object data. The hash is not affected by Python’s hash randomization (see object.__hash__()).


data – An immutable object of type bool, int, float, complex, str, bytes, tuple, frozenset, or Ellipsis or None, or the type itself, or an object with a __nutils_hash__ attribute.


The hash of data.

Return type:

40 bytes

class nutils.types.Immutable(*args, **kwargs)

Bases: object

Base class for immutable types. This class adds equality tests, traditional hashing (hash()), nutils hashing (nutils_hash()) and pickling, all based solely on the (positional) intialization arguments, args for future reference. Keyword-only arguments are not supported. All arguments should be hashable by nutils_hash().

Positional and keyword initialization arguments are canonicalized automatically (by argument_canonicalizer()).


Consider the following class.

>>> class Plain(Immutable):
...   def __init__(self, a, b):
...     pass

Calling Plain with equivalent positional or keyword arguments produces equal instances:

>>> Plain(1, 2) == Plain(a=1, b=2)

Passing unhashable values to Plain will fail:

>>> Plain([1, 2], [3, 4]) 
Traceback (most recent call last):
TypeError: unhashable type: 'list'

list of weak references to the object (if defined)

class nutils.types.Singleton(*args, **kwargs)

Bases: Immutable

Subclass of Immutable that creates a single instance per unique set of initialization arguments.


Consider the following class.

>>> class Plain(Singleton):
...   def __init__(self, a, b):
...     pass

Calling Plain with equivalent positional or keyword arguments produces one instance:

>>> Plain(1, 2) is Plain(a=1, b=2)
__eq__(self, value, /)

Return self==value.

class nutils.types.arraydata(dtype, shape, bytes)

Bases: Singleton

hashable array container.

The container can be used for fast equality checks and for dictionary keys. Data is copied at construction and canonicalized by casting it to the platform’s primary data representation (e.g. int64 i/o int32). It can be retrieved via numpy.asarray(). Additionally the arraydata object provides direct access to the array’s shape, dtype and bytes.


>>> a = numpy.array([1,2,3])
>>> w = arraydata(a)
>>> w == arraydata([1,2,4]) # NOTE: equality only if entire array matches
>>> numpy.asarray(w)
array([1, 2, 3])
class nutils.types.frozendict(base)

Bases: Mapping

An immutable version of dict. The frozendict is hashable and both the keys and values should be hashable as well.


>>> d = frozendict({'spam': 0.0})
>>> d['spam']
>>> d['spam'] = 1.0
Traceback (most recent call last):
TypeError: 'frozendict' object does not support item assignment

list of weak references to the object (if defined)

class nutils.types.frozenmultiset(items)

Bases: Container

An immutable multiset. A multiset is a generalization of a set: items may occur more than once. Two mutlisets are equal if they have the same set of items and the same item multiplicities.

A custom item constructor can be supplied via the notation frozenmultiset[I], with I the item constructor. This is shorthand for lambda items: frozenmultiset(map(I, items)). The item constructor should be any callable that takes one argument.


>>> a = frozenmultiset(['spam', 'bacon', 'spam'])
>>> b = frozenmultiset(['sausage', 'spam'])

The frozenmultiset objects support +, - and & operators:

>>> a + b
frozenmultiset(['spam', 'bacon', 'spam', 'sausage', 'spam'])
>>> a - b
frozenmultiset(['bacon', 'spam'])
>>> a & b

The order of the items is irrelevant:

>>> frozenmultiset(['spam', 'spam', 'eggs']) == frozenmultiset(['spam', 'eggs', 'spam'])

The multiplicities, however, are not:

>>> frozenmultiset(['spam', 'spam', 'eggs']) == frozenmultiset(['spam', 'eggs'])
__and__(self, other)

Return a frozenmultiset with elements from the left and right hand sides with strict positive multiplicity, where the multiplicity is the minimum of multiplicitie of the left and right hand side.

__add__(self, other)

Return a frozenmultiset with elements from the left and right hand sides with a multiplicity equal to the sum of the left and right hand sides.

__sub__(self, other)

Return a frozenmultiset with elements from the left hand sides with a multiplicity equal to the difference of the multiplicity of the left and right hand sides, truncated to zero. Elements with multiplicity zero are omitted.


list of weak references to the object (if defined)

nutils.types.frozenarray(arg, *, copy=True, dtype=None)

Create read-only Numpy array.

  • arg (numpy.ndarray or array_like) – Input data.

  • copy (bool) – If True (the default), do not modify the argument in place. No copy is ever forced if the argument is already immutable.

  • dtype (numpy.dtype or dtype_like, optional) – The desired data-type for the array.

Return type:



Buffer-aware cache.

Returns values from a cache for previously seen arguments. Arguments must be hasheable objects or immutable Numpy arrays, the latter identified by the underlying buffer. Destruction of the buffer triggers a callback that removes the corresponding cache entry.

At present, any writeable array will silently disable caching. This bevaviour is transitional, with future versions requiring that all arrays be immutable.


When a decorated function returns an object that references its argument (for instance, by returning the argument itself), the cached value keeps an argument’s reference count from falling to zero, causing the object to remain in cache indefinitely. For this reason, care must be taken that the decorator is only applied to functions that return objects with no references to its arguments.

class nutils.types.attributes(**args)

Bases: object

Dictionary-like container with attributes instead of keys, instantiated using keyword arguments:

>>> A = attributes(foo=10, bar=True)
>>> A
attributes(bar=True, foo=10)
>>> A.foo

list of weak references to the object (if defined)


Decorator that wraps the decorated function and adds a Nutils hash.

Return a decorator that wraps the decorated function and adds a Nutils hash based solely on the given identifier. The identifier can be anything that has a Nutils hash. The identifier should represent the behavior of the function and should be changed when the behavior of the function changes.

If used on methods, this decorator behaves like staticmethod().


Make some function func hashable:

>>> @hashable_function('func v1')
... def func(a, b):
...   return a + b

The Nutils hash can be obtained by calling nutils_hash on func:

>>> nutils_hash(func).hex()

Note that the hash is based solely on the identifier passed to the decorator. If we create another function other with the same identifier as func, then both have the same hash, despite returning different values.

>>> @hashable_function('func v1')
... def other(a, b):
...   return a * b
>>> nutils_hash(other) == nutils_hash(func)
>>> func(1, 2) == other(1, 2)

The decorator can also be applied on methods:

>>> class Spam:
...   @hashable_function('Spam.eggs v1')
...   def eggs(a, b):
...     # NOTE: `self` is absent because `hashable_function` behaves like `staticmethod`.
...     return a + b

The hash of eggs accessed via the class or an instance is the same:

>>> spam = Spam()
>>> nutils_hash(Spam.eggs).hex()
>>> nutils_hash(spam.eggs).hex()