Building API classes¶
After you have written an extension, you will need a Pythonic way to interact with the data model. To do this,
you will need to write some classes that represent the data you defined in your specificiation extensions.
The pynwb.core
module has various tools to make it easier to write classes that behave like
the rest of the PyNWB API.
The pynwb.core
defines two base classes that represent the primitive structures supported by
the schema. NWBData
represents datasets and NWBContainer
represents groups. Additionally, pynwb.core
offers subclasses of these two classes for
writing classes that come with more functionality.
register_class
¶
When defining a class that represents a neurodata_type (i.e. anything that has a neurodata_type_def)
from your extension, you can tell PyNWB which neurodata_type it represents using the function
register_class
. This class can be called on its own, or used as a class decorator. The
first argument should be the neurodata_type and the second argument should be the namespace name.
The following example demonstrates how to register a class as the Python class reprsentation of the neurodata_type “MyContainer” from the namespace “my_ns”.
from pynwb import register_class
from pynwb.core import NWBContainer
class MyContainer(NWBContainer):
...
regitser_class('MyContainer', 'my_ns', MyContainer)
Alternatively, you can use register_class
as a decorator.
from pynwb import register_class
from pynwb.core import NWBContainer
@regitser_class('MyContainer', 'my_ns')
class MyContainer(NWBContainer):
...
register_class
is used with NWBData
the same way it is used with
NWBContainer
.
__nwbfields__
¶
When subclassing NWBData
or NWBContainer
, you might want to
define some properties on your class. This can be done using the __nwbfields__
class property. This
class property should be a tuple of strings that name the properties. Adding a property using this functionality
will create a property than can be set only once.
For example, the following class definition will create the MyContainer
class that has the properties foo
and bar
.
from pynwb import register_class
from pynwb.core import NWBContainer
class MyContainer(NWBContainer):
__nwbfields__ = ('foo', 'bar')
...
NWBData
¶
NWBData
should be used to represent datasets with a neurodata_type_def. This section- will discuss the available
NWBData
subclasses for representing common dataset specifications.
NWBTable
¶
If your specification extension contains a table definition i.e. a dataset with a compound data type, you should use
the NWBTable
class to represent this specification. Since NWBTable
subclasses NWBData
you can still use __nwbfields__
. In addition, you can use the
__columns__
class property to specify the columns of the table. __columns__
should be a list or a tuple of
docval
-like dictionaries.
The following example demonstrates how to define a table with the columns foo
and bar
that are of type
str and int, respectively. We also register the class as the reppresentation of the neurodata_type “MyTable”
from the namespace “my_ns”.
from pynwb import register_class
from pynwb.core import NWBTable
@register_class('MyTable', 'my_ns')
class MyTable(NWBTable):
__columns__ = [
{'name': 'foo', 'type': str, 'doc': 'the foo column'},
{'name': 'bar', 'type': int, 'doc': 'the bar column'},
]
...
NWBTableRegion
¶
NWBTableRegion
should be used to represent datasets that store a region reference. The constructor
for NWBTableRegion
. When subclassing this class, make sure you provide a way to pass in the required
arguments for the NWBTableRegion
constructor–the name of the dataset, the table that the region
applies to, and the region itself.
NWBContainer
¶
NWBContainer
should be used to represent groups with a neurodata_type_def. This section
will discuss the available NWBContainer
subclasses for representing common group specifications.
NWBDataInterface
¶
The NWB schema users the neurodata type NWBDataInterface for specifying containers that contain data that is not considered metadata. For example, NWBDataInterface is a parent neurodata type to ElectricalSeries data, but not a parent to ElectrodeGroup.
There are no requirements for using NWBDataInterface
in addition to those inherited from
NWBContainer
.
MultiContainerInterface
¶
Throughout the NWB schema, there are multiple NWBDataInterface specifications that include one or more or zero
or more of a certain neurodata type. For example, the LFP neurodata type contains one or more ElectricalSeries.
If your extension follows this pattern, you can use MultiContainerInterface
for defining
the representative class.
MultiContainerInterface
provides a way of automatically generating setters, getters, and
properties for your class. These methods are autogenerated based on a configuration provided using the class property
__clsconf__
. __clsconf__
should be a dict or a list of dicts. A single dict should be used if your
specification contains a single neurodata type. A list of dicts should be used if your specification contains
multiple neurodata types that will exist as one or more or zero or more. The contents of the dict are described
in the following table.
Key | Attribute | Required? |
---|---|---|
type |
the type of the Container | Yes |
attr |
the property name that holds the Containers | Yes |
add |
the name of the method for adding a Container | Yes |
create |
the name of the method for creating a Container | No |
get |
the name of the method for getting a Container by name | Yes |
The type
key provides a way for the setters to check for type. The property under the name given by the.
attr
key will be a LabelledDict
. If your class uses a single dict,
a __getitem__
method will be autogenerated for indexing into this LabelledDict
.
Finally, a constructor will also be autogenerated if you do not provide one in the class definition.
The following code block demonstrates using MultiContainerInterface
to build a class
that represents the neurodata type “MyDataInterface” from the namespace “my_ns”. It contains one or more containers
with neurodata type “MyContainer”.
from pynwb import register_class
from pynwb.core import MultiContainerInterface
@register_class("MyDataInterface", "my_ns")
class MyDataInterface(MultiContainerInterface):
__clsconf__ = {
'type': MyContainer,
'attr': 'containers',
'add': 'add_container',
'create': 'create_container',
'get': 'get_container',
}
...
This class will have the methods add_container
, create_container
, and get_container
. It will also have
the property containers
. The add_container
method will check to make sure that either an object of type
MyContainer
or a list/dict/tuple of objects of type MyContainer
is passed in. create_container
will
accept the exact same arguments that the MyContainer
class constructor accepts.