forked from fluentpython/example-code-2e
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtombola_subhook.py
64 lines (49 loc) · 1.55 KB
/
tombola_subhook.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
"""
Variation of ``tombola.Tombola`` implementing ``__subclasshook__``.
Tests with simple classes::
>>> Tombola.__subclasshook__(object)
NotImplemented
>>> class Complete:
... def __init__(): pass
... def load(): pass
... def pick(): pass
... def loaded(): pass
...
>>> Tombola.__subclasshook__(Complete)
True
>>> issubclass(Complete, Tombola)
"""
from abc import ABC, abstractmethod
from inspect import getmembers, isfunction
class Tombola(ABC): # <1>
@abstractmethod
def __init__(self, iterable): # <2>
"""New instance is loaded from an iterable."""
@abstractmethod
def load(self, iterable):
"""Add items from an iterable."""
@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>
try:
item = self.pick()
except LookupError:
return False
else:
self.load([item]) # put it back
return True
@classmethod
def __subclasshook__(cls, other_cls):
if cls is Tombola:
interface_names = function_names(cls)
found_names = set()
for a_cls in other_cls.__mro__:
found_names |= function_names(a_cls)
if found_names >= interface_names:
return True
return NotImplemented
def function_names(obj):
return {name for name, _ in getmembers(obj, isfunction)}