1import re
2import random
3
4
5class Kid(type):
6 """Represents a dynamic metaclass for kid with instance tracking."""
7
8 _all_instances = {}
9
10 def __new__(cls, name, bases, dict):
11 """Create a new class type."""
12 if '__call__' not in dict:
13 raise NotImplementedError(f'Class {name} should implement __call__!')
14
15 dict['instances'] = {}
16 return type.__new__(cls, name, bases, dict)
17
18 def __call__(cls, *args, **kwargs):
19 """Create an instances and tracking them by their ID."""
20 instance = super(Kid, cls).__call__(*args, **kwargs)
21 instance_id = id(instance)
22 instance.years = 0
23 instance.id = instance_id
24 instance.is_naughty = False
25
26 for attr_name in dir(instance):
27 if not attr_name.startswith('_'): # Public methods
28 attr = getattr(instance, attr_name)
29 if callable(attr):
30 wrapped_method = Kid._wrap_method(attr, instance)
31 setattr(instance, attr_name, wrapped_method)
32
33 cls.instances[instance_id] = instance
34 Kid._all_instances[instance_id] = instance
35 return instance
36
37 @staticmethod
38 def _wrap_method(method, instance):
39 """Wrap a method to catch exceptions and mark the instance as naughty."""
40 def wrapped(*args, **kwargs):
41 try:
42 return method(*args, **kwargs)
43 except:
44 instance.is_naughty = True
45 raise
46
47 return wrapped
48
49 @classmethod
50 def kids_grow(cls):
51 """All kids grow with one passed year."""
52 for kid_id, kid in Kid._all_instances.items():
53 kid.years += 1
54
55 @classmethod
56 def gift_presents(cls, kids_requests, most_wanted):
57 """Gift presents to all kids."""
58 for kid_id, kid in Kid._all_instances.items():
59
60 if kid.is_naughty:
61 kid('coal')
62 kid.is_naughty = False
63 continue
64 if kid.years > 5:
65 Kid._all_instances.pop(kid_id)
66 continue
67
68 if kid_id in kids_requests:
69 kid(kids_requests[kid_id])
70 else:
71 kid(most_wanted)
72
73
74class Santa(object):
75 """Represents an abstract Santa class."""
76
77 def __new__(cls):
78 """Create a new singleton instance of Santa."""
79 if not hasattr(cls, '_instance'):
80 cls._instance = super(Santa, cls).__new__(cls)
81
82 cls._instance.kids_requests = {}
83 cls._instance.wishlist = {}
84
85 return cls._instance
86
87 def __call__(self, kid, wish):
88 """Kid calls for a Christmas present wish."""
89 match = re.search(r'["\']([a-zA-Z0-9 ]+)["\']', wish)
90 if match:
91 gift = match.group(1)
92 self.kids_requests[id(kid)] = gift
93 self._add_gift_to_wishlist(gift)
94
95 def __matmul__(self, letter):
96 """Kid writes a letter with a Christmas present wish."""
97 # id_match = re.search(r'^\s*\d+\s*$', letter)
98 id_match = re.search(r'\s*(\d+)\s*', letter)
99 gift_match = re.search(r'["\']([a-zA-Z0-9 ]+)["\']', letter)
100
101 if gift_match and id_match:
102 gift = gift_match.group(1)
103 kid_id = int(id_match.group(0))
104
105 self.kids_requests[kid_id] = gift
106 self._add_gift_to_wishlist(gift)
107
108 def __iter__(self):
109 """Iterate over requests of kids."""
110 return iter(self.kids_requests.values())
111
112 def xmas(self):
113 """Take presents to the kids method."""
114 if self.kids_requests and self.wishlist:
115 # max_key = max(self.wishlist, key=self.wishlist.get)
116 max_key = Santa._get_most_wanted_present(self.wishlist)
117 Kid.gift_presents(self.kids_requests, max_key)
118 self.kids_requests.clear()
119 self.wishlist.clear()
120
121 Kid.kids_grow()
122
123 def _add_gift_to_wishlist(self, gift):
124 """Help method for adding to Santa's wishes."""
125 if gift not in self.wishlist:
126 self.wishlist[gift] = 1
127 else:
128 self.wishlist[gift] += 1
129
130 @staticmethod
131 def _get_most_wanted_present(wishlist):
132 """Get random present wish of most wanted present."""
133 max_value = max(wishlist.values())
134 max_keys = [key for key, value in wishlist.items() if value == max_value]
135 random_max_key = random.choice(max_keys) # Chooses random from list with the same value
136
137 return random_max_key
...F....F.F.F.....F.
======================================================================
FAIL: test_call_and_mail_same_kid (test.TestSanta.test_call_and_mail_same_kid)
Test that calls and mails work for the same kid.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 71, in test_call_and_mail_same_kid
self.assertEqual(list(self.santa), ['toy1'])
AssertionError: Lists differ: ['toy1', 'toy1'] != ['toy1']
First list contains 1 additional elements.
First extra element 1:
'toy1'
- ['toy1', 'toy1']
+ ['toy1']
======================================================================
FAIL: test_signature_matching (test.TestSanta.test_signature_matching)
Test matching present in the letter / call.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 119, in test_signature_matching
self.assertEqual(kid2.SECRET_PRESENT, 'toy2')
AssertionError: 'toy1' != 'toy2'
- toy1
? ^
+ toy2
? ^
======================================================================
FAIL: test_xmass (test.TestSanta.test_xmass)
Test a simple Christmas case.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 143, in test_xmass
self.assertEqual(kid3.SECRET_PRESENT, 'toy3')
AssertionError: 'toy2' != 'toy3'
- toy2
? ^
+ toy3
? ^
======================================================================
FAIL: test_xmass_kid_without_a_wish (test.TestSanta.test_xmass_kid_without_a_wish)
Test a Christmas with a kids that hasn't sent a wish.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 177, in test_xmass_kid_without_a_wish
self.assertEqual(kid3.SECRET_PRESENT, 'toy2')
AssertionError: 'toy1' != 'toy2'
- toy1
? ^
+ toy2
? ^
======================================================================
FAIL: test_xmass_years_5_and_over (test.TestSanta.test_xmass_years_5_and_over)
Test with passing years with kid aged 5 and over.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 213, in test_xmass_years_5_and_over
self.assertEqual(kid1.SECRET_PRESENT, None)
AssertionError: 'toy' != None
----------------------------------------------------------------------
Ran 20 tests in 0.048s
FAILED (failures=5)
f | 1 | import re | f | 1 | import re |
2 | import random | 2 | import random | ||
3 | 3 | ||||
4 | 4 | ||||
5 | class Kid(type): | 5 | class Kid(type): | ||
6 | """Represents a dynamic metaclass for kid with instance tracking.""" | 6 | """Represents a dynamic metaclass for kid with instance tracking.""" | ||
7 | 7 | ||||
8 | _all_instances = {} | 8 | _all_instances = {} | ||
9 | 9 | ||||
10 | def __new__(cls, name, bases, dict): | 10 | def __new__(cls, name, bases, dict): | ||
11 | """Create a new class type.""" | 11 | """Create a new class type.""" | ||
12 | if '__call__' not in dict: | 12 | if '__call__' not in dict: | ||
13 | raise NotImplementedError(f'Class {name} should implement __call__!') | 13 | raise NotImplementedError(f'Class {name} should implement __call__!') | ||
14 | 14 | ||||
15 | dict['instances'] = {} | 15 | dict['instances'] = {} | ||
n | 16 | return super(Kid, cls).__new__(cls, name, bases, dict) | n | 16 | return type.__new__(cls, name, bases, dict) |
17 | 17 | ||||
18 | def __call__(cls, *args, **kwargs): | 18 | def __call__(cls, *args, **kwargs): | ||
19 | """Create an instances and tracking them by their ID.""" | 19 | """Create an instances and tracking them by their ID.""" | ||
20 | instance = super(Kid, cls).__call__(*args, **kwargs) | 20 | instance = super(Kid, cls).__call__(*args, **kwargs) | ||
21 | instance_id = id(instance) | 21 | instance_id = id(instance) | ||
22 | instance.years = 0 | 22 | instance.years = 0 | ||
23 | instance.id = instance_id | 23 | instance.id = instance_id | ||
24 | instance.is_naughty = False | 24 | instance.is_naughty = False | ||
25 | 25 | ||||
26 | for attr_name in dir(instance): | 26 | for attr_name in dir(instance): | ||
27 | if not attr_name.startswith('_'): # Public methods | 27 | if not attr_name.startswith('_'): # Public methods | ||
28 | attr = getattr(instance, attr_name) | 28 | attr = getattr(instance, attr_name) | ||
29 | if callable(attr): | 29 | if callable(attr): | ||
30 | wrapped_method = Kid._wrap_method(attr, instance) | 30 | wrapped_method = Kid._wrap_method(attr, instance) | ||
31 | setattr(instance, attr_name, wrapped_method) | 31 | setattr(instance, attr_name, wrapped_method) | ||
32 | 32 | ||||
33 | cls.instances[instance_id] = instance | 33 | cls.instances[instance_id] = instance | ||
34 | Kid._all_instances[instance_id] = instance | 34 | Kid._all_instances[instance_id] = instance | ||
35 | return instance | 35 | return instance | ||
36 | 36 | ||||
37 | @staticmethod | 37 | @staticmethod | ||
38 | def _wrap_method(method, instance): | 38 | def _wrap_method(method, instance): | ||
39 | """Wrap a method to catch exceptions and mark the instance as naughty.""" | 39 | """Wrap a method to catch exceptions and mark the instance as naughty.""" | ||
40 | def wrapped(*args, **kwargs): | 40 | def wrapped(*args, **kwargs): | ||
41 | try: | 41 | try: | ||
42 | return method(*args, **kwargs) | 42 | return method(*args, **kwargs) | ||
n | 43 | except Exception: | n | 43 | except: |
44 | instance.is_naughty = True | 44 | instance.is_naughty = True | ||
45 | raise | 45 | raise | ||
46 | 46 | ||||
47 | return wrapped | 47 | return wrapped | ||
48 | 48 | ||||
49 | @classmethod | 49 | @classmethod | ||
50 | def kids_grow(cls): | 50 | def kids_grow(cls): | ||
51 | """All kids grow with one passed year.""" | 51 | """All kids grow with one passed year.""" | ||
52 | for kid_id, kid in Kid._all_instances.items(): | 52 | for kid_id, kid in Kid._all_instances.items(): | ||
53 | kid.years += 1 | 53 | kid.years += 1 | ||
54 | 54 | ||||
55 | @classmethod | 55 | @classmethod | ||
56 | def gift_presents(cls, kids_requests, most_wanted): | 56 | def gift_presents(cls, kids_requests, most_wanted): | ||
57 | """Gift presents to all kids.""" | 57 | """Gift presents to all kids.""" | ||
58 | for kid_id, kid in Kid._all_instances.items(): | 58 | for kid_id, kid in Kid._all_instances.items(): | ||
59 | 59 | ||||
60 | if kid.is_naughty: | 60 | if kid.is_naughty: | ||
61 | kid('coal') | 61 | kid('coal') | ||
62 | kid.is_naughty = False | 62 | kid.is_naughty = False | ||
63 | continue | 63 | continue | ||
64 | if kid.years > 5: | 64 | if kid.years > 5: | ||
65 | Kid._all_instances.pop(kid_id) | 65 | Kid._all_instances.pop(kid_id) | ||
66 | continue | 66 | continue | ||
67 | 67 | ||||
68 | if kid_id in kids_requests: | 68 | if kid_id in kids_requests: | ||
69 | kid(kids_requests[kid_id]) | 69 | kid(kids_requests[kid_id]) | ||
70 | else: | 70 | else: | ||
71 | kid(most_wanted) | 71 | kid(most_wanted) | ||
n | 72 | n | |||
73 | kid.years += 1 | ||||
74 | 72 | ||||
75 | 73 | ||||
76 | class Santa(object): | 74 | class Santa(object): | ||
77 | """Represents an abstract Santa class.""" | 75 | """Represents an abstract Santa class.""" | ||
78 | 76 | ||||
79 | def __new__(cls): | 77 | def __new__(cls): | ||
80 | """Create a new singleton instance of Santa.""" | 78 | """Create a new singleton instance of Santa.""" | ||
81 | if not hasattr(cls, '_instance'): | 79 | if not hasattr(cls, '_instance'): | ||
82 | cls._instance = super(Santa, cls).__new__(cls) | 80 | cls._instance = super(Santa, cls).__new__(cls) | ||
83 | 81 | ||||
84 | cls._instance.kids_requests = {} | 82 | cls._instance.kids_requests = {} | ||
85 | cls._instance.wishlist = {} | 83 | cls._instance.wishlist = {} | ||
86 | 84 | ||||
87 | return cls._instance | 85 | return cls._instance | ||
88 | 86 | ||||
89 | def __call__(self, kid, wish): | 87 | def __call__(self, kid, wish): | ||
90 | """Kid calls for a Christmas present wish.""" | 88 | """Kid calls for a Christmas present wish.""" | ||
91 | match = re.search(r'["\']([a-zA-Z0-9 ]+)["\']', wish) | 89 | match = re.search(r'["\']([a-zA-Z0-9 ]+)["\']', wish) | ||
92 | if match: | 90 | if match: | ||
93 | gift = match.group(1) | 91 | gift = match.group(1) | ||
94 | self.kids_requests[id(kid)] = gift | 92 | self.kids_requests[id(kid)] = gift | ||
95 | self._add_gift_to_wishlist(gift) | 93 | self._add_gift_to_wishlist(gift) | ||
96 | 94 | ||||
97 | def __matmul__(self, letter): | 95 | def __matmul__(self, letter): | ||
98 | """Kid writes a letter with a Christmas present wish.""" | 96 | """Kid writes a letter with a Christmas present wish.""" | ||
99 | # id_match = re.search(r'^\s*\d+\s*$', letter) | 97 | # id_match = re.search(r'^\s*\d+\s*$', letter) | ||
100 | id_match = re.search(r'\s*(\d+)\s*', letter) | 98 | id_match = re.search(r'\s*(\d+)\s*', letter) | ||
101 | gift_match = re.search(r'["\']([a-zA-Z0-9 ]+)["\']', letter) | 99 | gift_match = re.search(r'["\']([a-zA-Z0-9 ]+)["\']', letter) | ||
102 | 100 | ||||
103 | if gift_match and id_match: | 101 | if gift_match and id_match: | ||
104 | gift = gift_match.group(1) | 102 | gift = gift_match.group(1) | ||
105 | kid_id = int(id_match.group(0)) | 103 | kid_id = int(id_match.group(0)) | ||
106 | 104 | ||||
107 | self.kids_requests[kid_id] = gift | 105 | self.kids_requests[kid_id] = gift | ||
108 | self._add_gift_to_wishlist(gift) | 106 | self._add_gift_to_wishlist(gift) | ||
n | 109 | else: | n | ||
110 | Kid.kids_grow() | ||||
111 | 107 | ||||
112 | def __iter__(self): | 108 | def __iter__(self): | ||
113 | """Iterate over requests of kids.""" | 109 | """Iterate over requests of kids.""" | ||
114 | return iter(self.kids_requests.values()) | 110 | return iter(self.kids_requests.values()) | ||
115 | 111 | ||||
116 | def xmas(self): | 112 | def xmas(self): | ||
117 | """Take presents to the kids method.""" | 113 | """Take presents to the kids method.""" | ||
118 | if self.kids_requests and self.wishlist: | 114 | if self.kids_requests and self.wishlist: | ||
119 | # max_key = max(self.wishlist, key=self.wishlist.get) | 115 | # max_key = max(self.wishlist, key=self.wishlist.get) | ||
n | 120 | max_key = self._get_most_wanted_present(self.wishlist) | n | 116 | max_key = Santa._get_most_wanted_present(self.wishlist) |
121 | Kid.gift_presents(self.kids_requests, max_key) | 117 | Kid.gift_presents(self.kids_requests, max_key) | ||
122 | self.kids_requests.clear() | 118 | self.kids_requests.clear() | ||
123 | self.wishlist.clear() | 119 | self.wishlist.clear() | ||
n | 124 | else: | n | 120 | |
125 | Kid.kids_grow() | 121 | Kid.kids_grow() | ||
126 | 122 | ||||
127 | def _add_gift_to_wishlist(self, gift): | 123 | def _add_gift_to_wishlist(self, gift): | ||
128 | """Help method for adding to Santa's wishes.""" | 124 | """Help method for adding to Santa's wishes.""" | ||
129 | if gift not in self.wishlist: | 125 | if gift not in self.wishlist: | ||
130 | self.wishlist[gift] = 1 | 126 | self.wishlist[gift] = 1 | ||
131 | else: | 127 | else: | ||
132 | self.wishlist[gift] += 1 | 128 | self.wishlist[gift] += 1 | ||
133 | 129 | ||||
t | t | 130 | @staticmethod | ||
134 | def _get_most_wanted_present(self, wishlist): | 131 | def _get_most_wanted_present(wishlist): | ||
135 | """Get random present wish of most wanted present.""" | 132 | """Get random present wish of most wanted present.""" | ||
136 | max_value = max(wishlist.values()) | 133 | max_value = max(wishlist.values()) | ||
137 | max_keys = [key for key, value in wishlist.items() if value == max_value] | 134 | max_keys = [key for key, value in wishlist.items() if value == max_value] | ||
138 | random_max_key = random.choice(max_keys) # Chooses random from list with the same value | 135 | random_max_key = random.choice(max_keys) # Chooses random from list with the same value | ||
139 | 136 | ||||
140 | return random_max_key | 137 | return random_max_key |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|