1import re as re
2import random
3
4PRESENT_PATTERN = r"(['\"])([A-Za-z0-9]+\s*)+\1"
5SIGNATURE_PATTERN = r"(\s*)\d+\1$"
6F_KID_YEARS_SANTA = 5
7BAD_KID = 'coal'
8
9class Kid(type):
10 instances = {}
11 def __new__(cls, name, bases, attr_dict):
12 attr_dict["is_naughty"] = False
13 if "__call__" not in attr_dict:
14 raise NotImplementedError(f"Error! Method '__call__' not implemented for this {name}!")
15
16 return super().__new__(cls, name, bases, attr_dict)
17
18 def __call__(cls, *args, **kwargs):
19 instance = super().__call__(*args, **kwargs)
20 for method_name in dir(cls):
21 method = getattr(cls, method_name)
22 if not method_name.startswith("_") and callable(method):
23 setattr(instance, method_name, cls._wrap_method(instance, method))
24
25 cls.instances[id(instance)] = [instance, 0]
26 return instance
27
28 @staticmethod
29 def _wrap_method(instance, method):
30 def wrapper(*args, **kwargs):
31 try:
32 return method(*args, **kwargs)
33 except Exception as e:
34 instance.is_naughty = True
35 return wrapper
36
37class Santa:
38 __single_instance = None
39 __kids_dict = {}
40
41 def __new__(cls):
42 if cls.__single_instance is None:
43 cls.__single_instance = object().__new__(cls)
44 return cls.__single_instance
45
46 def __iter__(self):
47 return iter(list(self.__kids_dict.values()))
48
49 def __get_wish(self, message):
50 present = re.search(PRESENT_PATTERN,message).group()
51 present = re.sub("'", "", present)
52 present = re.sub('"', "", present)
53 return present
54
55 def __call__(self, kid, wish):
56 present = self.__get_wish(wish)
57 kid_id = id(kid)
58 self.__kids_dict[kid_id] = present
59
60 def __matmul__(self, letter):
61 present = self.__get_wish(letter)
62 match = re.search(SIGNATURE_PATTERN, letter)
63 kid_id = re.sub(" ", "" , match.group())
64 self.__kids_dict[kid_id] = present
65
66 def best_present(self):
67 counts = {}
68 max_occurences = 0
69 presents = list(self.__kids_dict.values())
70 if not presents:
71 return None
72 for present in presents:
73 counts[present] = counts.get(present, 0) + 1
74 max_occurences = max(max_occurences,counts[present])
75 best_presents = [p for p, occurences in counts.items() if occurences == max_occurences]
76 return random.choice(best_presents)
77
78 def xmas(self):
79 best_present = self.best_present()
80
81 for kid_id, info in Kid.instances.items():
82 kid, years = info
83 present = self.__kids_dict.get(kid_id, best_present)
84 if years < F_KID_YEARS_SANTA:
85 Kid.instances[kid_id][1] += 1
86 if present:
87 if kid.is_naughty:
88 kid(BAD_KID)
89 kid.is_naughty = False
90 else:
91 kid(present)
92
93 self.__kids_dict = {}
...F..E.E.F.FF...F..
======================================================================
ERROR: test_present_matching (test.TestSanta.test_present_matching)
Test matching signature in the letter.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 78, in test_present_matching
self.santa @ f"""
File "/tmp/solution.py", line 63, in __matmul__
kid_id = re.sub(" ", "" , match.group())
^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'group'
======================================================================
ERROR: 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 100, in test_signature_matching
self.santa @ f"""
File "/tmp/solution.py", line 63, in __matmul__
kid_id = re.sub(" ", "" , match.group())
^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'group'
======================================================================
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_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: 'toy1' != 'toy3'
- toy1
? ^
+ 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_naughty (test.TestSanta.test_xmass_naughty)
Test a Christmas with naughty kids.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 222, in test_xmass_naughty
with self.assertRaises(ZeroDivisionError):
AssertionError: ZeroDivisionError not raised
======================================================================
FAIL: test_xmass_public_with_no_error (test.TestSanta.test_xmass_public_with_no_error)
Test a Christmas with not-so-naughty kids.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 246, in test_xmass_public_with_no_error
self.assertEqual(kid1.public_without_error(), 42)
AssertionError: None != 42
----------------------------------------------------------------------
Ran 20 tests in 0.031s
FAILED (failures=5, errors=2)
Виктор Бечев
16.12.2024 15:30`Деца, които са хвърлили изключение в някой от публичните си методи през изминалата година, са непослушни. Вместо подарък, те получават въглен, т.е. са извикани така: kid('coal'). Публичен метод дефинираме като метод, който не започва с долна черта.`
|
Дейвид Барух
16.12.2024 13:57Метода винаги ли се казва be_naughty или може да бъде какъвто и да е, просто ако дете викне метод дето дава exception да се маркира като лошо?
|
Виктор Бечев
15.12.2024 11:54Можеш да пипаш децата, но нямаш предварителна дефиниция на методите. Така че ще трябва да работиш с някаква абстракция, затова помисли какво се случва, когато бъде достъпен един метод.
|
Дейвид Барух
14.12.2024 03:00Може ли насока как да отбелязваме дете като лошо, след като е изпълнен негов 'лош' метод. Не знам до колко трябва да се пипат класовете наследници на Kid, дали може да имат атрибут който да следи дали са лоши и дали може да се сетва когато се вика 'лошия' метод? Малко съм объркан
|
n | 1 | import re as r | n | 1 | import re as re |
2 | import random | 2 | import random | ||
3 | 3 | ||||
4 | PRESENT_PATTERN = r"(['\"])([A-Za-z0-9]+\s*)+\1" | 4 | PRESENT_PATTERN = r"(['\"])([A-Za-z0-9]+\s*)+\1" | ||
5 | SIGNATURE_PATTERN = r"(\s*)\d+\1$" | 5 | SIGNATURE_PATTERN = r"(\s*)\d+\1$" | ||
6 | F_KID_YEARS_SANTA = 5 | 6 | F_KID_YEARS_SANTA = 5 | ||
7 | BAD_KID = 'coal' | 7 | BAD_KID = 'coal' | ||
8 | 8 | ||||
9 | class Kid(type): | 9 | class Kid(type): | ||
10 | instances = {} | 10 | instances = {} | ||
11 | def __new__(cls, name, bases, attr_dict): | 11 | def __new__(cls, name, bases, attr_dict): | ||
n | 12 | attr_dict["bad"] = False | n | 12 | attr_dict["is_naughty"] = False |
13 | if "__call__" not in attr_dict: | 13 | if "__call__" not in attr_dict: | ||
14 | raise NotImplementedError(f"Error! Method '__call__' not implemented for this {name}!") | 14 | raise NotImplementedError(f"Error! Method '__call__' not implemented for this {name}!") | ||
n | n | 15 | |||
15 | return type.__new__(cls, name, bases, attr_dict) | 16 | return super().__new__(cls, name, bases, attr_dict) | ||
17 | |||||
16 | def __call__(cls, *args, **kwargs): | 18 | def __call__(cls, *args, **kwargs): | ||
17 | instance = super().__call__(*args, **kwargs) | 19 | instance = super().__call__(*args, **kwargs) | ||
n | n | 20 | for method_name in dir(cls): | ||
21 | method = getattr(cls, method_name) | ||||
22 | if not method_name.startswith("_") and callable(method): | ||||
23 | setattr(instance, method_name, cls._wrap_method(instance, method)) | ||||
24 | |||||
18 | cls.instances[id(instance)] = [instance, 0] | 25 | cls.instances[id(instance)] = [instance, 0] | ||
19 | return instance | 26 | return instance | ||
n | 20 | n | 27 | ||
28 | @staticmethod | ||||
29 | def _wrap_method(instance, method): | ||||
30 | def wrapper(*args, **kwargs): | ||||
31 | try: | ||||
32 | return method(*args, **kwargs) | ||||
33 | except Exception as e: | ||||
34 | instance.is_naughty = True | ||||
35 | return wrapper | ||||
21 | 36 | ||||
22 | class Santa: | 37 | class Santa: | ||
23 | __single_instance = None | 38 | __single_instance = None | ||
24 | __kids_dict = {} | 39 | __kids_dict = {} | ||
25 | 40 | ||||
26 | def __new__(cls): | 41 | def __new__(cls): | ||
27 | if cls.__single_instance is None: | 42 | if cls.__single_instance is None: | ||
28 | cls.__single_instance = object().__new__(cls) | 43 | cls.__single_instance = object().__new__(cls) | ||
29 | return cls.__single_instance | 44 | return cls.__single_instance | ||
30 | 45 | ||||
31 | def __iter__(self): | 46 | def __iter__(self): | ||
32 | return iter(list(self.__kids_dict.values())) | 47 | return iter(list(self.__kids_dict.values())) | ||
33 | 48 | ||||
n | 34 | def __add_present(self, *args): | n | 49 | def __get_wish(self, message): |
35 | message = args[0] | ||||
36 | present = r.search(PRESENT_PATTERN,message).group() | 50 | present = re.search(PRESENT_PATTERN,message).group() | ||
37 | present = r.sub("'", "", present) | 51 | present = re.sub("'", "", present) | ||
38 | present = r.sub('"', "", present) | 52 | present = re.sub('"', "", present) | ||
53 | return present | ||||
54 | |||||
55 | def __call__(self, kid, wish): | ||||
56 | present = self.__get_wish(wish) | ||||
39 | kid_id = None | 57 | kid_id = id(kid) | ||
40 | if len(args) == 2: | ||||
41 | kid_id = id(args[1]) | ||||
42 | else: | ||||
43 | match = r.search(SIGNATURE_PATTERN, message) | ||||
44 | kid_id = r.sub(" ", "" , match.group()) | ||||
45 | |||||
46 | self.__kids_dict[kid_id] = present | 58 | self.__kids_dict[kid_id] = present | ||
47 | 59 | ||||
n | 48 | n | 60 | def __matmul__(self, letter): | |
49 | def __call__(self, kid, wish): | 61 | present = self.__get_wish(letter) | ||
50 | self.__add_present(wish, kid) | 62 | match = re.search(SIGNATURE_PATTERN, letter) | ||
63 | kid_id = re.sub(" ", "" , match.group()) | ||||
64 | self.__kids_dict[kid_id] = present | ||||
51 | 65 | ||||
n | 52 | def __matmul__(self, letter): | n | ||
53 | self.__add_present(letter) | ||||
54 | |||||
55 | def best_present(self): | 66 | def best_present(self): | ||
56 | counts = {} | 67 | counts = {} | ||
57 | max_occurences = 0 | 68 | max_occurences = 0 | ||
58 | presents = list(self.__kids_dict.values()) | 69 | presents = list(self.__kids_dict.values()) | ||
n | 59 | if presents == []: | n | 70 | if not presents: |
60 | return None | 71 | return None | ||
61 | for present in presents: | 72 | for present in presents: | ||
62 | counts[present] = counts.get(present, 0) + 1 | 73 | counts[present] = counts.get(present, 0) + 1 | ||
63 | max_occurences = max(max_occurences,counts[present]) | 74 | max_occurences = max(max_occurences,counts[present]) | ||
n | 64 | best_presents = [p for p, occs in counts.items() if occs == max_occurences] | n | 75 | best_presents = [p for p, occurences in counts.items() if occurences == max_occurences] |
65 | return random.choice(best_presents) | 76 | return random.choice(best_presents) | ||
66 | 77 | ||||
67 | def xmas(self): | 78 | def xmas(self): | ||
68 | best_present = self.best_present() | 79 | best_present = self.best_present() | ||
69 | 80 | ||||
70 | for kid_id, info in Kid.instances.items(): | 81 | for kid_id, info in Kid.instances.items(): | ||
n | 71 | kid = info[0] | n | ||
72 | years = info[1] | 82 | kid, years = info | ||
73 | present = self.__kids_dict.get(kid_id, best_present) | 83 | present = self.__kids_dict.get(kid_id, best_present) | ||
74 | if years < F_KID_YEARS_SANTA: | 84 | if years < F_KID_YEARS_SANTA: | ||
75 | Kid.instances[kid_id][1] += 1 | 85 | Kid.instances[kid_id][1] += 1 | ||
76 | if present: | 86 | if present: | ||
n | n | 87 | if kid.is_naughty: | ||
88 | kid(BAD_KID) | ||||
89 | kid.is_naughty = False | ||||
90 | else: | ||||
77 | kid(present) | 91 | kid(present) | ||
78 | 92 | ||||
79 | self.__kids_dict = {} | 93 | self.__kids_dict = {} | ||
n | 80 | n | |||
81 | santa = Santa() | ||||
82 | 94 | ||||
83 | 95 | ||||
84 | 96 | ||||
85 | 97 | ||||
86 | 98 | ||||
87 | 99 | ||||
88 | 100 | ||||
t | t | 101 |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|