Callbacks#
Callbacks are a way to extend the functionality of an experiment.
Enabale them by passing them to the @experiment
decorator.
Avaliable callbacks#
- class digital_experiments.callbacks.SaveLogs(name: str = 'logs.txt')#
Responsible for saving logs to file
- Parameters:
name (str, optional) – The name of the file to save logs to. Defaults to
logs.txt
.
Example
from digital_experiments import experiment, SaveLogs @experiment(callbacks=[SaveLogs("my-logs")]) def example(): print("hello world") example() id = example.observations()[-1].id artefacts = example.artefacts(id) # returns [Path("<some>/<path>/<id>/my-logs")] artefacts[0].read_text() # returns 'hello world\n'
Implementing your own callbacks#
- class digital_experiments.callbacks.Callback#
Abstract base class for callbacks
Subclass this class and override any of the hook methods to implement relevant, custom behaviour.
- end(observation: Observation) None #
Called at the end of each run of an experiment. Callbacks typically modify
observation.metadata
to record additional information.
The callback lifecycle#
We can learn about the lifecycle of a callback by implementing a simple callback of our own:
from typing import Callable, Any
from digital_experiments import Callback, Observation
class DummyCallback(Callback):
def __init__(self):
super().__init__()
print("I'm being initialized")
def setup(self, function: Callable) -> None:
print("I'm being setup")
def start(self, id: str, config: dict[str, Any]) -> None:
print(f"Experiment {id} is starting")
def end(self, observation: Observation) -> None:
print(f"Experiment {observation.id} has ended")
observation.metadata["dummy"] = "hello there"
Each callback to be used by an experiment is instantiated by the user and passed
to the @experiment
decorator:
>>> callback = DummyCallback()
I'm being initialized
When an experiment is first imported/defined, the setup
method is called:
>>> @experiment(callbacks=[callback])
... def my_experiment():
... print("my_experiment is running")
I'm being setup
Hint
The __init__
and setup
steps typically occur one after the other when the experiment is
decorated using compact syntax: @experiment(callbacks=[DummyCallback()])
Everytime the experiment is subsequently run, the start
and end
methods are called:
>>> my_experiment()
Experiment 1 is starting
my_experiment is running
Experiment 1 has ended
>>> my_experiment()
Experiment 2 is starting
my_experiment is running
Experiment 2 has ended
We can see that the end
method has worked:
>>> my_experiment.observations[0].metadata["dummy"]
hello there