# Lunch and Learn ## The Latest on Python's Type System <img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*XP9e7KuJAIYXPF7PN2tsUA.png" alt="pc" style="width:600px;"/> --- ## First of all.....why bother? - Code clarity - Self documenting - Verification (linting / static type checkers) - Refactoring ease --- ## Type annotations (Unions) Streamlined syntax ```python def mkdir(path: str, exist_ok=Optional[bool]) -> \ Optional[UnixError] ``` becomes ```python def mkdir(path: str, exist_ok=bool|None) -> \ None | UnixError ``` --- ## Type annotations (Lists) Streamlined syntax ```python def split(s:str) -> List[str] ``` becomes ```python def split(s:str) -> [str] ``` --- ## Type annotations (Tuples) Streamlined syntax ```python def boundaries(data:List[float]) -> Tuple[float, float] ``` becomes ```python def boundaries(data: List[float]) -> (float, float) ``` --- ## Type annotations (Numpy) https://numpy.org/devdocs/reference/typing.tml - `ArrayLike`: objects that can be converted to dtypes - `DtypeLike`: objects that can be converted to dtypes --- ## Type annotations (Numpy Example) ```python import numpy as np import numpy.typing as npt print(npt.NDArray) > numpy.ndarray[typing.Any, numpy.dtype[+ScalarType]] print(npt.NDArray[np.float64]) > numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]] NDArrayInt = npt.NDArray[np.int_] a: NDArrayInt = np.arange(10) ``` --- ### Literals ```python ## Creating from raw data from typing import List, Literal, get_args MaturityType = Literal['Monthly', 'Weekly', 'Quarterly', 'Daily'] MATURITY_TYPES : List[MaturityType] = list(get_args(MaturityType)) def parse_maturity_type(s: str) -> MaturityType: """Parses a maturity type safely from a string.""" # Type checkers will ordain your type from here! if s in MATURITY_TYPES: return s else: raise Exception(f"Not a maturity type: {s}.") ``` --- ## Pattern matching - Introduced in python 3.10 - No *sealed* classes, but there are work-arounds --- ## Enums - Similar to Java and C# Enums - Closed / Nominal - Not a *Tagged Union* like Rust, OCaml, F# ```python from enum import Enum class Side(Enum): BUY = 'B' SELL = 'S' def signed(self, price: float) -> float: match self: case Side.BUY: return price case Side.SELL: return -price ``` --- ## Adhoc error composition with Unions ```python! class RejectReason: ... class HttpRequestError: ... class Order: ... class Fill: ... class Cancel: ... def submit_order(order: Order) -> \ Fill | Cancel | RejectReason | HttpRequestError: ... ``` --- ## Result Type (1) ```python from typing import Generic, TypeVar T=TypeVar('T') E=TypeVar('E', bound=Exception) class Result(Generic[T, E]): def ok(self) -> T | None: return None def error(self) -> E | None: return None def unwrap(self) -> T: if (ret := self.ok()) is not None: return ret elif (err := self.error()): raise err else: raise Exception("Nothing to unwrap") ``` --- ## Result Type (2) ```python from dataclasses import dataclass @dataclass class Ok(Reult[T,E]): _ok : T def ok(self): return self._ok @dataclass class Error(Result[T,E]): _error : E def error(self): return self._error ``` --- ## Rustedpy Result (1) https://github.com/rustedpy/result > A result value can be either `Ok(value)` or `Err(error)`, with a way to differentiate between the two. Ok and Err are both classes encapsulating an arbitrary value. `Result[T, E]` is a generic type alias for `typing.Union[Ok[T], Err[E]]`. --- ## Rustedpy Result (2) ```python from result import Ok, Err, Result def get_user_by_email(email: str) -> Result[User, str]: """ Return the user instance or an error message. """ if not user_exists(email): return Err('User does not exist') if not user_active(email): return Err('User is inactive') user = get_user(email) return Ok(user) ``` --- ## Dry Python https://github.com/dry-python/ **Monads for python!** - `returns` library introduces `Maybe` and `Result` types - with `bind`, `flow,` `map`, operators etc. - "functional" dependency injection --- ## Type checking in practice - Use `pyright` for linting and static type checking with vscode - Alternatively can use `mypy` - Consider the `typeguard` pytest plugin - **Only as good as your type annotations!** --- ### Limitations - No Tail Cail Optimization (TCO) - `lambda` keyword is heavy handed - No standardized result type or monad api - Creator of python is anti-functional --- ### Thanks for joining! Questions? ---
{"metaMigratedAt":"2023-06-18T04:33:53.004Z","metaMigratedFrom":"YAML","title":"The Latest on Python's \"Type\" System","breaks":true,"description":"View the slide with \"Slide Mode\".","contributors":"[{\"id\":\"bf4db049-911e-493a-ad5c-ae15eadcd73d\",\"add\":7357,\"del\":2405}]"}
    193 views