Домашни > Подаръци ще има за всички от сърце > Решения > Решението на Росица Илиева

Резултати
9 точки от тестове
1 точки от учител

10 точки общо

18 успешни теста
2 неуспешни теста
Код

  1import re
  2
  3class SingletonType(type):
  4    def __new__(mcs, name, bases, attrs):
  5        def singleton_new(cls):
  6            if not hasattr(cls, 'instance'):
  7                cls.instance = object.__new__(cls)
  8            return cls.instance
  9        attrs['__new__'] = singleton_new
 10        return type.__new__(mcs, name, bases, attrs)
 11
 12
 13class Santa(metaclass = SingletonType):
 14    """A class for greedy kids."""
 15    all_kids = set()
 16    kids_objects = {}
 17    kids_ages = {}
 18    gifts = {}
 19    naughty_kids = set()
 20
 21    def extract_gift(self, letter: str):
 22        """Extracts a gift from a letter or call."""
 23        pattern_gift = r'(?:\")([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\")|(?:\')([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\')'
 24        pattern = re.compile(pattern_gift, re.MULTILINE)
 25        gifts = pattern.findall(letter)
 26
 27        for gift_tuple in reversed(gifts):
 28            for gift in gift_tuple:
 29                if gift:
 30                    return gift
 31        return None
 32
 33    def extract_signature_from_letter(self, letter: str):
 34        """Extracts a signiture from a letter."""
 35        pattern_signature = r'\s*(\d+)\s*'
 36        pattern = re.compile(pattern_signature, re.MULTILINE)
 37        signature = pattern.findall(letter)
 38        if signature:
 39            return signature[-1]
 40        return None
 41
 42    def __matmul__(self, letter: str):
 43        """Predefined operator @ when a kid writes a letter to Santa."""
 44        gift = self.extract_gift(letter)
 45        signature = self.extract_signature_from_letter(letter)
 46
 47        index = int(signature)
 48        self.gifts[index] = gift
 49        self.all_kids.add(index)
 50        self.kids_ages[index] = 0
 51        if id(signature) in self.kids_objects:
 52            self.kids_objects[signature] = signature
 53
 54    def __call__(self, child, wish: str):
 55        """Predefined operator () when a kid calls Santa."""
 56        index = id(child)
 57        self.gifts[index] = self.extract_gift(wish)
 58
 59    def __iter__(self):
 60        return iter(self.gifts.values())
 61
 62    def get_most_wanted_gift_this_year(self):
 63        """Calculates what is the most wanted gift this year."""
 64        this_years_gifts = [gift for gift in self.gifts.values() if gift is not None]
 65
 66        if set(this_years_gifts):
 67            most_wanted_gift_this_year = max(set(this_years_gifts) , key = this_years_gifts.count)
 68        else:
 69            most_wanted_gift_this_year = None
 70        return most_wanted_gift_this_year
 71
 72    def remove_kid(self, keys_to_delete):
 73        """Deletes kid because it is too old for presents."""
 74        for key in keys_to_delete:
 75            del self.gifts[key]
 76            del self.kids_ages[key]
 77            del self.kids_objects[key]
 78            self.all_kids.remove(key)
 79
 80    def mark_as_naughty(self, kid):
 81        """Marks a kid as naughty."""
 82        if id(kid) in self.all_kids:
 83            self.naughty_kids.add(id(kid))
 84
 85    def xmas(self):
 86        """Time for presents."""
 87        for key in self.kids_ages:
 88            self.kids_ages[key] += 1
 89
 90        most_wanted_gift = self.get_most_wanted_gift_this_year()
 91        keys_to_delete = []
 92
 93        for kid, age in self.kids_ages.items():
 94            cur_gift = self.gifts[kid]
 95            if age > 5:
 96                keys_to_delete.append(kid)
 97                continue
 98            elif age <= 5:
 99                kid_obj = self.kids_objects.get(kid)
100                if all(value == None for value in self.gifts.values()):
101                    break
102                elif kid in self.naughty_kids:
103                    kid_obj("coal")
104                elif cur_gift is None:
105                    kid_obj(most_wanted_gift)
106                else:
107                    kid_obj(cur_gift)
108
109        for cur_gift in self.gifts:
110            self.gifts[cur_gift] = None
111
112        self.remove_kid(keys_to_delete)
113        self.naughty_kids.clear()
114
115
116def detect_naughty_kid(func):
117    """Checks if the kid is naughty."""
118    def wrapper(self, *args, **kwargs):
119        try:
120            return func(self, *args, **kwargs)
121        except Exception:
122            Santa().mark_as_naughty(self)
123            raise
124    return wrapper
125
126
127class Kid(type):
128    """A class that defines a kid."""
129    def __new__(mcs, name, bases, attrs):
130        if '__call__' not in attrs:
131            raise NotImplementedError("Sorry, you should implement the __call__ method of the kid :).")
132        for key, value in attrs.items():
133            if callable(value) and not key.startswith("_"):
134                attrs[key] = detect_naughty_kid(value)
135        return super().__new__(mcs, name, bases, attrs)
136
137    def __call__(cls, *args, **kwargs):
138        instance = super().__call__(*args, **kwargs)
139        if id(instance) not in Santa().all_kids:
140            Santa().all_kids.add(id(instance))
141            Santa().gifts[id(instance)] = None
142            Santa().kids_ages[id(instance)] = 0
143            Santa().kids_objects[id(instance)] = instance
144        return instance

......FF............
======================================================================
FAIL: test_present_matching (test.TestSanta.test_present_matching)
Test matching signature in the letter.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 92, in test_present_matching
self.assertEqual(list(self.santa), ['toy4', 'abcdefgHIJKLMNopQRstUVwxYZ 1 2 3 4567890 '])
AssertionError: Lists differ: ['toy4', None, 'abcdefgHIJKLMNopQRstUVwxYZ 1 2 3 4567890 '] != ['toy4', 'abcdefgHIJKLMNopQRstUVwxYZ 1 2 3 4567890 ']

First differing element 1:
None
'abcdefgHIJKLMNopQRstUVwxYZ 1 2 3 4567890 '

First list contains 1 additional elements.
First extra element 2:
'abcdefgHIJKLMNopQRstUVwxYZ 1 2 3 4567890 '

- ['toy4', None, 'abcdefgHIJKLMNopQRstUVwxYZ 1 2 3 4567890 ']
? ------

+ ['toy4', 'abcdefgHIJKLMNopQRstUVwxYZ 1 2 3 4567890 ']

======================================================================
FAIL: test_santa_gift_order (test.TestSanta.test_santa_gift_order)
Test ordering of the Santa iterator.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 260, in test_santa_gift_order
self.assertEqual(list(self.santa), ["toy2v2", "toy3", "toy1"])
AssertionError: Lists differ: ['toy1', 'toy2v2', 'toy3'] != ['toy2v2', 'toy3', 'toy1']

First differing element 0:
'toy1'
'toy2v2'

- ['toy1', 'toy2v2', 'toy3']
+ ['toy2v2', 'toy3', 'toy1']

----------------------------------------------------------------------
Ran 20 tests in 0.038s

FAILED (failures=2)

Дискусия
Виктор Бечев
19.12.2024 13:30

Аз говорех за това, че имаш два почти идентични регулярни израза, с единствената разлика в кавичките. Това може да бъде обединено в клас - `[\'"]`.
Росица Илиева
19.12.2024 13:16

Няма проблем, иначе относно регекса в extract_gift го направих така, предвид това че подаръкът не може да е празен стринг или само whitespace-ове. Реално никъде в условието не е казано, че не може да е празен стринг, но аз от тази гледна точка го направих така.
Виктор Бечев
19.12.2024 13:12

Вътрешната репрезентация, за която говориш, е в `Santa`. Там нямаме претенции какво ще правиш, не би следвало да ни създаде проблем как и какво ще пазиш. Тестовете се базират на input и очакван output. Ако тези две неща са окей - тестовете минават. П.П. Късно, бях започнал да пиша. :grin:
Росица Илиева
19.12.2024 13:10

Отговорих си на въпроса
Росица Илиева
19.12.2024 12:14

Ии последен въпрос, когато никой няма желание за Коледа, в моя код задавам стойност None за подаръка на съответното дете. Това ли трябва да връщаме в такъв случай, или това ще си зависи от имплементацията на наследниците на Kid?
Виктор Бечев
19.12.2024 11:55

Хубав въпрос. Според буквалното четене на условието: ``` Ако през изминалата година (т.е. след последното извикване на xmas) Дядо Коледа не е получил нито едно желание (с писмо или с обаждане), той не раздава нищо на никого. Явно магията на Коледа е отминала и всички са забравили за него. ``` Следва дори и лошите деца да не получават нищо, защото на Дядо Коледа му е тъжно.
Росица Илиева
19.12.2024 11:01

Ако никое дете няма желания за подаръци за Коледа, обаче някое от тях тази година е било непослушно, изпращаме ли му въглен, или не получава нищо?
Виктор Бечев
18.12.2024 22:37

С изключение на едно минало предизвикателство - никъде в домашните (и като цяло рядко в production код) не очакваме да има принтове.
Росица Илиева
18.12.2024 21:33

Имам въпрос дали е необходимо да добавяме принтове за всеки подарък, който дете получава, или това не е задължително?
История

f1import ref1import re
22
3class SingletonType(type):3class SingletonType(type):
4    def __new__(mcs, name, bases, attrs):4    def __new__(mcs, name, bases, attrs):
5        def singleton_new(cls):5        def singleton_new(cls):
6            if not hasattr(cls, 'instance'):6            if not hasattr(cls, 'instance'):
7                cls.instance = object.__new__(cls)7                cls.instance = object.__new__(cls)
8            return cls.instance8            return cls.instance
9        attrs['__new__'] = singleton_new9        attrs['__new__'] = singleton_new
10        return type.__new__(mcs, name, bases, attrs)10        return type.__new__(mcs, name, bases, attrs)
1111
1212
13class Santa(metaclass = SingletonType):13class Santa(metaclass = SingletonType):
14    """A class for greedy kids."""14    """A class for greedy kids."""
15    all_kids = set()15    all_kids = set()
16    kids_objects = {}16    kids_objects = {}
17    kids_ages = {}17    kids_ages = {}
18    gifts = {}18    gifts = {}
19    naughty_kids = set()19    naughty_kids = set()
2020
21    def extract_gift(self, letter: str):21    def extract_gift(self, letter: str):
22        """Extracts a gift from a letter or call."""22        """Extracts a gift from a letter or call."""
23        pattern_gift = r'(?:\")([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\")|(?:\')([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\')'23        pattern_gift = r'(?:\")([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\")|(?:\')([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\')'
24        pattern = re.compile(pattern_gift, re.MULTILINE)24        pattern = re.compile(pattern_gift, re.MULTILINE)
25        gifts = pattern.findall(letter)25        gifts = pattern.findall(letter)
2626
27        for gift_tuple in reversed(gifts):27        for gift_tuple in reversed(gifts):
28            for gift in gift_tuple:28            for gift in gift_tuple:
29                if gift:29                if gift:
30                    return gift30                    return gift
31        return None31        return None
3232
33    def extract_signature_from_letter(self, letter: str):33    def extract_signature_from_letter(self, letter: str):
34        """Extracts a signiture from a letter."""34        """Extracts a signiture from a letter."""
35        pattern_signature = r'\s*(\d+)\s*'35        pattern_signature = r'\s*(\d+)\s*'
36        pattern = re.compile(pattern_signature, re.MULTILINE)36        pattern = re.compile(pattern_signature, re.MULTILINE)
37        signature = pattern.findall(letter)37        signature = pattern.findall(letter)
38        if signature:38        if signature:
39            return signature[-1]39            return signature[-1]
40        return None40        return None
4141
42    def __matmul__(self, letter: str):42    def __matmul__(self, letter: str):
43        """Predefined operator @ when a kid writes a letter to Santa."""43        """Predefined operator @ when a kid writes a letter to Santa."""
44        gift = self.extract_gift(letter)44        gift = self.extract_gift(letter)
45        signature = self.extract_signature_from_letter(letter)45        signature = self.extract_signature_from_letter(letter)
4646
47        index = int(signature)47        index = int(signature)
48        self.gifts[index] = gift48        self.gifts[index] = gift
49        self.all_kids.add(index)49        self.all_kids.add(index)
50        self.kids_ages[index] = 050        self.kids_ages[index] = 0
51        if id(signature) in self.kids_objects:51        if id(signature) in self.kids_objects:
52            self.kids_objects[signature] = signature52            self.kids_objects[signature] = signature
5353
54    def __call__(self, child, wish: str):54    def __call__(self, child, wish: str):
55        """Predefined operator () when a kid calls Santa."""55        """Predefined operator () when a kid calls Santa."""
56        index = id(child)56        index = id(child)
57        self.gifts[index] = self.extract_gift(wish)57        self.gifts[index] = self.extract_gift(wish)
5858
59    def __iter__(self):59    def __iter__(self):
60        return iter(self.gifts.values())60        return iter(self.gifts.values())
6161
62    def get_most_wanted_gift_this_year(self):62    def get_most_wanted_gift_this_year(self):
63        """Calculates what is the most wanted gift this year."""63        """Calculates what is the most wanted gift this year."""
64        this_years_gifts = [gift for gift in self.gifts.values() if gift is not None]64        this_years_gifts = [gift for gift in self.gifts.values() if gift is not None]
6565
66        if set(this_years_gifts):66        if set(this_years_gifts):
67            most_wanted_gift_this_year = max(set(this_years_gifts) , key = this_years_gifts.count)67            most_wanted_gift_this_year = max(set(this_years_gifts) , key = this_years_gifts.count)
68        else:68        else:
69            most_wanted_gift_this_year = None69            most_wanted_gift_this_year = None
70        return most_wanted_gift_this_year70        return most_wanted_gift_this_year
7171
72    def remove_kid(self, keys_to_delete):72    def remove_kid(self, keys_to_delete):
73        """Deletes kid because it is too old for presents."""73        """Deletes kid because it is too old for presents."""
74        for key in keys_to_delete:74        for key in keys_to_delete:
75            del self.gifts[key]75            del self.gifts[key]
76            del self.kids_ages[key]76            del self.kids_ages[key]
77            del self.kids_objects[key]77            del self.kids_objects[key]
78            self.all_kids.remove(key)78            self.all_kids.remove(key)
7979
80    def mark_as_naughty(self, kid):80    def mark_as_naughty(self, kid):
81        """Marks a kid as naughty."""81        """Marks a kid as naughty."""
82        if id(kid) in self.all_kids:82        if id(kid) in self.all_kids:
83            self.naughty_kids.add(id(kid))83            self.naughty_kids.add(id(kid))
8484
85    def xmas(self):85    def xmas(self):
86        """Time for presents."""86        """Time for presents."""
87        for key in self.kids_ages:87        for key in self.kids_ages:
88            self.kids_ages[key] += 188            self.kids_ages[key] += 1
8989
90        most_wanted_gift = self.get_most_wanted_gift_this_year()90        most_wanted_gift = self.get_most_wanted_gift_this_year()
91        keys_to_delete = []91        keys_to_delete = []
9292
93        for kid, age in self.kids_ages.items():93        for kid, age in self.kids_ages.items():
94            cur_gift = self.gifts[kid]94            cur_gift = self.gifts[kid]
95            if age > 5:95            if age > 5:
96                keys_to_delete.append(kid)96                keys_to_delete.append(kid)
97                continue97                continue
98            elif age <= 5:98            elif age <= 5:
99                kid_obj = self.kids_objects.get(kid)99                kid_obj = self.kids_objects.get(kid)
t100                if kid in self.naughty_kids and not all(value == None for value in self.gifts.values()):t100                if all(value == None for value in self.gifts.values()):
101                    break
102                elif kid in self.naughty_kids:
101                    kid_obj("coal")103                    kid_obj("coal")
102                elif cur_gift is None:104                elif cur_gift is None:
103                    kid_obj(most_wanted_gift)105                    kid_obj(most_wanted_gift)
104                else:106                else:
105                    kid_obj(cur_gift)107                    kid_obj(cur_gift)
106108
107        for cur_gift in self.gifts:109        for cur_gift in self.gifts:
108            self.gifts[cur_gift] = None110            self.gifts[cur_gift] = None
109111
110        self.remove_kid(keys_to_delete)112        self.remove_kid(keys_to_delete)
111        self.naughty_kids.clear()113        self.naughty_kids.clear()
112114
113115
114def detect_naughty_kid(func):116def detect_naughty_kid(func):
115    """Checks if the kid is naughty."""117    """Checks if the kid is naughty."""
116    def wrapper(self, *args, **kwargs):118    def wrapper(self, *args, **kwargs):
117        try:119        try:
118            return func(self, *args, **kwargs)120            return func(self, *args, **kwargs)
119        except Exception:121        except Exception:
120            Santa().mark_as_naughty(self)122            Santa().mark_as_naughty(self)
121            raise123            raise
122    return wrapper124    return wrapper
123125
124126
125class Kid(type):127class Kid(type):
126    """A class that defines a kid."""128    """A class that defines a kid."""
127    def __new__(mcs, name, bases, attrs):129    def __new__(mcs, name, bases, attrs):
128        if '__call__' not in attrs:130        if '__call__' not in attrs:
129            raise NotImplementedError("Sorry, you should implement the __call__ method of the kid :).")131            raise NotImplementedError("Sorry, you should implement the __call__ method of the kid :).")
130        for key, value in attrs.items():132        for key, value in attrs.items():
131            if callable(value) and not key.startswith("_"):133            if callable(value) and not key.startswith("_"):
132                attrs[key] = detect_naughty_kid(value)134                attrs[key] = detect_naughty_kid(value)
133        return super().__new__(mcs, name, bases, attrs)135        return super().__new__(mcs, name, bases, attrs)
134136
135    def __call__(cls, *args, **kwargs):137    def __call__(cls, *args, **kwargs):
136        instance = super().__call__(*args, **kwargs)138        instance = super().__call__(*args, **kwargs)
137        if id(instance) not in Santa().all_kids:139        if id(instance) not in Santa().all_kids:
138            Santa().all_kids.add(id(instance))140            Santa().all_kids.add(id(instance))
139            Santa().gifts[id(instance)] = None141            Santa().gifts[id(instance)] = None
140            Santa().kids_ages[id(instance)] = 0142            Santa().kids_ages[id(instance)] = 0
141            Santa().kids_objects[id(instance)] = instance143            Santa().kids_objects[id(instance)] = instance
142        return instance144        return instance
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import ref1import re
22
3class SingletonType(type):3class SingletonType(type):
4    def __new__(mcs, name, bases, attrs):4    def __new__(mcs, name, bases, attrs):
5        def singleton_new(cls):5        def singleton_new(cls):
6            if not hasattr(cls, 'instance'):6            if not hasattr(cls, 'instance'):
7                cls.instance = object.__new__(cls)7                cls.instance = object.__new__(cls)
8            return cls.instance8            return cls.instance
9        attrs['__new__'] = singleton_new9        attrs['__new__'] = singleton_new
10        return type.__new__(mcs, name, bases, attrs)10        return type.__new__(mcs, name, bases, attrs)
1111
1212
13class Santa(metaclass = SingletonType):13class Santa(metaclass = SingletonType):
14    """A class for greedy kids."""14    """A class for greedy kids."""
15    all_kids = set()15    all_kids = set()
16    kids_objects = {}16    kids_objects = {}
17    kids_ages = {}17    kids_ages = {}
18    gifts = {}18    gifts = {}
19    naughty_kids = set()19    naughty_kids = set()
2020
21    def extract_gift(self, letter: str):21    def extract_gift(self, letter: str):
22        """Extracts a gift from a letter or call."""22        """Extracts a gift from a letter or call."""
23        pattern_gift = r'(?:\")([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\")|(?:\')([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\')'23        pattern_gift = r'(?:\")([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\")|(?:\')([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\')'
24        pattern = re.compile(pattern_gift, re.MULTILINE)24        pattern = re.compile(pattern_gift, re.MULTILINE)
25        gifts = pattern.findall(letter)25        gifts = pattern.findall(letter)
2626
27        for gift_tuple in reversed(gifts):27        for gift_tuple in reversed(gifts):
28            for gift in gift_tuple:28            for gift in gift_tuple:
29                if gift:29                if gift:
30                    return gift30                    return gift
31        return None31        return None
3232
33    def extract_signature_from_letter(self, letter: str):33    def extract_signature_from_letter(self, letter: str):
34        """Extracts a signiture from a letter."""34        """Extracts a signiture from a letter."""
35        pattern_signature = r'\s*(\d+)\s*'35        pattern_signature = r'\s*(\d+)\s*'
36        pattern = re.compile(pattern_signature, re.MULTILINE)36        pattern = re.compile(pattern_signature, re.MULTILINE)
37        signature = pattern.findall(letter)37        signature = pattern.findall(letter)
38        if signature:38        if signature:
39            return signature[-1]39            return signature[-1]
40        return None40        return None
4141
42    def __matmul__(self, letter: str):42    def __matmul__(self, letter: str):
43        """Predefined operator @ when a kid writes a letter to Santa."""43        """Predefined operator @ when a kid writes a letter to Santa."""
44        gift = self.extract_gift(letter)44        gift = self.extract_gift(letter)
45        signature = self.extract_signature_from_letter(letter)45        signature = self.extract_signature_from_letter(letter)
4646
47        index = int(signature)47        index = int(signature)
48        self.gifts[index] = gift48        self.gifts[index] = gift
49        self.all_kids.add(index)49        self.all_kids.add(index)
50        self.kids_ages[index] = 050        self.kids_ages[index] = 0
51        if id(signature) in self.kids_objects:51        if id(signature) in self.kids_objects:
52            self.kids_objects[signature] = signature52            self.kids_objects[signature] = signature
5353
54    def __call__(self, child, wish: str):54    def __call__(self, child, wish: str):
55        """Predefined operator () when a kid calls Santa."""55        """Predefined operator () when a kid calls Santa."""
56        index = id(child)56        index = id(child)
57        self.gifts[index] = self.extract_gift(wish)57        self.gifts[index] = self.extract_gift(wish)
5858
59    def __iter__(self):59    def __iter__(self):
60        return iter(self.gifts.values())60        return iter(self.gifts.values())
6161
62    def get_most_wanted_gift_this_year(self):62    def get_most_wanted_gift_this_year(self):
63        """Calculates what is the most wanted gift this year."""63        """Calculates what is the most wanted gift this year."""
64        this_years_gifts = [gift for gift in self.gifts.values() if gift is not None]64        this_years_gifts = [gift for gift in self.gifts.values() if gift is not None]
6565
66        if set(this_years_gifts):66        if set(this_years_gifts):
67            most_wanted_gift_this_year = max(set(this_years_gifts) , key = this_years_gifts.count)67            most_wanted_gift_this_year = max(set(this_years_gifts) , key = this_years_gifts.count)
68        else:68        else:
69            most_wanted_gift_this_year = None69            most_wanted_gift_this_year = None
70        return most_wanted_gift_this_year70        return most_wanted_gift_this_year
7171
72    def remove_kid(self, keys_to_delete):72    def remove_kid(self, keys_to_delete):
73        """Deletes kid because it is too old for presents."""73        """Deletes kid because it is too old for presents."""
74        for key in keys_to_delete:74        for key in keys_to_delete:
75            del self.gifts[key]75            del self.gifts[key]
76            del self.kids_ages[key]76            del self.kids_ages[key]
77            del self.kids_objects[key]77            del self.kids_objects[key]
78            self.all_kids.remove(key)78            self.all_kids.remove(key)
7979
80    def mark_as_naughty(self, kid):80    def mark_as_naughty(self, kid):
81        """Marks a kid as naughty."""81        """Marks a kid as naughty."""
82        if id(kid) in self.all_kids:82        if id(kid) in self.all_kids:
83            self.naughty_kids.add(id(kid))83            self.naughty_kids.add(id(kid))
8484
85    def xmas(self):85    def xmas(self):
86        """Time for presents."""86        """Time for presents."""
87        for key in self.kids_ages:87        for key in self.kids_ages:
88            self.kids_ages[key] += 188            self.kids_ages[key] += 1
8989
90        most_wanted_gift = self.get_most_wanted_gift_this_year()90        most_wanted_gift = self.get_most_wanted_gift_this_year()
91        keys_to_delete = []91        keys_to_delete = []
9292
93        for kid, age in self.kids_ages.items():93        for kid, age in self.kids_ages.items():
94            cur_gift = self.gifts[kid]94            cur_gift = self.gifts[kid]
95            if age > 5:95            if age > 5:
96                keys_to_delete.append(kid)96                keys_to_delete.append(kid)
97                continue97                continue
98            elif age <= 5:98            elif age <= 5:
99                kid_obj = self.kids_objects.get(kid)99                kid_obj = self.kids_objects.get(kid)
t100                if kid in self.naughty_kids:t100                if kid in self.naughty_kids and not all(value == None for value in self.gifts.values()):
101                    kid_obj("coal")101                    kid_obj("coal")
102                elif cur_gift is None:102                elif cur_gift is None:
103                    kid_obj(most_wanted_gift)103                    kid_obj(most_wanted_gift)
104                else:104                else:
105                    kid_obj(cur_gift)105                    kid_obj(cur_gift)
106106
107        for cur_gift in self.gifts:107        for cur_gift in self.gifts:
108            self.gifts[cur_gift] = None108            self.gifts[cur_gift] = None
109109
110        self.remove_kid(keys_to_delete)110        self.remove_kid(keys_to_delete)
111        self.naughty_kids.clear()111        self.naughty_kids.clear()
112112
113113
114def detect_naughty_kid(func):114def detect_naughty_kid(func):
115    """Checks if the kid is naughty."""115    """Checks if the kid is naughty."""
116    def wrapper(self, *args, **kwargs):116    def wrapper(self, *args, **kwargs):
117        try:117        try:
118            return func(self, *args, **kwargs)118            return func(self, *args, **kwargs)
119        except Exception:119        except Exception:
120            Santa().mark_as_naughty(self)120            Santa().mark_as_naughty(self)
121            raise121            raise
122    return wrapper122    return wrapper
123123
124124
125class Kid(type):125class Kid(type):
126    """A class that defines a kid."""126    """A class that defines a kid."""
127    def __new__(mcs, name, bases, attrs):127    def __new__(mcs, name, bases, attrs):
128        if '__call__' not in attrs:128        if '__call__' not in attrs:
129            raise NotImplementedError("Sorry, you should implement the __call__ method of the kid :).")129            raise NotImplementedError("Sorry, you should implement the __call__ method of the kid :).")
130        for key, value in attrs.items():130        for key, value in attrs.items():
131            if callable(value) and not key.startswith("_"):131            if callable(value) and not key.startswith("_"):
132                attrs[key] = detect_naughty_kid(value)132                attrs[key] = detect_naughty_kid(value)
133        return super().__new__(mcs, name, bases, attrs)133        return super().__new__(mcs, name, bases, attrs)
134134
135    def __call__(cls, *args, **kwargs):135    def __call__(cls, *args, **kwargs):
136        instance = super().__call__(*args, **kwargs)136        instance = super().__call__(*args, **kwargs)
137        if id(instance) not in Santa().all_kids:137        if id(instance) not in Santa().all_kids:
138            Santa().all_kids.add(id(instance))138            Santa().all_kids.add(id(instance))
139            Santa().gifts[id(instance)] = None139            Santa().gifts[id(instance)] = None
140            Santa().kids_ages[id(instance)] = 0140            Santa().kids_ages[id(instance)] = 0
141            Santa().kids_objects[id(instance)] = instance141            Santa().kids_objects[id(instance)] = instance
142        return instance142        return instance
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import ref1import re
22
3class SingletonType(type):3class SingletonType(type):
4    def __new__(mcs, name, bases, attrs):4    def __new__(mcs, name, bases, attrs):
5        def singleton_new(cls):5        def singleton_new(cls):
6            if not hasattr(cls, 'instance'):6            if not hasattr(cls, 'instance'):
7                cls.instance = object.__new__(cls)7                cls.instance = object.__new__(cls)
8            return cls.instance8            return cls.instance
9        attrs['__new__'] = singleton_new9        attrs['__new__'] = singleton_new
10        return type.__new__(mcs, name, bases, attrs)10        return type.__new__(mcs, name, bases, attrs)
1111
1212
13class Santa(metaclass = SingletonType):13class Santa(metaclass = SingletonType):
14    """A class for greedy kids."""14    """A class for greedy kids."""
15    all_kids = set()15    all_kids = set()
16    kids_objects = {}16    kids_objects = {}
17    kids_ages = {}17    kids_ages = {}
18    gifts = {}18    gifts = {}
19    naughty_kids = set()19    naughty_kids = set()
2020
21    def extract_gift(self, letter: str):21    def extract_gift(self, letter: str):
22        """Extracts a gift from a letter or call."""22        """Extracts a gift from a letter or call."""
23        pattern_gift = r'(?:\")([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\")|(?:\')([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\')'23        pattern_gift = r'(?:\")([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\")|(?:\')([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\')'
24        pattern = re.compile(pattern_gift, re.MULTILINE)24        pattern = re.compile(pattern_gift, re.MULTILINE)
25        gifts = pattern.findall(letter)25        gifts = pattern.findall(letter)
2626
27        for gift_tuple in reversed(gifts):27        for gift_tuple in reversed(gifts):
28            for gift in gift_tuple:28            for gift in gift_tuple:
29                if gift:29                if gift:
30                    return gift30                    return gift
31        return None31        return None
3232
33    def extract_signature_from_letter(self, letter: str):33    def extract_signature_from_letter(self, letter: str):
34        """Extracts a signiture from a letter."""34        """Extracts a signiture from a letter."""
35        pattern_signature = r'\s*(\d+)\s*'35        pattern_signature = r'\s*(\d+)\s*'
36        pattern = re.compile(pattern_signature, re.MULTILINE)36        pattern = re.compile(pattern_signature, re.MULTILINE)
37        signature = pattern.findall(letter)37        signature = pattern.findall(letter)
38        if signature:38        if signature:
39            return signature[-1]39            return signature[-1]
40        return None40        return None
4141
42    def __matmul__(self, letter: str):42    def __matmul__(self, letter: str):
43        """Predefined operator @ when a kid writes a letter to Santa."""43        """Predefined operator @ when a kid writes a letter to Santa."""
44        gift = self.extract_gift(letter)44        gift = self.extract_gift(letter)
45        signature = self.extract_signature_from_letter(letter)45        signature = self.extract_signature_from_letter(letter)
t46        t46 
47        index = int(signature)47        index = int(signature)
48        self.gifts[index] = gift48        self.gifts[index] = gift
49        self.all_kids.add(index)49        self.all_kids.add(index)
50        self.kids_ages[index] = 050        self.kids_ages[index] = 0
51        if id(signature) in self.kids_objects:51        if id(signature) in self.kids_objects:
52            self.kids_objects[signature] = signature52            self.kids_objects[signature] = signature
5353
54    def __call__(self, child, wish: str):54    def __call__(self, child, wish: str):
55        """Predefined operator () when a kid calls Santa."""55        """Predefined operator () when a kid calls Santa."""
56        index = id(child)56        index = id(child)
57        self.gifts[index] = self.extract_gift(wish)57        self.gifts[index] = self.extract_gift(wish)
5858
59    def __iter__(self):59    def __iter__(self):
60        return iter(self.gifts.values())60        return iter(self.gifts.values())
6161
62    def get_most_wanted_gift_this_year(self):62    def get_most_wanted_gift_this_year(self):
63        """Calculates what is the most wanted gift this year."""63        """Calculates what is the most wanted gift this year."""
64        this_years_gifts = [gift for gift in self.gifts.values() if gift is not None]64        this_years_gifts = [gift for gift in self.gifts.values() if gift is not None]
6565
66        if set(this_years_gifts):66        if set(this_years_gifts):
67            most_wanted_gift_this_year = max(set(this_years_gifts) , key = this_years_gifts.count)67            most_wanted_gift_this_year = max(set(this_years_gifts) , key = this_years_gifts.count)
68        else:68        else:
69            most_wanted_gift_this_year = None69            most_wanted_gift_this_year = None
70        return most_wanted_gift_this_year70        return most_wanted_gift_this_year
7171
72    def remove_kid(self, keys_to_delete):72    def remove_kid(self, keys_to_delete):
73        """Deletes kid because it is too old for presents."""73        """Deletes kid because it is too old for presents."""
74        for key in keys_to_delete:74        for key in keys_to_delete:
75            del self.gifts[key]75            del self.gifts[key]
76            del self.kids_ages[key]76            del self.kids_ages[key]
77            del self.kids_objects[key]77            del self.kids_objects[key]
78            self.all_kids.remove(key)78            self.all_kids.remove(key)
7979
80    def mark_as_naughty(self, kid):80    def mark_as_naughty(self, kid):
81        """Marks a kid as naughty."""81        """Marks a kid as naughty."""
82        if id(kid) in self.all_kids:82        if id(kid) in self.all_kids:
83            self.naughty_kids.add(id(kid))83            self.naughty_kids.add(id(kid))
8484
85    def xmas(self):85    def xmas(self):
86        """Time for presents."""86        """Time for presents."""
87        for key in self.kids_ages:87        for key in self.kids_ages:
88            self.kids_ages[key] += 188            self.kids_ages[key] += 1
8989
90        most_wanted_gift = self.get_most_wanted_gift_this_year()90        most_wanted_gift = self.get_most_wanted_gift_this_year()
91        keys_to_delete = []91        keys_to_delete = []
9292
93        for kid, age in self.kids_ages.items():93        for kid, age in self.kids_ages.items():
94            cur_gift = self.gifts[kid]94            cur_gift = self.gifts[kid]
95            if age > 5:95            if age > 5:
96                keys_to_delete.append(kid)96                keys_to_delete.append(kid)
97                continue97                continue
98            elif age <= 5:98            elif age <= 5:
99                kid_obj = self.kids_objects.get(kid)99                kid_obj = self.kids_objects.get(kid)
100                if kid in self.naughty_kids:100                if kid in self.naughty_kids:
101                    kid_obj("coal")101                    kid_obj("coal")
102                elif cur_gift is None:102                elif cur_gift is None:
103                    kid_obj(most_wanted_gift)103                    kid_obj(most_wanted_gift)
104                else:104                else:
105                    kid_obj(cur_gift)105                    kid_obj(cur_gift)
106106
107        for cur_gift in self.gifts:107        for cur_gift in self.gifts:
108            self.gifts[cur_gift] = None108            self.gifts[cur_gift] = None
109109
110        self.remove_kid(keys_to_delete)110        self.remove_kid(keys_to_delete)
111        self.naughty_kids.clear()111        self.naughty_kids.clear()
112112
113113
114def detect_naughty_kid(func):114def detect_naughty_kid(func):
115    """Checks if the kid is naughty."""115    """Checks if the kid is naughty."""
116    def wrapper(self, *args, **kwargs):116    def wrapper(self, *args, **kwargs):
117        try:117        try:
118            return func(self, *args, **kwargs)118            return func(self, *args, **kwargs)
119        except Exception:119        except Exception:
120            Santa().mark_as_naughty(self)120            Santa().mark_as_naughty(self)
121            raise121            raise
122    return wrapper122    return wrapper
123123
124124
125class Kid(type):125class Kid(type):
126    """A class that defines a kid."""126    """A class that defines a kid."""
127    def __new__(mcs, name, bases, attrs):127    def __new__(mcs, name, bases, attrs):
128        if '__call__' not in attrs:128        if '__call__' not in attrs:
129            raise NotImplementedError("Sorry, you should implement the __call__ method of the kid :).")129            raise NotImplementedError("Sorry, you should implement the __call__ method of the kid :).")
130        for key, value in attrs.items():130        for key, value in attrs.items():
131            if callable(value) and not key.startswith("_"):131            if callable(value) and not key.startswith("_"):
132                attrs[key] = detect_naughty_kid(value)132                attrs[key] = detect_naughty_kid(value)
133        return super().__new__(mcs, name, bases, attrs)133        return super().__new__(mcs, name, bases, attrs)
134134
135    def __call__(cls, *args, **kwargs):135    def __call__(cls, *args, **kwargs):
136        instance = super().__call__(*args, **kwargs)136        instance = super().__call__(*args, **kwargs)
137        if id(instance) not in Santa().all_kids:137        if id(instance) not in Santa().all_kids:
138            Santa().all_kids.add(id(instance))138            Santa().all_kids.add(id(instance))
139            Santa().gifts[id(instance)] = None139            Santa().gifts[id(instance)] = None
140            Santa().kids_ages[id(instance)] = 0140            Santa().kids_ages[id(instance)] = 0
141            Santa().kids_objects[id(instance)] = instance141            Santa().kids_objects[id(instance)] = instance
142        return instance142        return instance
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import ref1import re
22
3class SingletonType(type):3class SingletonType(type):
4    def __new__(mcs, name, bases, attrs):4    def __new__(mcs, name, bases, attrs):
5        def singleton_new(cls):5        def singleton_new(cls):
6            if not hasattr(cls, 'instance'):6            if not hasattr(cls, 'instance'):
7                cls.instance = object.__new__(cls)7                cls.instance = object.__new__(cls)
8            return cls.instance8            return cls.instance
9        attrs['__new__'] = singleton_new9        attrs['__new__'] = singleton_new
10        return type.__new__(mcs, name, bases, attrs)10        return type.__new__(mcs, name, bases, attrs)
1111
1212
13class Santa(metaclass = SingletonType):13class Santa(metaclass = SingletonType):
14    """A class for greedy kids."""14    """A class for greedy kids."""
15    all_kids = set()15    all_kids = set()
nn16    kids_objects = {}
16    kids_ages = {}17    kids_ages = {}
17    gifts = {}18    gifts = {}
18    naughty_kids = set()19    naughty_kids = set()
1920
20    def extract_gift(self, letter: str):21    def extract_gift(self, letter: str):
21        """Extracts a gift from a letter or call."""22        """Extracts a gift from a letter or call."""
22        pattern_gift = r'(?:\")([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\")|(?:\')([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\')'23        pattern_gift = r'(?:\")([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\")|(?:\')([A-Za-z0-9]+[A-Za-z0-9 ]*)(?:\')'
23        pattern = re.compile(pattern_gift, re.MULTILINE)24        pattern = re.compile(pattern_gift, re.MULTILINE)
24        gifts = pattern.findall(letter)25        gifts = pattern.findall(letter)
2526
26        for gift_tuple in reversed(gifts):27        for gift_tuple in reversed(gifts):
27            for gift in gift_tuple:28            for gift in gift_tuple:
28                if gift:29                if gift:
29                    return gift30                    return gift
30        return None31        return None
3132
32    def extract_signature_from_letter(self, letter: str):33    def extract_signature_from_letter(self, letter: str):
33        """Extracts a signiture from a letter."""34        """Extracts a signiture from a letter."""
34        pattern_signature = r'\s*(\d+)\s*'35        pattern_signature = r'\s*(\d+)\s*'
35        pattern = re.compile(pattern_signature, re.MULTILINE)36        pattern = re.compile(pattern_signature, re.MULTILINE)
36        signature = pattern.findall(letter)37        signature = pattern.findall(letter)
37        if signature:38        if signature:
38            return signature[-1]39            return signature[-1]
39        return None40        return None
4041
41    def __matmul__(self, letter: str):42    def __matmul__(self, letter: str):
42        """Predefined operator @ when a kid writes a letter to Santa."""43        """Predefined operator @ when a kid writes a letter to Santa."""
43        gift = self.extract_gift(letter)44        gift = self.extract_gift(letter)
44        signature = self.extract_signature_from_letter(letter)45        signature = self.extract_signature_from_letter(letter)
n45 n46        
46        index = int(signature)47        index = int(signature)
47        self.gifts[index] = gift48        self.gifts[index] = gift
48        self.all_kids.add(index)49        self.all_kids.add(index)
49        self.kids_ages[index] = 050        self.kids_ages[index] = 0
nn51        if id(signature) in self.kids_objects:
52            self.kids_objects[signature] = signature
5053
51    def __call__(self, child, wish: str):54    def __call__(self, child, wish: str):
52        """Predefined operator () when a kid calls Santa."""55        """Predefined operator () when a kid calls Santa."""
53        index = id(child)56        index = id(child)
54        self.gifts[index] = self.extract_gift(wish)57        self.gifts[index] = self.extract_gift(wish)
5558
56    def __iter__(self):59    def __iter__(self):
57        return iter(self.gifts.values())60        return iter(self.gifts.values())
5861
59    def get_most_wanted_gift_this_year(self):62    def get_most_wanted_gift_this_year(self):
60        """Calculates what is the most wanted gift this year."""63        """Calculates what is the most wanted gift this year."""
61        this_years_gifts = [gift for gift in self.gifts.values() if gift is not None]64        this_years_gifts = [gift for gift in self.gifts.values() if gift is not None]
6265
63        if set(this_years_gifts):66        if set(this_years_gifts):
64            most_wanted_gift_this_year = max(set(this_years_gifts) , key = this_years_gifts.count)67            most_wanted_gift_this_year = max(set(this_years_gifts) , key = this_years_gifts.count)
65        else:68        else:
66            most_wanted_gift_this_year = None69            most_wanted_gift_this_year = None
67        return most_wanted_gift_this_year70        return most_wanted_gift_this_year
6871
69    def remove_kid(self, keys_to_delete):72    def remove_kid(self, keys_to_delete):
70        """Deletes kid because it is too old for presents."""73        """Deletes kid because it is too old for presents."""
71        for key in keys_to_delete:74        for key in keys_to_delete:
72            del self.gifts[key]75            del self.gifts[key]
73            del self.kids_ages[key]76            del self.kids_ages[key]
nn77            del self.kids_objects[key]
74            self.all_kids.remove(key)78            self.all_kids.remove(key)
7579
76    def mark_as_naughty(self, kid):80    def mark_as_naughty(self, kid):
77        """Marks a kid as naughty."""81        """Marks a kid as naughty."""
78        if id(kid) in self.all_kids:82        if id(kid) in self.all_kids:
79            self.naughty_kids.add(id(kid))83            self.naughty_kids.add(id(kid))
8084
81    def xmas(self):85    def xmas(self):
82        """Time for presents."""86        """Time for presents."""
83        for key in self.kids_ages:87        for key in self.kids_ages:
84            self.kids_ages[key] += 188            self.kids_ages[key] += 1
8589
86        most_wanted_gift = self.get_most_wanted_gift_this_year()90        most_wanted_gift = self.get_most_wanted_gift_this_year()
87        keys_to_delete = []91        keys_to_delete = []
8892
89        for kid, age in self.kids_ages.items():93        for kid, age in self.kids_ages.items():
90            cur_gift = self.gifts[kid]94            cur_gift = self.gifts[kid]
91            if age > 5:95            if age > 5:
92                keys_to_delete.append(kid)96                keys_to_delete.append(kid)
93                continue97                continue
94            elif age <= 5:98            elif age <= 5:
nn99                kid_obj = self.kids_objects.get(kid)
95                if kid in self.naughty_kids:100                if kid in self.naughty_kids:
n96                    cur_gift = "coal"n101                    kid_obj("coal")
97                elif cur_gift is None:102                elif cur_gift is None:
n98                    cur_gift = most_wanted_giftn103                    kid_obj(most_wanted_gift)
99                # print(f"{kid} получава {cur_gift}.")104                else:
100                cur_gift = None105                    kid_obj(cur_gift)
101106
102        for cur_gift in self.gifts:107        for cur_gift in self.gifts:
103            self.gifts[cur_gift] = None108            self.gifts[cur_gift] = None
104109
105        self.remove_kid(keys_to_delete)110        self.remove_kid(keys_to_delete)
106        self.naughty_kids.clear()111        self.naughty_kids.clear()
107112
108113
109def detect_naughty_kid(func):114def detect_naughty_kid(func):
110    """Checks if the kid is naughty."""115    """Checks if the kid is naughty."""
111    def wrapper(self, *args, **kwargs):116    def wrapper(self, *args, **kwargs):
112        try:117        try:
113            return func(self, *args, **kwargs)118            return func(self, *args, **kwargs)
114        except Exception:119        except Exception:
115            Santa().mark_as_naughty(self)120            Santa().mark_as_naughty(self)
116            raise121            raise
117    return wrapper122    return wrapper
118123
119124
120class Kid(type):125class Kid(type):
121    """A class that defines a kid."""126    """A class that defines a kid."""
122    def __new__(mcs, name, bases, attrs):127    def __new__(mcs, name, bases, attrs):
123        if '__call__' not in attrs:128        if '__call__' not in attrs:
124            raise NotImplementedError("Sorry, you should implement the __call__ method of the kid :).")129            raise NotImplementedError("Sorry, you should implement the __call__ method of the kid :).")
125        for key, value in attrs.items():130        for key, value in attrs.items():
126            if callable(value) and not key.startswith("_"):131            if callable(value) and not key.startswith("_"):
127                attrs[key] = detect_naughty_kid(value)132                attrs[key] = detect_naughty_kid(value)
128        return super().__new__(mcs, name, bases, attrs)133        return super().__new__(mcs, name, bases, attrs)
129134
130    def __call__(cls, *args, **kwargs):135    def __call__(cls, *args, **kwargs):
131        instance = super().__call__(*args, **kwargs)136        instance = super().__call__(*args, **kwargs)
132        if id(instance) not in Santa().all_kids:137        if id(instance) not in Santa().all_kids:
133            Santa().all_kids.add(id(instance))138            Santa().all_kids.add(id(instance))
134            Santa().gifts[id(instance)] = None139            Santa().gifts[id(instance)] = None
135            Santa().kids_ages[id(instance)] = 0140            Santa().kids_ages[id(instance)] = 0
tt141            Santa().kids_objects[id(instance)] = instance
136        return instance142        return instance
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op