Callbacks

Callbacks provide an interface to register other callbacks, that will be called back when the Callback object is called.

A Callback is similar to holding a pointer to a function, except it supports multiple functions.

Example:

class Data:

    def __init__(self, x: int) -> None:
        self._x = x
        self.on_changed = Callback()

    @property
    def x(self) -> int:
        return self._x

    @x.setter
    def x(self, x: int) -> None:
        self._x = x
        self.on_changed(x)

In the code above, Data contains a x property, which triggers a on_changed callback whenever x changes.

We can be notified whenever x changes by registering a function in the callback:

def on_x(x: int) -> None:
    print(f"x changed to {x}")

data = Data(10)
data.on_changed.Register(on_x)
data.x = 20

The code above will print x changed to 20, because changing data.x triggers all functions registered in data.on_changed.

An important feature is that the functions connected to the callback are weakly referenced, so methods connected to a callback won’t keep the method instance alive due to the connection.

We can unregister functions using Unregister, check if a function is registered with Contains, and unregister all connected functions with UnregisterAll.

Type Checking

New in version 1.1.0.

oop-ext also provides type-checked variants, Callback0, Callback1, Callback2, etc, which explicitly declare the number of arguments and types of the parameters supported by the callback.

Example:

class Point:
    def __init__(self, x: float, y: float) -> None:
        self._x = x
        self._y = y
        self.on_changed = Callback2[float, float]()

    def update(self, x: float, y: float) -> None:
        self._x = x
        self._y = y
        self.on_changed(x, y)


def on_point_changed(x: float, y: float) -> None:
    print(f"point changed: ({x}, {y})")


p = Point(0.0, 0.0)
p.on_changed.Register(on_point_changed)
p.update(100.0, 2.5)

In the example above, both the calls self.on_changed and on_changed.Register are properly type checked for number of arguments and types.

The method specialized signatures are only seen by the type checker, so using one of the specialized variants should have nearly zero runtime cost (only the cost of an empty subclass).

Note

The separate callback classes are needed for now, but if/when pep-0646 lands, we should be able to implement the generic variants into Callback itself.