If it walks like a duck and it quacks like a duck, then it must be a duck
Thanks to PEP544, Python now has protocols: a way to define duck typing statically. This library gives you some niceties to make common idioms easier.
pip install quacks
mypy
, you'll need to enable the plugin in
your mypy config file:
[mypy]
plugins = quacks.mypy
Defining read-only protocols is great for encouraging immutability and
working with frozen dataclasses. Use the readonly
decorator:
from quacks import readonly
@readonly
class User(Protocol):
id: int
name: str
is_premium: bool
Without this decorator, we'd have to write quite a lot of cruft, reducing readability:
class User(Protocol):
@property
def id(self) -> int: ...
@property
def name(self) -> str: ...
@property
def is_premium(self) -> bool: ...
What if you want to reuse parts of a protocol?
Imagine we have several functions who use various properties of User
.
With partial protocols you can reuse attributes without having to define
many overlapping protocols.
Inspired by clojure spec.
(exact syntax TBD)
class User(Protocol):
id: int
name: str
is_premium: bool
address: Address
class Address(Protocol):
street: str
city: str
country: str
from quacks import _
def determine_discount(u: User[_.id.is_premium]) -> int:
... # access `id` and `is_premium` attributes
def greet(u: User[_.name.address[_.country]]) -> None:
... # access `name` and `address.country` attributes
u: User = ...
determine_discount(u)
greet(u)