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

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

11 точки общо

20 успешни теста
0 неуспешни теста
Код

  1import re
  2
  3
  4class Singleton(type):
  5    """Metaclass for making only one instance for class"""
  6    _instance = {}
  7
  8    def __call__(cls, *args, **kwargs):
  9        """Create new instance"""
 10        if cls not in cls._instance:
 11            cls._instance[cls] = super(Singleton, cls).__call__(*args, **kwargs)
 12        return cls._instance[cls]
 13
 14
 15class Santa(metaclass=Singleton):
 16    """Class representing a white-bearded old man, bearer of gifts and coal"""
 17    _wishes = {}
 18    kids = []
 19    bad_kids = []
 20
 21    @staticmethod
 22    def _read_wish(wish):
 23        """Read wish"""
 24        gift = re.search(r'("[a-zA-Z0-9\s]+")|(\'[a-zA-Z0-9\s]+\')', wish)
 25        return gift.group()[1:-1]
 26    @staticmethod
 27    def _read_id(wish):
 28        """Read id from wish"""
 29        kid_id = re.search(r'^\s*(\d+)\s*$', wish, re.MULTILINE)
 30        return int(kid_id.group())
 31
 32    def __call__(self, child, wish):
 33        """Receive wishes during phone call"""
 34        gift = self._read_wish(wish)
 35        self._wishes[child] = gift
 36
 37    def __matmul__(self, wish):
 38        """Handle wish from letter"""
 39        gift = self._read_wish(wish)
 40        kid_id = self._read_id(wish)
 41        for kid in self.kids:
 42            if id(kid) == kid_id:
 43                self._wishes[kid] = gift
 44
 45    def __iter__(self):
 46        """Allow to iterate Santa"""
 47        return iter(list(self._wishes.values()))
 48
 49    def _most_wanted(self):
 50        """Find most wanted gift for this year"""
 51        wish_counts = {}
 52        most_wanted_wish = None
 53        for wish in self._wishes.values():
 54            if wish not in wish_counts:
 55                wish_counts[wish] = 0
 56            wish_counts[wish] += 1
 57            if most_wanted_wish is None or wish_counts[wish] > wish_counts[most_wanted_wish]:
 58                most_wanted_wish = wish
 59        return most_wanted_wish
 60
 61    def xmas(self):
 62        """Handle the distribution"""
 63        for kid in self.kids:
 64            kid.years += 1
 65        if not self._wishes:
 66            return
 67        default_gift = self._most_wanted()
 68        for kid in self.kids[:]:
 69            if kid.years > 5:
 70                self.kids.remove(kid)
 71            else:
 72                if kid in self.bad_kids:
 73                    kid('coal')
 74                else:
 75                    gift = self._wishes.get(kid, default_gift)
 76                    kid(gift)
 77        self._wishes.clear()
 78        self.bad_kids.clear()
 79
 80    def add_to_kids(self, kid):
 81        """Add kids to santa list"""
 82        self.kids.append(kid)
 83
 84    def add_to_bad_kids(self, kid):
 85        """Add kids to bad kids"""
 86        if kid not in self.bad_kids:
 87            self.bad_kids.append(kid)
 88
 89
 90class Kid(type):
 91    """Metaclass for creating Kids"""
 92
 93    def __call__(cls, *args, **kwargs):
 94        """Create new Kid"""
 95        santa = Santa()
 96        instance = super().__call__(*args, **kwargs)
 97        santa.add_to_kids(instance)
 98        instance.years = 0
 99        return instance
100
101    @staticmethod
102    def _decorator(func):
103        """Decorator that catches bad kids"""
104        santa = Santa()
105
106        def wrapper(*args, **kwargs):
107            result = None
108            try:
109                result = func(*args, **kwargs)
110            except Exception as e:
111                santa.add_to_bad_kids(args[0])
112                raise e
113            return result
114
115        return wrapper
116
117    @staticmethod
118    def _check_key(key):
119        """Check if the function is public"""
120        if re.match(r'^[^_]', key):
121            return True
122        return False
123
124    def __new__(cls, name, bases, attr_dict):
125        """Call decorator to help Santa punish bad kids"""
126        if '__call__' not in attr_dict:
127            raise NotImplementedError()
128
129        for key, value in attr_dict.items():
130            if callable(value) and cls._check_key(key):
131                attr_dict[key] = cls._decorator(value)
132        return super().__new__(cls, name, bases, attr_dict)

....................
----------------------------------------------------------------------
Ran 20 tests in 0.071s

OK

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

Да, би било една идея по-добре. Edit: Не знам защо толкова уклончиво съм го направил да звучи - не е една идея по-добре, това е **the** right way. :grin:
Ева Ганчева
19.12.2024 13:16

А ако в Santa има функции,които се викат от kid за да променят данните би ли било валидно
Виктор Бечев
19.12.2024 12:14

В повечето случаи е нежелателно да променяш атрибутите на клас или негова инстанция. Няма да ти създаде **никакъв** проблем по отношение на решението, така че те съветвам да не го променяш 6 часа преди крайния срок. Малко обяснение "защо" - когато създаваме подобни взаимовръзки между класовете те често остават скрити. Ако имаш наследяване - то е очевидно, ако имаш композиция - тя също. Обаче ако имаш очакване един класов атрибут да бъде променян _(обърни внимание - променян, а не четен, четенето е напълно окей)_ от един или повече външни обекта - това е доста по-неинтуитивно. Ако си представим, че правиш дизайн за нещо по-обемно, някакъв модул, който ще трябва други хора да ползват. Не би било особено логично да се очаква хората да присвояват стойности на или да променят някакъв атрибут на някакъв обект в твоя модул, за да може нещо да сработи. Класове и тяхното инстанциране, методи и функции, и техни извиквания - това са интуитивните неща. Вероятно ако историята на програмирането и на езиците беше различна - може би нямаше да се счита за лоша практика нещото, което дискутираме, но в случая "интуитивното" се определя на базата на това как работи всичко останало. Не искам да казвам "със сигурност", защото може и да излъжа, но е много малко вероятно да видиш подобно поведение в някоя от стандартните библиотеки или по-популярни питонски модули. Ако видиш нещо подобно - вероятно е свързано с конфигурация, а не с това как работи даден клас на фундаментално ниво. That being said, глобалните променливи са същото зло. Така че ако ще "поправиш" проблема с бъркането във външен обект с глобални променливи - недей.
Ева Ганчева
19.12.2024 02:41

kids и bad_kids трябва ли да са глобални, защото не знам дали е правилно от Kid да ги променям при положение, че са част от Santa. Та въпроса ми е грешно ли е в този случай( и като цяло) един клас да пипа по инстанциите на друг
История

f1import ref1import re
22
33
4class Singleton(type):4class Singleton(type):
5    """Metaclass for making only one instance for class"""5    """Metaclass for making only one instance for class"""
6    _instance = {}6    _instance = {}
77
8    def __call__(cls, *args, **kwargs):8    def __call__(cls, *args, **kwargs):
9        """Create new instance"""9        """Create new instance"""
10        if cls not in cls._instance:10        if cls not in cls._instance:
11            cls._instance[cls] = super(Singleton, cls).__call__(*args, **kwargs)11            cls._instance[cls] = super(Singleton, cls).__call__(*args, **kwargs)
12        return cls._instance[cls]12        return cls._instance[cls]
1313
1414
15class Santa(metaclass=Singleton):15class Santa(metaclass=Singleton):
16    """Class representing a white-bearded old man, bearer of gifts and coal"""16    """Class representing a white-bearded old man, bearer of gifts and coal"""
17    _wishes = {}17    _wishes = {}
18    kids = []18    kids = []
19    bad_kids = []19    bad_kids = []
2020
21    @staticmethod21    @staticmethod
n22    def _read_wish(wish, search_id=False):n22    def _read_wish(wish):
23        """Read wish"""23        """Read wish"""
24        gift = re.search(r'("[a-zA-Z0-9\s]+")|(\'[a-zA-Z0-9\s]+\')', wish)24        gift = re.search(r'("[a-zA-Z0-9\s]+")|(\'[a-zA-Z0-9\s]+\')', wish)
n25        if not search_id:n
26            return gift.group()[1:-1]25        return gift.group()[1:-1]
26    @staticmethod
27    def _read_id(wish):
28        """Read id from wish"""
27        kid_id = re.search(r'^\s*(\d+)\s*$', wish, re.MULTILINE)29        kid_id = re.search(r'^\s*(\d+)\s*$', wish, re.MULTILINE)
n28        return int(kid_id.group()), gift.group()[1:-1]n30        return int(kid_id.group())
2931
30    def __call__(self, child, wish):32    def __call__(self, child, wish):
31        """Receive wishes during phone call"""33        """Receive wishes during phone call"""
32        gift = self._read_wish(wish)34        gift = self._read_wish(wish)
33        self._wishes[child] = gift35        self._wishes[child] = gift
3436
35    def __matmul__(self, wish):37    def __matmul__(self, wish):
36        """Handle wish from letter"""38        """Handle wish from letter"""
n37        kid_id, gift = self._read_wish(wish, True)n39        gift = self._read_wish(wish)
40        kid_id = self._read_id(wish)
38        for kid in self.kids:41        for kid in self.kids:
39            if id(kid) == kid_id:42            if id(kid) == kid_id:
40                self._wishes[kid] = gift43                self._wishes[kid] = gift
4144
42    def __iter__(self):45    def __iter__(self):
43        """Allow to iterate Santa"""46        """Allow to iterate Santa"""
44        return iter(list(self._wishes.values()))47        return iter(list(self._wishes.values()))
4548
46    def _most_wanted(self):49    def _most_wanted(self):
47        """Find most wanted gift for this year"""50        """Find most wanted gift for this year"""
48        wish_counts = {}51        wish_counts = {}
49        most_wanted_wish = None52        most_wanted_wish = None
50        for wish in self._wishes.values():53        for wish in self._wishes.values():
51            if wish not in wish_counts:54            if wish not in wish_counts:
52                wish_counts[wish] = 055                wish_counts[wish] = 0
53            wish_counts[wish] += 156            wish_counts[wish] += 1
54            if most_wanted_wish is None or wish_counts[wish] > wish_counts[most_wanted_wish]:57            if most_wanted_wish is None or wish_counts[wish] > wish_counts[most_wanted_wish]:
55                most_wanted_wish = wish58                most_wanted_wish = wish
56        return most_wanted_wish59        return most_wanted_wish
5760
58    def xmas(self):61    def xmas(self):
59        """Handle the distribution"""62        """Handle the distribution"""
60        for kid in self.kids:63        for kid in self.kids:
61            kid.years += 164            kid.years += 1
62        if not self._wishes:65        if not self._wishes:
63            return66            return
64        default_gift = self._most_wanted()67        default_gift = self._most_wanted()
65        for kid in self.kids[:]:68        for kid in self.kids[:]:
66            if kid.years > 5:69            if kid.years > 5:
67                self.kids.remove(kid)70                self.kids.remove(kid)
68            else:71            else:
69                if kid in self.bad_kids:72                if kid in self.bad_kids:
70                    kid('coal')73                    kid('coal')
71                else:74                else:
72                    gift = self._wishes.get(kid, default_gift)75                    gift = self._wishes.get(kid, default_gift)
73                    kid(gift)76                    kid(gift)
74        self._wishes.clear()77        self._wishes.clear()
75        self.bad_kids.clear()78        self.bad_kids.clear()
7679
nn80    def add_to_kids(self, kid):
81        """Add kids to santa list"""
82        self.kids.append(kid)
83 
84    def add_to_bad_kids(self, kid):
85        """Add kids to bad kids"""
86        if kid not in self.bad_kids:
87            self.bad_kids.append(kid)
88 
7789
78class Kid(type):90class Kid(type):
79    """Metaclass for creating Kids"""91    """Metaclass for creating Kids"""
8092
81    def __call__(cls, *args, **kwargs):93    def __call__(cls, *args, **kwargs):
82        """Create new Kid"""94        """Create new Kid"""
nn95        santa = Santa()
83        instance = super().__call__(*args, **kwargs)96        instance = super().__call__(*args, **kwargs)
n84        Santa.kids.append(instance)n97        santa.add_to_kids(instance)
85        instance.years = 098        instance.years = 0
86        return instance99        return instance
87100
88    @staticmethod101    @staticmethod
89    def _decorator(func):102    def _decorator(func):
90        """Decorator that catches bad kids"""103        """Decorator that catches bad kids"""
nn104        santa = Santa()
91105
92        def wrapper(*args, **kwargs):106        def wrapper(*args, **kwargs):
93            result = None107            result = None
94            try:108            try:
95                result = func(*args, **kwargs)109                result = func(*args, **kwargs)
96            except Exception as e:110            except Exception as e:
t97                if args[0] not in Santa.bad_kids:t
98                    Santa.bad_kids.append(args[0])111                santa.add_to_bad_kids(args[0])
99                raise e112                raise e
100            return result113            return result
101114
102        return wrapper115        return wrapper
103116
104    @staticmethod117    @staticmethod
105    def _check_key(key):118    def _check_key(key):
106        """Check if the function is public"""119        """Check if the function is public"""
107        if re.match(r'^[^_]', key):120        if re.match(r'^[^_]', key):
108            return True121            return True
109        return False122        return False
110123
111    def __new__(cls, name, bases, attr_dict):124    def __new__(cls, name, bases, attr_dict):
112        """Call decorator to help Santa punish bad kids"""125        """Call decorator to help Santa punish bad kids"""
113        if '__call__' not in attr_dict:126        if '__call__' not in attr_dict:
114            raise NotImplementedError()127            raise NotImplementedError()
115128
116        for key, value in attr_dict.items():129        for key, value in attr_dict.items():
117            if callable(value) and cls._check_key(key):130            if callable(value) and cls._check_key(key):
118                attr_dict[key] = cls._decorator(value)131                attr_dict[key] = cls._decorator(value)
119        return super().__new__(cls, name, bases, attr_dict)132        return super().__new__(cls, name, bases, attr_dict)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import ref1import re
22
33
4class Singleton(type):4class Singleton(type):
5    """Metaclass for making only one instance for class"""5    """Metaclass for making only one instance for class"""
6    _instance = {}6    _instance = {}
77
8    def __call__(cls, *args, **kwargs):8    def __call__(cls, *args, **kwargs):
9        """Create new instance"""9        """Create new instance"""
10        if cls not in cls._instance:10        if cls not in cls._instance:
11            cls._instance[cls] = super(Singleton, cls).__call__(*args, **kwargs)11            cls._instance[cls] = super(Singleton, cls).__call__(*args, **kwargs)
12        return cls._instance[cls]12        return cls._instance[cls]
1313
1414
15class Santa(metaclass=Singleton):15class Santa(metaclass=Singleton):
16    """Class representing a white-bearded old man, bearer of gifts and coal"""16    """Class representing a white-bearded old man, bearer of gifts and coal"""
17    _wishes = {}17    _wishes = {}
18    kids = []18    kids = []
19    bad_kids = []19    bad_kids = []
2020
21    @staticmethod21    @staticmethod
22    def _read_wish(wish, search_id=False):22    def _read_wish(wish, search_id=False):
23        """Read wish"""23        """Read wish"""
24        gift = re.search(r'("[a-zA-Z0-9\s]+")|(\'[a-zA-Z0-9\s]+\')', wish)24        gift = re.search(r'("[a-zA-Z0-9\s]+")|(\'[a-zA-Z0-9\s]+\')', wish)
25        if not search_id:25        if not search_id:
26            return gift.group()[1:-1]26            return gift.group()[1:-1]
27        kid_id = re.search(r'^\s*(\d+)\s*$', wish, re.MULTILINE)27        kid_id = re.search(r'^\s*(\d+)\s*$', wish, re.MULTILINE)
28        return int(kid_id.group()), gift.group()[1:-1]28        return int(kid_id.group()), gift.group()[1:-1]
2929
30    def __call__(self, child, wish):30    def __call__(self, child, wish):
31        """Receive wishes during phone call"""31        """Receive wishes during phone call"""
32        gift = self._read_wish(wish)32        gift = self._read_wish(wish)
33        self._wishes[child] = gift33        self._wishes[child] = gift
3434
35    def __matmul__(self, wish):35    def __matmul__(self, wish):
36        """Handle wish from letter"""36        """Handle wish from letter"""
37        kid_id, gift = self._read_wish(wish, True)37        kid_id, gift = self._read_wish(wish, True)
38        for kid in self.kids:38        for kid in self.kids:
39            if id(kid) == kid_id:39            if id(kid) == kid_id:
40                self._wishes[kid] = gift40                self._wishes[kid] = gift
4141
42    def __iter__(self):42    def __iter__(self):
43        """Allow to iterate Santa"""43        """Allow to iterate Santa"""
44        return iter(list(self._wishes.values()))44        return iter(list(self._wishes.values()))
4545
46    def _most_wanted(self):46    def _most_wanted(self):
47        """Find most wanted gift for this year"""47        """Find most wanted gift for this year"""
48        wish_counts = {}48        wish_counts = {}
49        most_wanted_wish = None49        most_wanted_wish = None
50        for wish in self._wishes.values():50        for wish in self._wishes.values():
51            if wish not in wish_counts:51            if wish not in wish_counts:
52                wish_counts[wish] = 052                wish_counts[wish] = 0
53            wish_counts[wish] += 153            wish_counts[wish] += 1
54            if most_wanted_wish is None or wish_counts[wish] > wish_counts[most_wanted_wish]:54            if most_wanted_wish is None or wish_counts[wish] > wish_counts[most_wanted_wish]:
55                most_wanted_wish = wish55                most_wanted_wish = wish
56        return most_wanted_wish56        return most_wanted_wish
5757
58    def xmas(self):58    def xmas(self):
59        """Handle the distribution"""59        """Handle the distribution"""
60        for kid in self.kids:60        for kid in self.kids:
61            kid.years += 161            kid.years += 1
62        if not self._wishes:62        if not self._wishes:
63            return63            return
64        default_gift = self._most_wanted()64        default_gift = self._most_wanted()
65        for kid in self.kids[:]:65        for kid in self.kids[:]:
66            if kid.years > 5:66            if kid.years > 5:
67                self.kids.remove(kid)67                self.kids.remove(kid)
68            else:68            else:
69                if kid in self.bad_kids:69                if kid in self.bad_kids:
70                    kid('coal')70                    kid('coal')
71                else:71                else:
72                    gift = self._wishes.get(kid, default_gift)72                    gift = self._wishes.get(kid, default_gift)
73                    kid(gift)73                    kid(gift)
74        self._wishes.clear()74        self._wishes.clear()
75        self.bad_kids.clear()75        self.bad_kids.clear()
7676
7777
78class Kid(type):78class Kid(type):
79    """Metaclass for creating Kids"""79    """Metaclass for creating Kids"""
8080
81    def __call__(cls, *args, **kwargs):81    def __call__(cls, *args, **kwargs):
82        """Create new Kid"""82        """Create new Kid"""
83        instance = super().__call__(*args, **kwargs)83        instance = super().__call__(*args, **kwargs)
84        Santa.kids.append(instance)84        Santa.kids.append(instance)
85        instance.years = 085        instance.years = 0
86        return instance86        return instance
8787
88    @staticmethod88    @staticmethod
89    def _decorator(func):89    def _decorator(func):
90        """Decorator that catches bad kids"""90        """Decorator that catches bad kids"""
9191
92        def wrapper(*args, **kwargs):92        def wrapper(*args, **kwargs):
93            result = None93            result = None
94            try:94            try:
95                result = func(*args, **kwargs)95                result = func(*args, **kwargs)
96            except Exception as e:96            except Exception as e:
97                if args[0] not in Santa.bad_kids:97                if args[0] not in Santa.bad_kids:
98                    Santa.bad_kids.append(args[0])98                    Santa.bad_kids.append(args[0])
99                raise e99                raise e
100            return result100            return result
101101
102        return wrapper102        return wrapper
103103
104    @staticmethod104    @staticmethod
n105    def check_key(key):n105    def _check_key(key):
106        """Check if the function is public"""106        """Check if the function is public"""
107        if re.match(r'^[^_]', key):107        if re.match(r'^[^_]', key):
108            return True108            return True
109        return False109        return False
110110
111    def __new__(cls, name, bases, attr_dict):111    def __new__(cls, name, bases, attr_dict):
112        """Call decorator to help Santa punish bad kids"""112        """Call decorator to help Santa punish bad kids"""
113        if '__call__' not in attr_dict:113        if '__call__' not in attr_dict:
114            raise NotImplementedError()114            raise NotImplementedError()
115115
116        for key, value in attr_dict.items():116        for key, value in attr_dict.items():
t117            if callable(value) and cls.check_key(key):t117            if callable(value) and cls._check_key(key):
118                attr_dict[key] = cls._decorator(value)118                attr_dict[key] = cls._decorator(value)
119        return super().__new__(cls, name, bases, attr_dict)119        return super().__new__(cls, name, bases, attr_dict)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op