32
loading...
This website collects cookies to deliver better user experience
If it walks like a duck and it quacks like a duck, then it must be a duck
def calculate_windowed_avg(
measurements: Union[List[TemperatureMeasurement], List[HumidityMeasurement]],
window_size: timedelta,
field_name: str
) -> Dict[datetime, float]:
window_upper_bound = measurements[0].timestamp + window_size
current_window = []
window_averages = OrderedDict()
for m in measurements:
# various calculations happen here
# based on the timestamp of each measurement
...
return window_averages
field_name
) in a rolling window. At the time of writing this function, we were using it for TemperatureMeasurement
and HumidityMeasurement
, but it is very likely we'll want to use it for different types of measurements in the future.timestamp
field. So instead of specifying each different type that has adheres to this contract, we'd like to tell the type checker that we only care about having a timestamp
field to work with.Protocol
from the typing
module lets us do that. Just like with duck typing, Protocols let you specify the behaviour or attributes you expect, without caring about the type. Here is what that looks like:from typing import Protocol, List, Dict
from datetime import datetime
class MeasurementLike(Protocol):
timestamp: datetime
def calculate_windowed_avg(
measurements: List[MeasurementLike],
window_size: timedelta,
field_name: str
) -> Dict[datetime, float]:
window_upper_bound = measurements[0].timestamp + window_size
...
measurements
but it does know what those items have a timestamp
field because they adhere to the MeasurementLike
Protocol.TypeVar
for even more generic functions that are still type checked to some extend. One use-case that comes to mind, is when you don't care about the input type to a function, as long as it follows a protocol, but you also want to guarantee that the output of the function is of the same type as the input, no matter what the exact type is.from typing import Protocol, TypeVar
from datetime import datetime
class MeasurementLike(Protocol):
timestamp: datetime
M = TypeVar('M', bound=MeasurementLike)
def measurement_as_timezone(measurement: M, tz: tzinfo) -> M:
measurement.timestamp = measurement.timestamp.astimezone(tz)
return measurement
timestamp
field and guarantees that the output will be of the same type as the input.