adding sentinel metaclass example

This commit is contained in:
Luciano Ramalho 2021-05-23 22:12:13 -03:00
parent 8a330d822b
commit f248baf418
14 changed files with 220 additions and 1 deletions

View File

@ -0,0 +1,26 @@
from typing import TypeVar, TYPE_CHECKING
from decimal import Decimal
# tag::TYPEVAR_RESTRICTED[]
RT = TypeVar('RT', float, Decimal)
def triple1(a: RT) -> RT:
return a * 3
res1 = triple1(1, 2)
if TYPE_CHECKING:
reveal_type(res1)
# end::TYPEVAR_RESTRICTED[]
# tag::TYPEVAR_BOUNDED[]
BT = TypeVar('BT', bound=float)
def triple2(a: BT) -> BT:
return a * 3
res2 = triple2(1, 2)
if TYPE_CHECKING:
reveal_type(res2)
# tag::TYPEVAR_BOUNDED[]

View File

@ -41,7 +41,7 @@ T_contra = TypeVar('T_contra', contravariant=True)
class TrashCan(Generic[T_contra]):
def put(self, trash: T_contra) -> None:
"""Store trash until dumped..."""
"""Store trash until dumped."""
class Cafeteria:

View File

@ -0,0 +1,45 @@
# tag::TRASH_TYPES[]
from typing import TypeVar, Generic
class Refuse: # <1>
"""Any refuse."""
class Biodegradable(Refuse):
"""Biodegradable refuse."""
class Compostable(Biodegradable):
"""Compostable refuse."""
T_contra = TypeVar('T_contra', contravariant=True) # <2>
class TrashCan(Generic[T_contra]): # <3>
def put(self, refuse: T_contra) -> None:
"""Store trash until dumped."""
def deploy(trash_can: TrashCan[Biodegradable]):
"""Deploy a trash can for biodegradable refuse."""
# end::TRASH_TYPES[]
################################################ contravariant trash can
# tag::DEPLOY_TRASH_CANS[]
bio_can: TrashCan[Biodegradable] = TrashCan()
deploy(bio_can)
trash_can: TrashCan[Refuse] = TrashCan()
deploy(trash_can)
# end::DEPLOY_TRASH_CANS[]
################################################ more specific trash can
# tag::DEPLOY_NOT_VALID[]
compost_can: TrashCan[Compostable] = TrashCan()
deploy(compost_can)
# end::DEPLOY_NOT_VALID[]
## Argument 1 to "deploy" has
## incompatible type "TrashCan[Compostable]"
## expected "TrashCan[Biodegradable]"

View File

@ -0,0 +1,48 @@
from typing import TypeVar, Generic
class Beverage:
"""Any beverage."""
class Juice(Beverage):
"""Any fruit juice."""
class OrangeJuice(Juice):
"""Delicious juice from Brazilian oranges."""
# tag::BEVERAGE_TYPES[]
T_co = TypeVar('T_co', covariant=True) # <1>
class BeverageDispenser(Generic[T_co]): # <2>
def __init__(self, beverage: T_co) -> None:
self.beverage = beverage
def dispense(self) -> T_co:
return self.beverage
def install(dispenser: BeverageDispenser[Juice]) -> None: # <3>
"""Install a fruit juice dispenser."""
# end::BEVERAGE_TYPES[]
################################################ covariant dispenser
# tag::INSTALL_JUICE_DISPENSERS[]
juice_dispenser = BeverageDispenser(Juice())
install(juice_dispenser)
orange_juice_dispenser = BeverageDispenser(OrangeJuice())
install(orange_juice_dispenser)
# end::INSTALL_JUICE_DISPENSERS[]
################################################ not a juice dispenser
beverage_dispenser = BeverageDispenser(Beverage())
## Argument 1 to "install" has
## incompatible type "BeverageDispenser[Beverage]"
## expected "BeverageDispenser[Juice]"
install(beverage_dispenser)

View File

@ -0,0 +1,54 @@
# tag::BEVERAGE_TYPES[]
from typing import TypeVar, Generic
class Beverage: # <1>
"""Any beverage."""
class Juice(Beverage):
"""Any fruit juice."""
class OrangeJuice(Juice):
"""Delicious juice from Brazilian oranges."""
T = TypeVar('T') # <2>
class BeverageDispenser(Generic[T]): # <3>
"""A dispenser parameterized on the beverage type."""
def __init__(self, beverage: T) -> None:
self.beverage = beverage
def dispense(self) -> T:
return self.beverage
def install(dispenser: BeverageDispenser[Juice]) -> None: # <4>
"""Install a fruit juice dispenser."""
# end::BEVERAGE_TYPES[]
################################################ exact type
# tag::INSTALL_JUICE_DISPENSER[]
juice_dispenser = BeverageDispenser(Juice())
install(juice_dispenser)
# end::INSTALL_JUICE_DISPENSER[]
################################################ variant dispenser
# tag::INSTALL_BEVERAGE_DISPENSER[]
beverage_dispenser = BeverageDispenser(Beverage())
install(beverage_dispenser)
## Argument 1 to "install" has
## incompatible type "BeverageDispenser[Beverage]"
## expected "BeverageDispenser[Juice]"
# end::INSTALL_BEVERAGE_DISPENSER[]
################################################ variant dispenser
# tag::INSTALL_ORANGE_JUICE_DISPENSER[]
orange_juice_dispenser = BeverageDispenser(OrangeJuice())
install(orange_juice_dispenser)
# end::INSTALL_ORANGE_JUICE_DISPENSER[]
## Argument 1 to "install" has
## incompatible type "BeverageDispenser[OrangeJuice]"
## expected "BeverageDispenser[Juice]"

View File

@ -0,0 +1,24 @@
"""
>>> class Missing(Sentinel): pass
>>> Missing
<Missing>
>>> class CustomRepr(Sentinel):
... repr = '*** sentinel ***'
...
>>> CustomRepr
*** sentinel ***
"""
class SentinelMeta(type):
def __repr__(cls):
try:
return cls.repr
except AttributeError:
return f'<{cls.__name__}>'
class Sentinel(metaclass=SentinelMeta):
def __new__(cls):
return cls

View File

@ -0,0 +1,22 @@
from sentinel import Sentinel
class PlainSentinel(Sentinel): pass
class SentinelCustomRepr(Sentinel):
repr = '***SentinelRepr***'
def test_repr():
assert repr(PlainSentinel) == '<PlainSentinel>'
def test_pickle():
from pickle import dumps, loads
s = dumps(PlainSentinel)
ps = loads(s)
assert ps is PlainSentinel
def test_custom_repr():
assert repr(SentinelCustomRepr) == '***SentinelRepr***'