Skip to content

Instantly share code, notes, and snippets.

@willmcgugan
Created October 18, 2021 10:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save willmcgugan/eab215a9707a9d2b6f9730a3a704232f to your computer and use it in GitHub Desktop.
Save willmcgugan/eab215a9707a9d2b6f9730a3a704232f to your computer and use it in GitHub Desktop.
A property decorator that permits a different type in the setter
from typing import Any, Callable, Generic, NamedTuple, Optional, Tuple, TypeVar
GetterT = TypeVar("GetterT")
SetterT = TypeVar("SetterT")
GetterCallable = Callable[[Any], GetterT]
SetterCallable = Callable[[Any, SetterT], SetterT]
def typed_property(getter: GetterCallable) -> "TypedPropertyGetter[GetterT]":
return TypedPropertyGetter(getter)
class TypedPropertyGetter(Generic[GetterT]):
def __init__(self, getter: GetterCallable) -> None:
self._getter = getter
self._setter: Optional[SetterCallable] = None
def __get__(self, obj, obj_type) -> GetterT:
return self._getter(obj)
def __set__(self, obj, value: SetterT) -> SetterT:
if self._setter is None:
raise TypeError(f"{obj!r} is not callable")
return self._setter(obj, value)
def setter(
self, setter: Callable[[Any, SetterT], object]
) -> "TypedPropertyGetter[GetterT]":
self._setter = setter
return self
class ConsoleDimensions(NamedTuple):
width: int
height: int
class Console:
def __init__(self) -> None:
self._size = ConsoleDimensions(80, 25)
@typed_property
def size(self) -> ConsoleDimensions:
return self._size
@size.setter
def _set_size(self, new_size: Tuple[int, int]) -> Tuple[int, int]:
width, height = new_size
self._size = ConsoleDimensions(width, height)
return new_size
console = Console()
print(console.size)
console.size = (10, 20)
print(console.size)
console.size = ConsoleDimensions(3, 4)
print(console.size)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment