Skip to content

Instantly share code, notes, and snippets.

@campagnola
Created October 31, 2019 20:13
Show Gist options
  • Save campagnola/5630870e45ed8a9f66d854fee11271f0 to your computer and use it in GitHub Desktop.
Save campagnola/5630870e45ed8a9f66d854fee11271f0 to your computer and use it in GitHub Desktop.
Composable, inheritance-free data modeling in Python
"""
Goals:
- Extensible data modeling in python
- Does _not_ use inheritance as a mechanism for model extension
- Lightweight / simple infrastructure
- Data objects have reasonably nice user API
"""
class Model:
"""Base class providing infrastructure for composable data modeling
"""
def __init__(self, attachments=()):
self._attachments = {}
self._attached_types = set()
for a in attachments:
self.attach(a)
def attach(self, attachment):
name = attachment._attaches_to[type(self)]
assert name not in self._attachments
self._attachments[name] = attachment
self._attached_tyes.add(type(attachment))
def __getattr__(self, name):
return self._attachments[name]
def has(self, attachment_type):
return attachment_type in self._attached_types
### Data model classes
class Image(Model):
data = None
class AcquiredImageMeta(Model):
_attaches_to = {Image: 'acq'}
timestamp = None
transform = None
device = None
class CameraImageMeta(Model):
_attaches_to = {AcquiredImageMeta: 'camera'}
binning = None
region = None
exposure = None
class LaserScanImageMeta(Model):
_attaches_to = {AcquiredImageMeta: 'laser_scan'}
pixel_dwell_time = None
laser_power = None
laser_wavelength = None
### Use example
# one-shot construction:
img = Image(data, attachments=[
AcquiredImageMeta(timestamp, transform, attachments=[
CameraImageMeta(region, binning, exposure)
])
])
# piecewise construction (because sometimes all of the pieces can't be
# easily instantiated at the same time)
img = Image(data)
img.attach(AcquiredImageMeta(timestamp, transform))
img.acq.attach(CameraImageMeta(region, binning, exposure))
# introspection
assert img.has(AcquiredImageMeta)
assert img.acq.has(CameraImageMeta)
# with modifications to the Model class, we could also support this kind of introspection:
# (all Image instances have an `acq` attribute, but it is set to None if the attachment is not present)
assert img.acq is not None
# data access
assert img.acq.timestamp == timestamp
assert img.acq.camera.binning == binning
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment