types¶
Module with general purpose types.
- nutils.types.aspreprocessor(apply)¶
Convert
applyinto a preprocessor decorator. When applied to a function,wrapped, the returned decorator preprocesses the arguments withapplybefore callingwrapped. Theapplyfunction should return a tuple ofargs(tupleorlist) andkwargs(dict). The decorated functionwrappedwill be called withwrapped(*args, **kwargs). Theapplyfunction is allowed to change the signature of the decorated function.Examples
The following preprocessor swaps two arguments.
>>> @aspreprocessor ... def swapargs(a, b): ... return (b, a), {}
Decorating a function with
swapargswill cause the arguments to be swapped before the wrapped function is called.>>> @swapargs ... def func(a, b): ... return a, b >>> func(1, 2) (2, 1)
- nutils.types.apply_annotations(wrapped)¶
Decorator that applies annotations to arguments. All annotations should be
callables taking one argument and returning a transformed argument. All annotations are strongly recommended to be idempotent.If a parameter of the decorated function has a default value
Noneand the value of this parameter isNoneas well, the annotation is omitted.Examples
Consider the following function.
>>> @apply_annotations ... def f(a:tuple, b:int): ... return a + (b,)
When calling
fwith alistandstras arguments, theapply_annotations()decorator first appliestupleandintto the arguments before passing them to the decorated function.>>> f([1, 2], '3') (1, 2, 3)
The following example illustrates the behavior of parameters with default value
None.>>> addone = lambda x: x+1 >>> @apply_annotations ... def g(a:addone=None): ... return a
When calling
gwithout arguments or with argumentNone, the annotationaddoneis not applied. Note thatNone + 1would raise an exception.>>> g() is None True >>> g(None) is None True
When passing a different value, the annotation is applied:
>>> g(1) 2
- nutils.types.argument_canonicalizer(signature)¶
Returns a function that converts arguments matching
signatureto 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.- Parameters:
signature (
inspect.Signature) – The signature of a function to generate canonical arguments for.- Returns:
A function that returns a
tupleof atupleof positional arguments and adictof keyword arguments.- Return type:
Examples
Consider the following function.
>>> def f(a, b=4, *, c): pass
The
argument_canonicalizerforfis generated as follows:>>> canon = argument_canonicalizer(inspect.signature(f))
Calling
canonwith parameterbpassed as keyword returns arguments with parameterbas positional argument:>>> canon(1, c=3, b=2) ((1, 2), {'c': 3})
When calling
canonwithout parameterbthe default value is added to the positional arguments:>>> canon(1, c=3) ((1, 4), {'c': 3})
- nutils.types.nutils_hash(data)¶
Compute a stable hash of immutable object
data. The hash is not affected by Python’s hash randomization (seeobject.__hash__()).
- class nutils.types.CacheMeta(name, bases, namespace, **kwargs)¶
Bases:
ABCMetaMetaclass that adds caching functionality to properties and methods listed in the special attribute
__cache__. If an attribute is of typeproperty, the value of the property will be computed at the first attribute access and served from cache subsequently. If an attribute is a method, the arguments and return value are cached and the cached value will be used if a subsequent call is made with the same arguments; if not, the cache will be overwritten. The cache lives in private attributes in the instance. The metaclass supports the use of__slots__. If a subclass redefines a cached property or method (in the sense of this metaclass) of a base class, the property or method of the subclass is not automatically cached;__cache__should be used in the subclass explicitly.Examples
An example of a class with a cached property:
>>> class T(metaclass=CacheMeta): ... __cache__ = 'x', ... @property ... def x(self): ... print('uncached') ... return 1
The print statement is added to illustrate when method
x(as defined above) is called:>>> t = T() >>> t.x uncached 1 >>> t.x 1
An example of a class with a cached method:
>>> class U(metaclass=CacheMeta): ... __cache__ = 'y', ... def y(self, a): ... print('uncached') ... return a
Again, the print statement is added to illustrate when the method
y(as defined above) is called:>>> u = U() >>> u.y(1) uncached 1 >>> u.y(1) 1 >>> u.y(2) uncached 2 >>> u.y(2) 2
- class nutils.types.Immutable(*args, **kwargs)¶
Bases:
objectBase 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,argsfor future reference. Keyword-only arguments are not supported. All arguments should be hashable bynutils_hash().Positional and keyword initialization arguments are canonicalized automatically (by
argument_canonicalizer()). If the__init__method of a subclass is decorated with preprocessors (seeaspreprocessor()), the preprocessors are applied to the initialization arguments andargsbecomes the preprocessed positional part.Examples
Consider the following class.
>>> class Plain(Immutable): ... def __init__(self, a, b): ... pass
Calling
Plainwith equivalent positional or keyword arguments produces equal instances:>>> Plain(1, 2) == Plain(a=1, b=2) True
Passing unhashable values to
Plainwill fail:>>> Plain([1, 2], [3, 4]) Traceback (most recent call last): ... TypeError: unhashable type: 'list'
This can be solved by adding and applying annotations to
__init__. The following class converts its initialization arguments totupleautomaticaly:>>> class Annotated(Immutable): ... @apply_annotations ... def __init__(self, a:tuple, b:tuple): ... pass
Calling
Annotatedwith eitherlists of1, 2and3, 4ortuples gives equal instances:>>> Annotated([1, 2], [3, 4]) == Annotated((1, 2), (3, 4)) True
- class nutils.types.Singleton(*args, **kwargs)¶
Bases:
ImmutableSubclass of
Immutablethat creates a single instance per unique set of initialization arguments.Examples
Consider the following class.
>>> class Plain(Singleton): ... def __init__(self, a, b): ... pass
Calling
Plainwith equivalent positional or keyword arguments produces one instance:>>> Plain(1, 2) is Plain(a=1, b=2) True
Consider the folling class with annotations.
>>> class Annotated(Singleton): ... @apply_annotations ... def __init__(self, a:tuple, b:tuple): ... pass
Calling
Annotatedwith eitherlists of1, 2and3, 4ortuples gives a single instance:>>> Annotated([1, 2], [3, 4]) is Annotated((1, 2), (3, 4)) True
- __eq__(self, value, /)¶
Return self==value.
- class nutils.types.arraydata(dtype, shape, bytes)¶
Bases:
Singletonhashable 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 thearraydataobject provides direct access to the array’s shape, dtype and bytes.Example
>>> a = numpy.array([1,2,3]) >>> w = arraydata(a) >>> w == arraydata([1,2,4]) # NOTE: equality only if entire array matches False >>> numpy.asarray(w) array([1, 2, 3])
- nutils.types.strictint(value)¶
Converts any type that is a subclass of
numbers.Integral(e.g.intandnumpy.int64) toint, and fails otherwise. Notable differences with the behavior ofint:strictint()does not convert astrto anint.strictint()does not truncatefloatto anint.
Examples
>>> strictint(1), type(strictint(1)) (1, <class 'int'>) >>> strictint(numpy.int64(1)), type(strictint(numpy.int64(1))) (1, <class 'int'>) >>> strictint(1.0) Traceback (most recent call last): ... ValueError: not an integer: 1.0 >>> strictint('1') Traceback (most recent call last): ... ValueError: not an integer: '1'
- nutils.types.strictfloat(value)¶
Converts any type that is a subclass of
numbers.Real(e.g.floatandnumpy.float64) tofloat, and fails otherwise. Notable difference with the behavior offloat:strictfloat()does not convert astrto anfloat.
Examples
>>> strictfloat(1), type(strictfloat(1)) (1.0, <class 'float'>) >>> strictfloat(numpy.float64(1.2)), type(strictfloat(numpy.float64(1.2))) (1.2, <class 'float'>) >>> strictfloat(1.2+3.4j) Traceback (most recent call last): ... ValueError: not a real number: (1.2+3.4j) >>> strictfloat('1.2') Traceback (most recent call last): ... ValueError: not a real number: '1.2'
- nutils.types.strictstr(value)¶
Returns
valueunmodified if it is astr, and fails otherwise. Notable difference with the behavior ofstr:strictstr()does not call__str__methods of objects to automatically convert objects tostrs.
Examples
Passing a
strtostrictstr()works:>>> strictstr('spam') 'spam'
Passing an
intwill fail:>>> strictstr(1) Traceback (most recent call last): ... ValueError: not a 'str': 1
- class nutils.types.strict(*args, **kwargs)¶
Bases:
objectType checker. The function
strict[cls](value)returnsvalueunmodified ifvalueis an instance ofcls, otherwise aValueErroris raised.Examples
The
strict[int]function passes integers unmodified:>>> strict[int](1) 1
Other types fail:
>>> strict[int]('1') Traceback (most recent call last): ... ValueError: expected an object of type 'int' but got '1' with type 'str'
- __weakref__¶
list of weak references to the object (if defined)
- class nutils.types.tuple(*args, **kwargs)¶
Bases:
tupleWrapper of
tuplethat supports a user-defined item constructor via the notationtuple[I], withIthe item constructor. This is shorthand forlambda items: tuple(map(I, items)). The item constructor should be any callable that takes one argument.Examples
A tuple with items processed with
strictint():>>> tuple[strictint]((False, 1, 2, numpy.int64(3))) (0, 1, 2, 3)
If the item constructor raises an exception, the construction of the
tuplefailes accordingly:>>> tuple[strictint]((1, 2, 3.4)) Traceback (most recent call last): ... ValueError: not an integer: 3.4
- class nutils.types.frozendict(base)¶
Bases:
MappingAn immutable version of
dict. Thefrozendictis hashable and both the keys and values should be hashable as well.Custom key and value constructors can be supplied via the
frozendict[K,V]notation, withKthe key constructor andVthe value constructor, which is roughly equivalent tolambda *args, **kwargs: {K(k): V(v) for k, v in dict(*args, **kwargs).items()}.Examples
A
frozendictwithstrictstr()as key constructor andstrictfloat()as value constructor:>>> frozendict[strictstr,strictfloat]({'spam': False}) frozendict({'spam': 0.0})
Similar but with non-strict constructors:
>>> frozendict[str,float]({None: '1.2'}) frozendict({'None': 1.2})
Applying the strict constructors to invalid data raises an exception:
>>> frozendict[strictstr,strictfloat]({None: '1.2'}) Traceback (most recent call last): ... ValueError: not a 'str': None
- class nutils.types.frozenmultiset(items)¶
Bases:
ContainerAn 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], withIthe item constructor. This is shorthand forlambda items: frozenmultiset(map(I, items)). The item constructor should be any callable that takes one argument.Examples
>>> a = frozenmultiset(['spam', 'bacon', 'spam']) >>> b = frozenmultiset(['sausage', 'spam'])
The
frozenmultisetobjects support+,-and&operators:>>> a + b frozenmultiset(['spam', 'bacon', 'spam', 'sausage', 'spam']) >>> a - b frozenmultiset(['bacon', 'spam']) >>> a & b frozenmultiset(['spam'])
The order of the items is irrelevant:
>>> frozenmultiset(['spam', 'spam', 'eggs']) == frozenmultiset(['spam', 'eggs', 'spam']) True
The multiplicities, however, are not:
>>> frozenmultiset(['spam', 'spam', 'eggs']) == frozenmultiset(['spam', 'eggs']) False
- __and__(self, other)¶
Return a
frozenmultisetwith 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
frozenmultisetwith 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
frozenmultisetwith 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.
- nutils.types.frozenarray(arg, *, copy=True, dtype=None)¶
Create read-only Numpy array.
- Parameters:
arg (
numpy.ndarrayor 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.dtypeor dtype_like, optional) – The desired data-type for the array.
- Return type:
- class nutils.types.c_array(*args, **kwargs)¶
Bases:
objectConverts an array-like object to a ctypes array with a specific dtype. The function
c_array[dtype](array)returnsarrayunmodified ifarrayis already a ctypes array. Ifarrayis anumpy.ndarray, the array is converted if thedtypeis correct and the array is contiguous; otherwiseValueErroris raised. Otherwise,arrayis first converted to a contiguousnumpy.ndarrayand then converted to ctypes array. In the first two cases changes made to the ctypes array are reflected by thearrayargument: both are essentially views of the same data. In the third case, changes to eitherarrayor the returned ctypes array are not reflected by the other.- __weakref__¶
list of weak references to the object (if defined)
- nutils.types.lru_cache(func)¶
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.
Caution
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:
objectDictionary-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 10
- __weakref__¶
list of weak references to the object (if defined)
- nutils.types.hashable_function(identifier)¶
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().Examples
Make some function
funchashable:>>> @hashable_function('func v1') ... def func(a, b): ... return a + b ...
The Nutils hash can be obtained by calling
nutils_hashonfunc:>>> nutils_hash(func).hex() 'b7fed72647f6a88dd3ce3808b2710eede7d7b5a5'
Note that the hash is based solely on the identifier passed to the decorator. If we create another function
otherwith the same identifier asfunc, 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) True >>> func(1, 2) == other(1, 2) False
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
eggsaccessed via the class or an instance is the same:>>> spam = Spam() >>> nutils_hash(Spam.eggs).hex() 'dfdbb0ce20b617b17c3b854c23b2b9f7deb94cc6' >>> nutils_hash(spam.eggs).hex() 'dfdbb0ce20b617b17c3b854c23b2b9f7deb94cc6'