complete draft: update from Atlas
This commit is contained in:
29
15-more-types/lotto/generic_lotto.py
Normal file
29
15-more-types/lotto/generic_lotto.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import random
|
||||
|
||||
from collections.abc import Iterable
|
||||
from typing import TypeVar, Generic
|
||||
|
||||
from tombola import Tombola
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class LottoBlower(Tombola, Generic[T]): # <1>
|
||||
|
||||
def __init__(self, items: Iterable[T]) -> None: # <2>
|
||||
self._balls = list[T](items)
|
||||
|
||||
def load(self, items: Iterable[T]) -> None: # <3>
|
||||
self._balls.extend(items)
|
||||
|
||||
def pick(self) -> T: # <4>
|
||||
try:
|
||||
position = random.randrange(len(self._balls))
|
||||
except ValueError:
|
||||
raise LookupError('pick from empty LottoBlower')
|
||||
return self._balls.pop(position)
|
||||
|
||||
def loaded(self) -> bool: # <5>
|
||||
return bool(self._balls)
|
||||
|
||||
def inspect(self) -> tuple[T, ...]: # <6>
|
||||
return tuple(self._balls)
|
||||
28
15-more-types/lotto/generic_lotto_demo.py
Executable file
28
15-more-types/lotto/generic_lotto_demo.py
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
# tag::LOTTO_USE[]
|
||||
from generic_lotto import LottoBlower
|
||||
|
||||
machine = LottoBlower[int](range(1, 11)) # <1>
|
||||
|
||||
first = machine.pick() # <2>
|
||||
remain = machine.inspect() # <3>
|
||||
# end::LOTTO_USE[]
|
||||
|
||||
expected = set(i for i in range(1, 11) if i != first)
|
||||
|
||||
assert set(remain) == expected
|
||||
|
||||
print('picked:', first)
|
||||
print('remain:', remain)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
reveal_type(first)
|
||||
# Revealed type is 'builtins.int*'
|
||||
if TYPE_CHECKING:
|
||||
reveal_type(remain)
|
||||
# Revealed type is 'builtins.tuple[builtins.int*]'
|
||||
|
||||
|
||||
18
15-more-types/lotto/generic_lotto_errors.py
Executable file
18
15-more-types/lotto/generic_lotto_errors.py
Executable file
@@ -0,0 +1,18 @@
|
||||
from generic_lotto import LottoBlower
|
||||
|
||||
machine = LottoBlower[int]([1, .2])
|
||||
## error: List item 1 has incompatible type "float"; # <1>
|
||||
## expected "int"
|
||||
|
||||
machine = LottoBlower[int](range(1, 11))
|
||||
|
||||
machine.load('ABC')
|
||||
## error: Argument 1 to "load" of "LottoBlower" # <2>
|
||||
## has incompatible type "str";
|
||||
## expected "Iterable[int]"
|
||||
## note: Following member(s) of "str" have conflicts:
|
||||
## note: Expected:
|
||||
## note: def __iter__(self) -> Iterator[int]
|
||||
## note: Got:
|
||||
## note: def __iter__(self) -> Iterator[str]
|
||||
|
||||
34
15-more-types/lotto/tombola.py
Normal file
34
15-more-types/lotto/tombola.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# tag::TOMBOLA_ABC[]
|
||||
|
||||
import abc
|
||||
|
||||
class Tombola(abc.ABC): # <1>
|
||||
|
||||
@abc.abstractmethod
|
||||
def load(self, iterable): # <2>
|
||||
"""Add items from an iterable."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def pick(self): # <3>
|
||||
"""Remove item at random, returning it.
|
||||
|
||||
This method should raise `LookupError` when the instance is empty.
|
||||
"""
|
||||
|
||||
def loaded(self): # <4>
|
||||
"""Return `True` if there's at least 1 item, `False` otherwise."""
|
||||
return bool(self.inspect()) # <5>
|
||||
|
||||
def inspect(self):
|
||||
"""Return a sorted tuple with the items currently inside."""
|
||||
items = []
|
||||
while True: # <6>
|
||||
try:
|
||||
items.append(self.pick())
|
||||
except LookupError:
|
||||
break
|
||||
self.load(items) # <7>
|
||||
return tuple(items)
|
||||
|
||||
|
||||
# end::TOMBOLA_ABC[]
|
||||
Reference in New Issue
Block a user