Skip to content

Working with attributes

Zarr arrays and groups support custom key/value attributes, which can be useful for storing application-specific metadata. For example:

import zarr
root = zarr.create_group(store="memory://attributes-demo")
root.attrs['foo'] = 'bar'
z = root.create_array(name='zzz', shape=(10000, 10000), dtype='int32')
z.attrs['baz'] = 42
z.attrs['qux'] = [1, 4, 7, 12]
print(sorted(root.attrs))
['foo']
print('foo' in root.attrs)
True
print(root.attrs['foo'])
bar
print(sorted(z.attrs))
['baz', 'qux']
print(z.attrs['baz'])
42
print(z.attrs['qux'])
[1, 4, 7, 12]

Attributes can be deleted with the del operator:

del z.attrs['baz']
print(sorted(z.attrs))
['qux']

Note that each attribute assignment or deletion writes the node's metadata document back to the store. To change several attributes in a single write, use zarr.Array.update_attributes (or zarr.Group.update_attributes for groups), which merges the given dict into the existing attributes and returns the updated array or group:

z = z.update_attributes({'baz': 43, 'quux': True})
print(sorted(z.attrs))
['baz', 'quux', 'qux']

Internally Zarr uses JSON to store array and group attributes, so attribute values must be JSON serializable.

When copying a Zarr array with zarr.from_array, its attributes are deep-copied by default so nested dictionaries and lists are independent of the source. Deeply nested attributes can raise RecursionError during this copy, even when the source array can be stored and reopened successfully. The threshold depends on Python's recursion limit and the current call stack; it is not a fixed Zarr nesting limit. Pass attributes={} if the copy should omit attributes, or provide an explicit attribute dictionary to replace the inherited attributes.

When working with hierarchies that contain many arrays and groups, reading the attributes of each node separately can be slow. See Consolidated metadata for a way to store the metadata (including attributes) of all nodes in a hierarchy in a single document.