TemplateMeta

class lsst.utils.TemplateMeta[source]

Bases: type

A metaclass for abstract base classes that tie together wrapped C++ template types.

C++ template classes are most easily wrapped with a separate Python class for each template type, which results in an unnatural Python interface. TemplateMeta provides a thin layer that connects these Python classes by giving them a common base class and acting as a factory to construct them in a consistent way.

To use, simply create a new class with the name of the template class, and use TemplateMeta as its metaclass, and then call register on each of its subclasses. This registers the class with a “type key” - usually a Python representation of the C++ template types. The type key must be a hashable object - strings, type objects, and tuples of these (for C++ classes with multiple template parameters) are good choices. Alternate type keys for existing classes can be added by calling alias, but only after a subclass already been registered with a “primary” type key. For example (using Python 3 metaclass syntax):

import numpy as np
from ._image import ImageF, ImageD

class Image(metaclass=TemplateMeta):
    pass

Image.register(np.float32, ImageF)
Image.register(np.float64, ImageD)
Image.alias("F", ImageF)
Image.alias("D", ImageD)

We have intentionally used numpy types as the primary keys for these objects in this example, with strings as secondary aliases simply because the primary key is added as a dtype attribute on the the registered classes (so ImageF.dtype == numpy.float32 in the above example).

This allows user code to construct objects directly using Image, as long as an extra dtype keyword argument is passed that matches one of the type keys:

img = Image(52, 64, dtype=np.float32)

This simply forwards additional positional and keyword arguments to the wrapped template class’s constructor.

The choice of “dtype” as the name of the template parameter is also configurable, and in fact multiple template parameters are also supported, by setting a TEMPLATE_PARAMS class attribute on the ABC to a tuple containing the names of the template parameters. A TEMPLATE_DEFAULTS attribute can also be defined to a tuple of the same length containing default values for the template parameters, allowing them to be omitted in constructor calls. When the length of these attributes is more than one, the type keys passed to register and alias should be tuple of the same length; when the length of these attributes is one, type keys should generally not be tuples.

As an aid for those writing the Python wrappers for C++ classes, TemplateMeta also provides a way to add pure-Python methods and other attributes to the wrapped template classes. To add a sum method to all registered types, for example, we can just do:

class Image(metaclass=TemplateMeta):

    def sum(self):
        return np.sum(self.getArray())

Image.register(np.float32, ImageF)
Image.register(np.float64, ImageD)

Note

TemplateMeta works by overriding the __instancecheck__ and __subclasscheck__ special methods, and hence does not appear in its registered subclasses’ method resolution order or __bases__ attributes. That means its attributes are not inherited by registered subclasses. Instead, attributes added to an instance of TemplateMeta are copied into the types registered with it. These attributes will thus replace existing attributes in those classes with the same name, and subclasses cannot delegate to base class implementations of these methods.

Finally, abstract base classes that use TemplateMeta define a dict- like interface for accessing their registered subclasses, providing something like the C++ syntax for templates:

Image[np.float32] -> ImageF
Image["D"] -> ImageD

Both primary dtypes and aliases can be used as keys in this interface, which means types with aliases will be present multiple times in the dict. To obtain the sequence of unique subclasses, use the __subclasses__ method.

Methods Summary

__call__(*args, **kwds)
alias(key, subclass) Add an alias that allows an existing subclass to be accessed with a different key.
get(key[, default]) Return the subclass associated with the given key (including aliases), or default if the key is not recognized.
items() Return an iterable of (key, subclass) pairs.
keys() Return an iterable containing all keys (including aliases).
mro(() -> list) return a type’s method resolution order
register(key, subclass) Register a subclass of this ABC with the given key (a string, number, type, or other hashable).
values() Return an iterable of registered subclasses, with duplicates corresponding to any aliases.

Methods Documentation

__call__(*args, **kwds)[source]
alias(key, subclass)[source]

Add an alias that allows an existing subclass to be accessed with a different key.

get(key, default=None)[source]

Return the subclass associated with the given key (including aliases), or default if the key is not recognized.

items()[source]

Return an iterable of (key, subclass) pairs.

keys()[source]

Return an iterable containing all keys (including aliases).

mro() → list

return a type’s method resolution order

register(key, subclass)[source]

Register a subclass of this ABC with the given key (a string, number, type, or other hashable).

Register may only be called once for a given key or a given subclass.

values()[source]

Return an iterable of registered subclasses, with duplicates corresponding to any aliases.