1def times_decorator(func, times):
2 def wrapper(target):
3 for _ in range(times):
4 func(target)
5
6 return wrapper
7
8
9def is_merged_decorator(func):
10 def wrapper(*args, **kwargs):
11 self = args[0]
12 other = args[1]
13 if self.is_merged or (other.is_merged if isinstance(other, Potion) else False):
14 raise TypeError('Potion is now part of something bigger than itself.')
15 try:
16 return func(*args, **kwargs)
17 finally:
18 if func.__name__ != '__getattr__':
19 """Set both both potions as merged and 'part of something bigger than itself'
20 if the function is not __getattr__.
21 If the potion is merged, __getattr__ will also raise an exception.
22 """
23 self.set_is_merged(True)
24 other.set_is_merged(True)
25
26 return wrapper
27
28
29class Potion:
30 def __init__(self, effects, duration, intensities=None):
31 self._effects = effects
32 self._intensities = intensities or {effect: 1 for effect in effects}
33 self._depleted = set()
34 self._duration = duration
35 self._is_merged = False
36 self._is_depleted = False
37
38 @property
39 def duration(self):
40 return self._duration
41
42 @property
43 def is_merged(self):
44 return self._is_merged
45
46 @property
47 def effects(self):
48 return list(self._effects.keys())
49
50 @property
51 def depleted(self):
52 return self._depleted
53
54 def __getitem__(self, item):
55 return self.__getattr__(item)
56
57 def set_is_depleted(self):
58 self._is_depleted = True
59
60 def set_is_merged(self, value):
61 self._is_merged = value
62
63 @is_merged_decorator
64 def __getattr__(self, effect_name):
65 if effect_name not in self._effects:
66 raise AttributeError
67
68 if effect_name in self._depleted:
69 raise TypeError('Effect is depleted')
70
71 if self._is_depleted:
72 raise TypeError('Potion is depleted.')
73
74 self._depleted.add(effect_name)
75 return times_decorator(self._effects[effect_name], self._intensities[effect_name])
76
77 @is_merged_decorator
78 def __add__(self, other):
79 if not isinstance(other, Potion):
80 raise TypeError('Potions can only be combined with other potions')
81
82 effects = {**self._effects, **other._effects}
83 duration = max(self._duration, other._duration)
84 intensities = {
85 effect: (self._intensities.get(effect, 0)) + (other._intensities.get(effect, 0))
86 for effect in effects
87 }
88 return Potion(effects, duration, intensities)
89
90 @is_merged_decorator
91 def __sub__(self, other):
92 if not isinstance(other, Potion):
93 raise TypeError('Potions can only be combined with other potions')
94
95 if other._effects.keys() - self._effects.keys() != set():
96 raise TypeError('All effects of the right potion must be present in the left potion')
97
98 duration = self._duration
99 effects = {}
100 intensities = {}
101 for effect in self._effects:
102 if effect in other._effects:
103 new_effect_intensity = self._intensities.get(effect) - other._intensities.get(effect)
104 if new_effect_intensity > 0:
105 effects[effect] = self._effects[effect]
106 intensities[effect] = new_effect_intensity
107 else:
108 effects[effect] = self._effects[effect]
109 intensities[effect] = self._intensities[effect]
110
111 return Potion(effects, duration, intensities)
112
113 @is_merged_decorator
114 def __mul__(self, intensity_growth):
115 try:
116 float(intensity_growth)
117 except ValueError:
118 raise TypeError('Potions can only be multiplied by integers')
119
120 effects = {**self._effects}
121 duration = self._duration
122 intensities = {
123 effect: round(self._intensities.get(effect, 0) * intensity_growth)
124 for effect in effects
125 }
126 return Potion(effects, duration, intensities)
127
128 @is_merged_decorator
129 def __truediv__(self, new_potions_count):
130 try:
131 int(new_potions_count)
132 except ValueError:
133 raise TypeError('Potions can only be divided by integers')
134
135 return tuple([Potion({**self._effects},
136 self._duration,
137 {effect: round(self._intensities.get(effect, 0) / new_potions_count)
138 for effect in self._intensities}
139 ) for _ in range(new_potions_count)])
140
141 def __eq__(self, other):
142 if not isinstance(other, Potion):
143 return False
144
145 return self._effects.keys() - other._effects.keys() == set() and all(
146 [self._intensities[effect] == other._intensities[effect] for effect in self._effects.keys()]
147 )
148
149 def __lt__(self, other):
150 if not isinstance(other, Potion):
151 return False
152
153 return sum(self._intensities.values()) < sum(other._intensities.values())
154
155 def __gt__(self, other):
156 if not isinstance(other, Potion):
157 return False
158
159 return not self == other and not self < other
160
161
162class ГоспожатаПоХимия:
163 def __init__(self):
164 self._target = None
165 self._target_public_attributes = None
166 self._current_tick_duration = None
167
168 def _sort_effects_by_molecule_mass(self, effects):
169 new_effects = list(effects)[:]
170 return sorted(new_effects, key=lambda effect: sum(ord(ch) for ch in effect), reverse=True)
171
172 def _get_public_attributes(self, target):
173 all_attributes = vars(target)
174 return {key: value for key, value in all_attributes.items() if
175 not key.startswith('_')}
176
177 def apply(self, target, potion):
178 self._target_public_attributes = self._get_public_attributes(target)
179 self._target = target
180 self._current_tick_duration = potion.duration
181
182 for effect in self._sort_effects_by_molecule_mass(potion.effects):
183 potion[effect](target)
184
185 if set(potion.depleted) == set(potion.effects):
186 potion.set_is_depleted()
187
188 def tick(self):
189 if not self._current_tick_duration:
190 raise Exception('No potion applied')
191
192 self._current_tick_duration -= 1
193
194 if self._current_tick_duration != 0:
195 return
196
197 current_public_attributes = self._get_public_attributes(self._target)
198 for key in current_public_attributes:
199 if key in self._target_public_attributes:
200 setattr(self._target, key, self._target_public_attributes[key])
201 else:
202 del self._target.__dict__[key]
.F.EE..EEEEEF..E.FEF
======================================================================
ERROR: test_equal (test.TestPotionComparison)
Test equality of potions.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 296, in test_equal
potion3 = Potion({'int_attr_fun': int_attr_fun}, duration=1) * 2
File "/tmp/solution.py", line 24, in wrapper
other.set_is_merged(True)
AttributeError: 'int' object has no attribute 'set_is_merged'
======================================================================
ERROR: test_superbness (test.TestPotionComparison)
Test superbness of potions.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 316, in test_superbness
potion2 = Potion({'int_attr_fun': int_attr_fun}, duration=1) * 2
File "/tmp/solution.py", line 24, in wrapper
other.set_is_merged(True)
AttributeError: 'int' object has no attribute 'set_is_merged'
======================================================================
ERROR: test_deprecation (test.TestPotionOperations)
Test deprecation of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 263, in test_deprecation
potion = potion1 * 2
File "/tmp/solution.py", line 24, in wrapper
other.set_is_merged(True)
AttributeError: 'int' object has no attribute 'set_is_merged'
======================================================================
ERROR: test_dilution (test.TestPotionOperations)
Test dilution of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 118, in test_dilution
half_potion = base_potion * 0.5
File "/tmp/solution.py", line 24, in wrapper
other.set_is_merged(True)
AttributeError: 'float' object has no attribute 'set_is_merged'
======================================================================
ERROR: test_potentiation (test.TestPotionOperations)
Test potentiation of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 110, in test_potentiation
potion = potion * 3
File "/tmp/solution.py", line 24, in wrapper
other.set_is_merged(True)
AttributeError: 'int' object has no attribute 'set_is_merged'
======================================================================
ERROR: test_purification (test.TestPotionOperations)
Test purification of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 183, in test_purification
potion1 = Potion({'int_attr_fun': int_attr_fun}, duration=1) * 3
File "/tmp/solution.py", line 24, in wrapper
other.set_is_merged(True)
AttributeError: 'int' object has no attribute 'set_is_merged'
======================================================================
ERROR: test_separation (test.TestPotionOperations)
Test separation of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 212, in test_separation
potion = Potion({'int_attr_fun': int_attr_fun}, duration=1) * 9
File "/tmp/solution.py", line 24, in wrapper
other.set_is_merged(True)
AttributeError: 'int' object has no attribute 'set_is_merged'
======================================================================
ERROR: test_applying_part_of_potion (test.TestГоспожатаПоХимия)
Test applying only a part of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 365, in test_applying_part_of_potion
self._dimitrichka.apply(self._target, potion)
File "/tmp/solution.py", line 183, in apply
potion[effect](target)
File "/tmp/solution.py", line 55, in __getitem__
return self.__getattr__(item)
File "/tmp/solution.py", line 16, in wrapper
return func(*args, **kwargs)
File "/tmp/solution.py", line 69, in __getattr__
raise TypeError('Effect is depleted')
TypeError: Effect is depleted
======================================================================
ERROR: test_ticking_multiple_targets (test.TestГоспожатаПоХимия)
Test ticking after applying a potion with mutable attributes.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 479, in test_ticking_multiple_targets
potion1 = Potion({'int_attr_fun': int_attr_fun}, duration=1) * 2
File "/tmp/solution.py", line 24, in wrapper
other.set_is_merged(True)
AttributeError: 'int' object has no attribute 'set_is_merged'
======================================================================
FAIL: test_depletion (test.TestBasicPotion)
Test depletion of a potion effect.
----------------------------------------------------------------------
TypeError: Effect is depleted
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/tmp/test.py", line 78, in test_depletion
with self.assertRaisesRegex(TypeError, 'Effect is depleted\.'):
AssertionError: "Effect is depleted\." does not match "Effect is depleted"
======================================================================
FAIL: test_applying_depleted_potion (test.TestГоспожатаПоХимия)
Test applying a depleted potion or a potion that was used in a reaction.
----------------------------------------------------------------------
TypeError: Effect is depleted
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/tmp/test.py", line 380, in test_applying_depleted_potion
with self.assertRaisesRegex(TypeError, 'Potion is depleted\.'):
AssertionError: "Potion is depleted\." does not match "Effect is depleted"
======================================================================
FAIL: test_ticking_multiple_potions (test.TestГоспожатаПоХимия)
Test ticking after applying multiple potions which affect the same attribute.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 458, in test_ticking_multiple_potions
self.assertEqual(self._target.int_attr, 50)
AssertionError: 500 != 50
======================================================================
FAIL: test_ticking_mutable (test.TestГоспожатаПоХимия)
Test ticking after applying a potion with mutable attributes.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 446, in test_ticking_mutable
self.assertEqual(self._target.list_attr, [1, 2, 3])
AssertionError: Lists differ: [1, 2, 3, 4] != [1, 2, 3]
First list contains 1 additional elements.
First extra element 3:
4
- [1, 2, 3, 4]
? ---
+ [1, 2, 3]
----------------------------------------------------------------------
Ran 20 tests in 0.002s
FAILED (failures=4, errors=9)
Виктор Бечев
03.12.2023 23:51Да, ако имаш дефинирана `__eq__` и едно от `__gt__` или `__lt__` - Python ще се оправи с останалите. **В общия случай** е така, пак казвам - тук логиката може да се окаже шано, не мога да го сметна наум. :)
|
Филип Филчев
03.12.2023 15:09Относно коментара за \_\_gt__, не е нужно да се предефинира ли даже, ако е в такъв вид? Тоест == и < са достатъчни? Може да е споменавано на лекция, но не съм запомнил.
|
Филип Филчев
03.12.2023 15:08Ами да осъзнавам го това, но според мен е логично подобен проблем да се обособи по някакъв такъв начин, в случая с декоратор. Пък и тъкмо да се упражня с декоратори, понеже ме кефят.
Уж го изтествах и работи. Ако ми остане време преди крайния срок ще се опитам да го счупя.
|
Виктор Бечев
03.12.2023 13:44Не са драма декораторите, проблем е, че се опитваш да генерализираш декориране на неща, които имат различни сигнатури (i.e. в единия случай приемат `Potion` и `Potion`, в другия `Potion` и `int` и т.н.), така че дано си се подсигурил, че това не обърква нещо.
|
Филип Филчев
03.12.2023 00:46Реших да си поиграя с декоратори, получи се доста грозно, че даже и може да не работи много като хората, ама кво такова :)
|
| f | 1 | def times_decorator(func, times): | f | 1 | def times_decorator(func, times): |
| 2 | def wrapper(target): | 2 | def wrapper(target): | ||
| 3 | for _ in range(times): | 3 | for _ in range(times): | ||
| 4 | func(target) | 4 | func(target) | ||
| 5 | 5 | ||||
| 6 | return wrapper | 6 | return wrapper | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| 9 | def is_merged_decorator(func): | 9 | def is_merged_decorator(func): | ||
| 10 | def wrapper(*args, **kwargs): | 10 | def wrapper(*args, **kwargs): | ||
| 11 | self = args[0] | 11 | self = args[0] | ||
| 12 | other = args[1] | 12 | other = args[1] | ||
| n | 13 | if self.is_merged or other.is_merged: | n | 13 | if self.is_merged or (other.is_merged if isinstance(other, Potion) else False): |
| 14 | raise TypeError('Potion is now part of something bigger than itself.') | 14 | raise TypeError('Potion is now part of something bigger than itself.') | ||
| 15 | try: | 15 | try: | ||
| 16 | return func(*args, **kwargs) | 16 | return func(*args, **kwargs) | ||
| 17 | finally: | 17 | finally: | ||
| 18 | if func.__name__ != '__getattr__': | 18 | if func.__name__ != '__getattr__': | ||
| 19 | """Set both both potions as merged and 'part of something bigger than itself' | 19 | """Set both both potions as merged and 'part of something bigger than itself' | ||
| 20 | if the function is not __getattr__. | 20 | if the function is not __getattr__. | ||
| 21 | If the potion is merged, __getattr__ will also raise an exception. | 21 | If the potion is merged, __getattr__ will also raise an exception. | ||
| 22 | """ | 22 | """ | ||
| 23 | self.set_is_merged(True) | 23 | self.set_is_merged(True) | ||
| 24 | other.set_is_merged(True) | 24 | other.set_is_merged(True) | ||
| 25 | 25 | ||||
| 26 | return wrapper | 26 | return wrapper | ||
| 27 | 27 | ||||
| 28 | 28 | ||||
| 29 | class Potion: | 29 | class Potion: | ||
| 30 | def __init__(self, effects, duration, intensities=None): | 30 | def __init__(self, effects, duration, intensities=None): | ||
| 31 | self._effects = effects | 31 | self._effects = effects | ||
| 32 | self._intensities = intensities or {effect: 1 for effect in effects} | 32 | self._intensities = intensities or {effect: 1 for effect in effects} | ||
| 33 | self._depleted = set() | 33 | self._depleted = set() | ||
| 34 | self._duration = duration | 34 | self._duration = duration | ||
| 35 | self._is_merged = False | 35 | self._is_merged = False | ||
| 36 | self._is_depleted = False | 36 | self._is_depleted = False | ||
| 37 | 37 | ||||
| 38 | @property | 38 | @property | ||
| 39 | def duration(self): | 39 | def duration(self): | ||
| 40 | return self._duration | 40 | return self._duration | ||
| 41 | 41 | ||||
| 42 | @property | 42 | @property | ||
| 43 | def is_merged(self): | 43 | def is_merged(self): | ||
| 44 | return self._is_merged | 44 | return self._is_merged | ||
| 45 | 45 | ||||
| 46 | @property | 46 | @property | ||
| 47 | def effects(self): | 47 | def effects(self): | ||
| 48 | return list(self._effects.keys()) | 48 | return list(self._effects.keys()) | ||
| 49 | 49 | ||||
| 50 | @property | 50 | @property | ||
| 51 | def depleted(self): | 51 | def depleted(self): | ||
| 52 | return self._depleted | 52 | return self._depleted | ||
| 53 | 53 | ||||
| 54 | def __getitem__(self, item): | 54 | def __getitem__(self, item): | ||
| 55 | return self.__getattr__(item) | 55 | return self.__getattr__(item) | ||
| 56 | 56 | ||||
| 57 | def set_is_depleted(self): | 57 | def set_is_depleted(self): | ||
| 58 | self._is_depleted = True | 58 | self._is_depleted = True | ||
| 59 | 59 | ||||
| 60 | def set_is_merged(self, value): | 60 | def set_is_merged(self, value): | ||
| 61 | self._is_merged = value | 61 | self._is_merged = value | ||
| 62 | 62 | ||||
| 63 | @is_merged_decorator | 63 | @is_merged_decorator | ||
| 64 | def __getattr__(self, effect_name): | 64 | def __getattr__(self, effect_name): | ||
| 65 | if effect_name not in self._effects: | 65 | if effect_name not in self._effects: | ||
| 66 | raise AttributeError | 66 | raise AttributeError | ||
| 67 | 67 | ||||
| 68 | if effect_name in self._depleted: | 68 | if effect_name in self._depleted: | ||
| 69 | raise TypeError('Effect is depleted') | 69 | raise TypeError('Effect is depleted') | ||
| 70 | 70 | ||||
| 71 | if self._is_depleted: | 71 | if self._is_depleted: | ||
| 72 | raise TypeError('Potion is depleted.') | 72 | raise TypeError('Potion is depleted.') | ||
| 73 | 73 | ||||
| 74 | self._depleted.add(effect_name) | 74 | self._depleted.add(effect_name) | ||
| 75 | return times_decorator(self._effects[effect_name], self._intensities[effect_name]) | 75 | return times_decorator(self._effects[effect_name], self._intensities[effect_name]) | ||
| 76 | 76 | ||||
| 77 | @is_merged_decorator | 77 | @is_merged_decorator | ||
| 78 | def __add__(self, other): | 78 | def __add__(self, other): | ||
| 79 | if not isinstance(other, Potion): | 79 | if not isinstance(other, Potion): | ||
| 80 | raise TypeError('Potions can only be combined with other potions') | 80 | raise TypeError('Potions can only be combined with other potions') | ||
| 81 | 81 | ||||
| 82 | effects = {**self._effects, **other._effects} | 82 | effects = {**self._effects, **other._effects} | ||
| 83 | duration = max(self._duration, other._duration) | 83 | duration = max(self._duration, other._duration) | ||
| 84 | intensities = { | 84 | intensities = { | ||
| 85 | effect: (self._intensities.get(effect, 0)) + (other._intensities.get(effect, 0)) | 85 | effect: (self._intensities.get(effect, 0)) + (other._intensities.get(effect, 0)) | ||
| 86 | for effect in effects | 86 | for effect in effects | ||
| 87 | } | 87 | } | ||
| 88 | return Potion(effects, duration, intensities) | 88 | return Potion(effects, duration, intensities) | ||
| 89 | 89 | ||||
| 90 | @is_merged_decorator | 90 | @is_merged_decorator | ||
| 91 | def __sub__(self, other): | 91 | def __sub__(self, other): | ||
| 92 | if not isinstance(other, Potion): | 92 | if not isinstance(other, Potion): | ||
| 93 | raise TypeError('Potions can only be combined with other potions') | 93 | raise TypeError('Potions can only be combined with other potions') | ||
| 94 | 94 | ||||
| 95 | if other._effects.keys() - self._effects.keys() != set(): | 95 | if other._effects.keys() - self._effects.keys() != set(): | ||
| 96 | raise TypeError('All effects of the right potion must be present in the left potion') | 96 | raise TypeError('All effects of the right potion must be present in the left potion') | ||
| 97 | 97 | ||||
| 98 | duration = self._duration | 98 | duration = self._duration | ||
| 99 | effects = {} | 99 | effects = {} | ||
| 100 | intensities = {} | 100 | intensities = {} | ||
| 101 | for effect in self._effects: | 101 | for effect in self._effects: | ||
| 102 | if effect in other._effects: | 102 | if effect in other._effects: | ||
| 103 | new_effect_intensity = self._intensities.get(effect) - other._intensities.get(effect) | 103 | new_effect_intensity = self._intensities.get(effect) - other._intensities.get(effect) | ||
| 104 | if new_effect_intensity > 0: | 104 | if new_effect_intensity > 0: | ||
| 105 | effects[effect] = self._effects[effect] | 105 | effects[effect] = self._effects[effect] | ||
| 106 | intensities[effect] = new_effect_intensity | 106 | intensities[effect] = new_effect_intensity | ||
| 107 | else: | 107 | else: | ||
| 108 | effects[effect] = self._effects[effect] | 108 | effects[effect] = self._effects[effect] | ||
| 109 | intensities[effect] = self._intensities[effect] | 109 | intensities[effect] = self._intensities[effect] | ||
| 110 | 110 | ||||
| 111 | return Potion(effects, duration, intensities) | 111 | return Potion(effects, duration, intensities) | ||
| 112 | 112 | ||||
| 113 | @is_merged_decorator | 113 | @is_merged_decorator | ||
| 114 | def __mul__(self, intensity_growth): | 114 | def __mul__(self, intensity_growth): | ||
| 115 | try: | 115 | try: | ||
| 116 | float(intensity_growth) | 116 | float(intensity_growth) | ||
| 117 | except ValueError: | 117 | except ValueError: | ||
| 118 | raise TypeError('Potions can only be multiplied by integers') | 118 | raise TypeError('Potions can only be multiplied by integers') | ||
| 119 | 119 | ||||
| 120 | effects = {**self._effects} | 120 | effects = {**self._effects} | ||
| 121 | duration = self._duration | 121 | duration = self._duration | ||
| 122 | intensities = { | 122 | intensities = { | ||
| 123 | effect: round(self._intensities.get(effect, 0) * intensity_growth) | 123 | effect: round(self._intensities.get(effect, 0) * intensity_growth) | ||
| 124 | for effect in effects | 124 | for effect in effects | ||
| 125 | } | 125 | } | ||
| 126 | return Potion(effects, duration, intensities) | 126 | return Potion(effects, duration, intensities) | ||
| 127 | 127 | ||||
| 128 | @is_merged_decorator | 128 | @is_merged_decorator | ||
| 129 | def __truediv__(self, new_potions_count): | 129 | def __truediv__(self, new_potions_count): | ||
| 130 | try: | 130 | try: | ||
| 131 | int(new_potions_count) | 131 | int(new_potions_count) | ||
| 132 | except ValueError: | 132 | except ValueError: | ||
| 133 | raise TypeError('Potions can only be divided by integers') | 133 | raise TypeError('Potions can only be divided by integers') | ||
| 134 | 134 | ||||
| 135 | return tuple([Potion({**self._effects}, | 135 | return tuple([Potion({**self._effects}, | ||
| 136 | self._duration, | 136 | self._duration, | ||
| 137 | {effect: round(self._intensities.get(effect, 0) / new_potions_count) | 137 | {effect: round(self._intensities.get(effect, 0) / new_potions_count) | ||
| 138 | for effect in self._intensities} | 138 | for effect in self._intensities} | ||
| 139 | ) for _ in range(new_potions_count)]) | 139 | ) for _ in range(new_potions_count)]) | ||
| 140 | 140 | ||||
| 141 | def __eq__(self, other): | 141 | def __eq__(self, other): | ||
| 142 | if not isinstance(other, Potion): | 142 | if not isinstance(other, Potion): | ||
| 143 | return False | 143 | return False | ||
| 144 | 144 | ||||
| 145 | return self._effects.keys() - other._effects.keys() == set() and all( | 145 | return self._effects.keys() - other._effects.keys() == set() and all( | ||
| 146 | [self._intensities[effect] == other._intensities[effect] for effect in self._effects.keys()] | 146 | [self._intensities[effect] == other._intensities[effect] for effect in self._effects.keys()] | ||
| 147 | ) | 147 | ) | ||
| 148 | 148 | ||||
| 149 | def __lt__(self, other): | 149 | def __lt__(self, other): | ||
| 150 | if not isinstance(other, Potion): | 150 | if not isinstance(other, Potion): | ||
| 151 | return False | 151 | return False | ||
| 152 | 152 | ||||
| 153 | return sum(self._intensities.values()) < sum(other._intensities.values()) | 153 | return sum(self._intensities.values()) < sum(other._intensities.values()) | ||
| 154 | 154 | ||||
| 155 | def __gt__(self, other): | 155 | def __gt__(self, other): | ||
| 156 | if not isinstance(other, Potion): | 156 | if not isinstance(other, Potion): | ||
| 157 | return False | 157 | return False | ||
| 158 | 158 | ||||
| 159 | return not self == other and not self < other | 159 | return not self == other and not self < other | ||
| 160 | 160 | ||||
| 161 | 161 | ||||
| 162 | class ГоспожатаПоХимия: | 162 | class ГоспожатаПоХимия: | ||
| 163 | def __init__(self): | 163 | def __init__(self): | ||
| 164 | self._target = None | 164 | self._target = None | ||
| 165 | self._target_public_attributes = None | 165 | self._target_public_attributes = None | ||
| 166 | self._current_tick_duration = None | 166 | self._current_tick_duration = None | ||
| 167 | 167 | ||||
| 168 | def _sort_effects_by_molecule_mass(self, effects): | 168 | def _sort_effects_by_molecule_mass(self, effects): | ||
| 169 | new_effects = list(effects)[:] | 169 | new_effects = list(effects)[:] | ||
| 170 | return sorted(new_effects, key=lambda effect: sum(ord(ch) for ch in effect), reverse=True) | 170 | return sorted(new_effects, key=lambda effect: sum(ord(ch) for ch in effect), reverse=True) | ||
| 171 | 171 | ||||
| 172 | def _get_public_attributes(self, target): | 172 | def _get_public_attributes(self, target): | ||
| 173 | all_attributes = vars(target) | 173 | all_attributes = vars(target) | ||
| 174 | return {key: value for key, value in all_attributes.items() if | 174 | return {key: value for key, value in all_attributes.items() if | ||
| 175 | not key.startswith('_')} | 175 | not key.startswith('_')} | ||
| 176 | 176 | ||||
| 177 | def apply(self, target, potion): | 177 | def apply(self, target, potion): | ||
| 178 | self._target_public_attributes = self._get_public_attributes(target) | 178 | self._target_public_attributes = self._get_public_attributes(target) | ||
| 179 | self._target = target | 179 | self._target = target | ||
| 180 | self._current_tick_duration = potion.duration | 180 | self._current_tick_duration = potion.duration | ||
| 181 | 181 | ||||
| 182 | for effect in self._sort_effects_by_molecule_mass(potion.effects): | 182 | for effect in self._sort_effects_by_molecule_mass(potion.effects): | ||
| 183 | potion[effect](target) | 183 | potion[effect](target) | ||
| 184 | 184 | ||||
| 185 | if set(potion.depleted) == set(potion.effects): | 185 | if set(potion.depleted) == set(potion.effects): | ||
| 186 | potion.set_is_depleted() | 186 | potion.set_is_depleted() | ||
| 187 | 187 | ||||
| 188 | def tick(self): | 188 | def tick(self): | ||
| 189 | if not self._current_tick_duration: | 189 | if not self._current_tick_duration: | ||
| 190 | raise Exception('No potion applied') | 190 | raise Exception('No potion applied') | ||
| 191 | 191 | ||||
| 192 | self._current_tick_duration -= 1 | 192 | self._current_tick_duration -= 1 | ||
| 193 | 193 | ||||
| 194 | if self._current_tick_duration != 0: | 194 | if self._current_tick_duration != 0: | ||
| 195 | return | 195 | return | ||
| 196 | 196 | ||||
| 197 | current_public_attributes = self._get_public_attributes(self._target) | 197 | current_public_attributes = self._get_public_attributes(self._target) | ||
| 198 | for key in current_public_attributes: | 198 | for key in current_public_attributes: | ||
| 199 | if key in self._target_public_attributes: | 199 | if key in self._target_public_attributes: | ||
| 200 | setattr(self._target, key, self._target_public_attributes[key]) | 200 | setattr(self._target, key, self._target_public_attributes[key]) | ||
| 201 | else: | 201 | else: | ||
| 202 | del self._target.__dict__[key] | 202 | del self._target.__dict__[key] | ||
| t | t | 203 |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | def times_decorator(func, times): | f | 1 | def times_decorator(func, times): |
| 2 | def wrapper(target): | 2 | def wrapper(target): | ||
| 3 | for _ in range(times): | 3 | for _ in range(times): | ||
| 4 | func(target) | 4 | func(target) | ||
| 5 | 5 | ||||
| 6 | return wrapper | 6 | return wrapper | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| 9 | def is_merged_decorator(func): | 9 | def is_merged_decorator(func): | ||
| 10 | def wrapper(*args, **kwargs): | 10 | def wrapper(*args, **kwargs): | ||
| 11 | self = args[0] | 11 | self = args[0] | ||
| n | n | 12 | other = args[1] | ||
| 12 | if self._is_merged: | 13 | if self.is_merged or other.is_merged: | ||
| 13 | raise TypeError('Potion is now part of something bigger than itself.') | 14 | raise TypeError('Potion is now part of something bigger than itself.') | ||
| 14 | try: | 15 | try: | ||
| 15 | return func(*args, **kwargs) | 16 | return func(*args, **kwargs) | ||
| 16 | finally: | 17 | finally: | ||
| 17 | if func.__name__ != '__getattr__': | 18 | if func.__name__ != '__getattr__': | ||
| n | n | 19 | """Set both both potions as merged and 'part of something bigger than itself' | ||
| 20 | if the function is not __getattr__. | ||||
| 21 | If the potion is merged, __getattr__ will also raise an exception. | ||||
| 22 | """ | ||||
| 18 | self._is_merged = True | 23 | self.set_is_merged(True) | ||
| 24 | other.set_is_merged(True) | ||||
| 19 | 25 | ||||
| 20 | return wrapper | 26 | return wrapper | ||
| 21 | 27 | ||||
| 22 | 28 | ||||
| 23 | class Potion: | 29 | class Potion: | ||
| 24 | def __init__(self, effects, duration, intensities=None): | 30 | def __init__(self, effects, duration, intensities=None): | ||
| n | 25 | self._is_merged = False | n | ||
| 26 | self._effects = effects | 31 | self._effects = effects | ||
| 27 | self._intensities = intensities or {effect: 1 for effect in effects} | 32 | self._intensities = intensities or {effect: 1 for effect in effects} | ||
| 28 | self._depleted = set() | 33 | self._depleted = set() | ||
| 29 | self._duration = duration | 34 | self._duration = duration | ||
| n | n | 35 | self._is_merged = False | ||
| 30 | self._is_depleted = False | 36 | self._is_depleted = False | ||
| 31 | 37 | ||||
| 32 | @property | 38 | @property | ||
| 33 | def duration(self): | 39 | def duration(self): | ||
| 34 | return self._duration | 40 | return self._duration | ||
| 35 | 41 | ||||
| 36 | @property | 42 | @property | ||
| n | n | 43 | def is_merged(self): | ||
| 44 | return self._is_merged | ||||
| 45 | |||||
| 46 | @property | ||||
| 37 | def effects(self): | 47 | def effects(self): | ||
| 38 | return list(self._effects.keys()) | 48 | return list(self._effects.keys()) | ||
| 39 | 49 | ||||
| 40 | @property | 50 | @property | ||
| 41 | def depleted(self): | 51 | def depleted(self): | ||
| 42 | return self._depleted | 52 | return self._depleted | ||
| 43 | 53 | ||||
| 44 | def __getitem__(self, item): | 54 | def __getitem__(self, item): | ||
| 45 | return self.__getattr__(item) | 55 | return self.__getattr__(item) | ||
| 46 | 56 | ||||
| n | 47 | def deplete(self): | n | 57 | def set_is_depleted(self): |
| 48 | self._is_depleted = True | 58 | self._is_depleted = True | ||
| 49 | 59 | ||||
| n | 50 | def __repr__(self): | n | 60 | def set_is_merged(self, value): |
| 51 | return f'Potion({self._effects}, {self._duration})' | 61 | self._is_merged = value | ||
| 52 | 62 | ||||
| 53 | @is_merged_decorator | 63 | @is_merged_decorator | ||
| 54 | def __getattr__(self, effect_name): | 64 | def __getattr__(self, effect_name): | ||
| 55 | if effect_name not in self._effects: | 65 | if effect_name not in self._effects: | ||
| 56 | raise AttributeError | 66 | raise AttributeError | ||
| 57 | 67 | ||||
| 58 | if effect_name in self._depleted: | 68 | if effect_name in self._depleted: | ||
| 59 | raise TypeError('Effect is depleted') | 69 | raise TypeError('Effect is depleted') | ||
| 60 | 70 | ||||
| 61 | if self._is_depleted: | 71 | if self._is_depleted: | ||
| 62 | raise TypeError('Potion is depleted.') | 72 | raise TypeError('Potion is depleted.') | ||
| 63 | 73 | ||||
| 64 | self._depleted.add(effect_name) | 74 | self._depleted.add(effect_name) | ||
| 65 | return times_decorator(self._effects[effect_name], self._intensities[effect_name]) | 75 | return times_decorator(self._effects[effect_name], self._intensities[effect_name]) | ||
| 66 | 76 | ||||
| 67 | @is_merged_decorator | 77 | @is_merged_decorator | ||
| 68 | def __add__(self, other): | 78 | def __add__(self, other): | ||
| 69 | if not isinstance(other, Potion): | 79 | if not isinstance(other, Potion): | ||
| 70 | raise TypeError('Potions can only be combined with other potions') | 80 | raise TypeError('Potions can only be combined with other potions') | ||
| 71 | 81 | ||||
| 72 | effects = {**self._effects, **other._effects} | 82 | effects = {**self._effects, **other._effects} | ||
| 73 | duration = max(self._duration, other._duration) | 83 | duration = max(self._duration, other._duration) | ||
| 74 | intensities = { | 84 | intensities = { | ||
| 75 | effect: (self._intensities.get(effect, 0)) + (other._intensities.get(effect, 0)) | 85 | effect: (self._intensities.get(effect, 0)) + (other._intensities.get(effect, 0)) | ||
| 76 | for effect in effects | 86 | for effect in effects | ||
| 77 | } | 87 | } | ||
| 78 | return Potion(effects, duration, intensities) | 88 | return Potion(effects, duration, intensities) | ||
| 79 | 89 | ||||
| 80 | @is_merged_decorator | 90 | @is_merged_decorator | ||
| 81 | def __sub__(self, other): | 91 | def __sub__(self, other): | ||
| 82 | if not isinstance(other, Potion): | 92 | if not isinstance(other, Potion): | ||
| 83 | raise TypeError('Potions can only be combined with other potions') | 93 | raise TypeError('Potions can only be combined with other potions') | ||
| 84 | 94 | ||||
| 85 | if other._effects.keys() - self._effects.keys() != set(): | 95 | if other._effects.keys() - self._effects.keys() != set(): | ||
| 86 | raise TypeError('All effects of the right potion must be present in the left potion') | 96 | raise TypeError('All effects of the right potion must be present in the left potion') | ||
| 87 | 97 | ||||
| 88 | duration = self._duration | 98 | duration = self._duration | ||
| 89 | effects = {} | 99 | effects = {} | ||
| 90 | intensities = {} | 100 | intensities = {} | ||
| 91 | for effect in self._effects: | 101 | for effect in self._effects: | ||
| 92 | if effect in other._effects: | 102 | if effect in other._effects: | ||
| 93 | new_effect_intensity = self._intensities.get(effect) - other._intensities.get(effect) | 103 | new_effect_intensity = self._intensities.get(effect) - other._intensities.get(effect) | ||
| 94 | if new_effect_intensity > 0: | 104 | if new_effect_intensity > 0: | ||
| 95 | effects[effect] = self._effects[effect] | 105 | effects[effect] = self._effects[effect] | ||
| 96 | intensities[effect] = new_effect_intensity | 106 | intensities[effect] = new_effect_intensity | ||
| 97 | else: | 107 | else: | ||
| 98 | effects[effect] = self._effects[effect] | 108 | effects[effect] = self._effects[effect] | ||
| 99 | intensities[effect] = self._intensities[effect] | 109 | intensities[effect] = self._intensities[effect] | ||
| 100 | 110 | ||||
| 101 | return Potion(effects, duration, intensities) | 111 | return Potion(effects, duration, intensities) | ||
| 102 | 112 | ||||
| 103 | @is_merged_decorator | 113 | @is_merged_decorator | ||
| 104 | def __mul__(self, intensity_growth): | 114 | def __mul__(self, intensity_growth): | ||
| 105 | try: | 115 | try: | ||
| 106 | float(intensity_growth) | 116 | float(intensity_growth) | ||
| 107 | except ValueError: | 117 | except ValueError: | ||
| 108 | raise TypeError('Potions can only be multiplied by integers') | 118 | raise TypeError('Potions can only be multiplied by integers') | ||
| 109 | 119 | ||||
| 110 | effects = {**self._effects} | 120 | effects = {**self._effects} | ||
| 111 | duration = self._duration | 121 | duration = self._duration | ||
| 112 | intensities = { | 122 | intensities = { | ||
| 113 | effect: round(self._intensities.get(effect, 0) * intensity_growth) | 123 | effect: round(self._intensities.get(effect, 0) * intensity_growth) | ||
| 114 | for effect in effects | 124 | for effect in effects | ||
| 115 | } | 125 | } | ||
| 116 | return Potion(effects, duration, intensities) | 126 | return Potion(effects, duration, intensities) | ||
| 117 | 127 | ||||
| 118 | @is_merged_decorator | 128 | @is_merged_decorator | ||
| 119 | def __truediv__(self, new_potions_count): | 129 | def __truediv__(self, new_potions_count): | ||
| 120 | try: | 130 | try: | ||
| 121 | int(new_potions_count) | 131 | int(new_potions_count) | ||
| 122 | except ValueError: | 132 | except ValueError: | ||
| 123 | raise TypeError('Potions can only be divided by integers') | 133 | raise TypeError('Potions can only be divided by integers') | ||
| 124 | 134 | ||||
| 125 | return tuple([Potion({**self._effects}, | 135 | return tuple([Potion({**self._effects}, | ||
| 126 | self._duration, | 136 | self._duration, | ||
| 127 | {effect: round(self._intensities.get(effect, 0) / new_potions_count) | 137 | {effect: round(self._intensities.get(effect, 0) / new_potions_count) | ||
| 128 | for effect in self._intensities} | 138 | for effect in self._intensities} | ||
| 129 | ) for _ in range(new_potions_count)]) | 139 | ) for _ in range(new_potions_count)]) | ||
| 130 | 140 | ||||
| 131 | def __eq__(self, other): | 141 | def __eq__(self, other): | ||
| 132 | if not isinstance(other, Potion): | 142 | if not isinstance(other, Potion): | ||
| 133 | return False | 143 | return False | ||
| 134 | 144 | ||||
| 135 | return self._effects.keys() - other._effects.keys() == set() and all( | 145 | return self._effects.keys() - other._effects.keys() == set() and all( | ||
| 136 | [self._intensities[effect] == other._intensities[effect] for effect in self._effects.keys()] | 146 | [self._intensities[effect] == other._intensities[effect] for effect in self._effects.keys()] | ||
| 137 | ) | 147 | ) | ||
| 138 | 148 | ||||
| 139 | def __lt__(self, other): | 149 | def __lt__(self, other): | ||
| 140 | if not isinstance(other, Potion): | 150 | if not isinstance(other, Potion): | ||
| 141 | return False | 151 | return False | ||
| 142 | 152 | ||||
| 143 | return sum(self._intensities.values()) < sum(other._intensities.values()) | 153 | return sum(self._intensities.values()) < sum(other._intensities.values()) | ||
| 144 | 154 | ||||
| 145 | def __gt__(self, other): | 155 | def __gt__(self, other): | ||
| 146 | if not isinstance(other, Potion): | 156 | if not isinstance(other, Potion): | ||
| 147 | return False | 157 | return False | ||
| 148 | 158 | ||||
| 149 | return not self == other and not self < other | 159 | return not self == other and not self < other | ||
| 150 | 160 | ||||
| 151 | 161 | ||||
| 152 | class ГоспожатаПоХимия: | 162 | class ГоспожатаПоХимия: | ||
| 153 | def __init__(self): | 163 | def __init__(self): | ||
| 154 | self._target = None | 164 | self._target = None | ||
| 155 | self._target_public_attributes = None | 165 | self._target_public_attributes = None | ||
| 156 | self._current_tick_duration = None | 166 | self._current_tick_duration = None | ||
| 157 | 167 | ||||
| 158 | def _sort_effects_by_molecule_mass(self, effects): | 168 | def _sort_effects_by_molecule_mass(self, effects): | ||
| 159 | new_effects = list(effects)[:] | 169 | new_effects = list(effects)[:] | ||
| 160 | return sorted(new_effects, key=lambda effect: sum(ord(ch) for ch in effect), reverse=True) | 170 | return sorted(new_effects, key=lambda effect: sum(ord(ch) for ch in effect), reverse=True) | ||
| 161 | 171 | ||||
| 162 | def _get_public_attributes(self, target): | 172 | def _get_public_attributes(self, target): | ||
| 163 | all_attributes = vars(target) | 173 | all_attributes = vars(target) | ||
| 164 | return {key: value for key, value in all_attributes.items() if | 174 | return {key: value for key, value in all_attributes.items() if | ||
| 165 | not key.startswith('_')} | 175 | not key.startswith('_')} | ||
| 166 | 176 | ||||
| 167 | def apply(self, target, potion): | 177 | def apply(self, target, potion): | ||
| 168 | self._target_public_attributes = self._get_public_attributes(target) | 178 | self._target_public_attributes = self._get_public_attributes(target) | ||
| 169 | self._target = target | 179 | self._target = target | ||
| 170 | self._current_tick_duration = potion.duration | 180 | self._current_tick_duration = potion.duration | ||
| 171 | 181 | ||||
| 172 | for effect in self._sort_effects_by_molecule_mass(potion.effects): | 182 | for effect in self._sort_effects_by_molecule_mass(potion.effects): | ||
| 173 | potion[effect](target) | 183 | potion[effect](target) | ||
| 174 | 184 | ||||
| 175 | if set(potion.depleted) == set(potion.effects): | 185 | if set(potion.depleted) == set(potion.effects): | ||
| t | 176 | potion.deplete() | t | 186 | potion.set_is_depleted() |
| 177 | 187 | ||||
| 178 | def tick(self): | 188 | def tick(self): | ||
| 179 | if not self._current_tick_duration: | 189 | if not self._current_tick_duration: | ||
| 180 | raise Exception('No potion applied') | 190 | raise Exception('No potion applied') | ||
| 181 | 191 | ||||
| 182 | self._current_tick_duration -= 1 | 192 | self._current_tick_duration -= 1 | ||
| 183 | 193 | ||||
| 184 | if self._current_tick_duration != 0: | 194 | if self._current_tick_duration != 0: | ||
| 185 | return | 195 | return | ||
| 186 | 196 | ||||
| 187 | current_public_attributes = self._get_public_attributes(self._target) | 197 | current_public_attributes = self._get_public_attributes(self._target) | ||
| 188 | for key in current_public_attributes: | 198 | for key in current_public_attributes: | ||
| 189 | if key in self._target_public_attributes: | 199 | if key in self._target_public_attributes: | ||
| 190 | setattr(self._target, key, self._target_public_attributes[key]) | 200 | setattr(self._target, key, self._target_public_attributes[key]) | ||
| 191 | else: | 201 | else: | ||
| 192 | del self._target.__dict__[key] | 202 | del self._target.__dict__[key] |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | def times_decorator(func, times): | f | 1 | def times_decorator(func, times): |
| 2 | def wrapper(target): | 2 | def wrapper(target): | ||
| 3 | for _ in range(times): | 3 | for _ in range(times): | ||
| 4 | func(target) | 4 | func(target) | ||
| 5 | 5 | ||||
| 6 | return wrapper | 6 | return wrapper | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| 9 | def is_merged_decorator(func): | 9 | def is_merged_decorator(func): | ||
| 10 | def wrapper(*args, **kwargs): | 10 | def wrapper(*args, **kwargs): | ||
| 11 | self = args[0] | 11 | self = args[0] | ||
| 12 | if self._is_merged: | 12 | if self._is_merged: | ||
| 13 | raise TypeError('Potion is now part of something bigger than itself.') | 13 | raise TypeError('Potion is now part of something bigger than itself.') | ||
| 14 | try: | 14 | try: | ||
| 15 | return func(*args, **kwargs) | 15 | return func(*args, **kwargs) | ||
| 16 | finally: | 16 | finally: | ||
| 17 | if func.__name__ != '__getattr__': | 17 | if func.__name__ != '__getattr__': | ||
| 18 | self._is_merged = True | 18 | self._is_merged = True | ||
| 19 | 19 | ||||
| 20 | return wrapper | 20 | return wrapper | ||
| 21 | 21 | ||||
| 22 | 22 | ||||
| 23 | class Potion: | 23 | class Potion: | ||
| 24 | def __init__(self, effects, duration, intensities=None): | 24 | def __init__(self, effects, duration, intensities=None): | ||
| 25 | self._is_merged = False | 25 | self._is_merged = False | ||
| 26 | self._effects = effects | 26 | self._effects = effects | ||
| 27 | self._intensities = intensities or {effect: 1 for effect in effects} | 27 | self._intensities = intensities or {effect: 1 for effect in effects} | ||
| 28 | self._depleted = set() | 28 | self._depleted = set() | ||
| 29 | self._duration = duration | 29 | self._duration = duration | ||
| n | 30 | self._depleted_message = 'Effect is depleted' | n | ||
| 31 | self._is_depleted = False | 30 | self._is_depleted = False | ||
| 32 | 31 | ||||
| 33 | @property | 32 | @property | ||
| 34 | def duration(self): | 33 | def duration(self): | ||
| 35 | return self._duration | 34 | return self._duration | ||
| 36 | 35 | ||||
| 37 | @property | 36 | @property | ||
| 38 | def effects(self): | 37 | def effects(self): | ||
| 39 | return list(self._effects.keys()) | 38 | return list(self._effects.keys()) | ||
| 40 | 39 | ||||
| 41 | @property | 40 | @property | ||
| 42 | def depleted(self): | 41 | def depleted(self): | ||
| 43 | return self._depleted | 42 | return self._depleted | ||
| 44 | 43 | ||||
| 45 | def __getitem__(self, item): | 44 | def __getitem__(self, item): | ||
| 46 | return self.__getattr__(item) | 45 | return self.__getattr__(item) | ||
| 47 | 46 | ||||
| n | 48 | def deplete(self, depleted_message): | n | 47 | def deplete(self): |
| 49 | self._depleted_message = depleted_message | ||||
| 50 | self._is_depleted = True | 48 | self._is_depleted = True | ||
| 51 | 49 | ||||
| 52 | def __repr__(self): | 50 | def __repr__(self): | ||
| 53 | return f'Potion({self._effects}, {self._duration})' | 51 | return f'Potion({self._effects}, {self._duration})' | ||
| 54 | 52 | ||||
| 55 | @is_merged_decorator | 53 | @is_merged_decorator | ||
| 56 | def __getattr__(self, effect_name): | 54 | def __getattr__(self, effect_name): | ||
| 57 | if effect_name not in self._effects: | 55 | if effect_name not in self._effects: | ||
| 58 | raise AttributeError | 56 | raise AttributeError | ||
| 59 | 57 | ||||
| n | 60 | if effect_name in self._depleted or self._is_depleted: | n | 58 | if effect_name in self._depleted: |
| 61 | raise TypeError(self._depleted_message) | 59 | raise TypeError('Effect is depleted') | ||
| 60 | |||||
| 61 | if self._is_depleted: | ||||
| 62 | raise TypeError('Potion is depleted.') | ||||
| 62 | 63 | ||||
| 63 | self._depleted.add(effect_name) | 64 | self._depleted.add(effect_name) | ||
| 64 | return times_decorator(self._effects[effect_name], self._intensities[effect_name]) | 65 | return times_decorator(self._effects[effect_name], self._intensities[effect_name]) | ||
| 65 | 66 | ||||
| 66 | @is_merged_decorator | 67 | @is_merged_decorator | ||
| 67 | def __add__(self, other): | 68 | def __add__(self, other): | ||
| 68 | if not isinstance(other, Potion): | 69 | if not isinstance(other, Potion): | ||
| 69 | raise TypeError('Potions can only be combined with other potions') | 70 | raise TypeError('Potions can only be combined with other potions') | ||
| 70 | 71 | ||||
| 71 | effects = {**self._effects, **other._effects} | 72 | effects = {**self._effects, **other._effects} | ||
| 72 | duration = max(self._duration, other._duration) | 73 | duration = max(self._duration, other._duration) | ||
| 73 | intensities = { | 74 | intensities = { | ||
| 74 | effect: (self._intensities.get(effect, 0)) + (other._intensities.get(effect, 0)) | 75 | effect: (self._intensities.get(effect, 0)) + (other._intensities.get(effect, 0)) | ||
| 75 | for effect in effects | 76 | for effect in effects | ||
| 76 | } | 77 | } | ||
| 77 | return Potion(effects, duration, intensities) | 78 | return Potion(effects, duration, intensities) | ||
| 78 | 79 | ||||
| 79 | @is_merged_decorator | 80 | @is_merged_decorator | ||
| 80 | def __sub__(self, other): | 81 | def __sub__(self, other): | ||
| 81 | if not isinstance(other, Potion): | 82 | if not isinstance(other, Potion): | ||
| 82 | raise TypeError('Potions can only be combined with other potions') | 83 | raise TypeError('Potions can only be combined with other potions') | ||
| 83 | 84 | ||||
| 84 | if other._effects.keys() - self._effects.keys() != set(): | 85 | if other._effects.keys() - self._effects.keys() != set(): | ||
| 85 | raise TypeError('All effects of the right potion must be present in the left potion') | 86 | raise TypeError('All effects of the right potion must be present in the left potion') | ||
| 86 | 87 | ||||
| 87 | duration = self._duration | 88 | duration = self._duration | ||
| 88 | effects = {} | 89 | effects = {} | ||
| 89 | intensities = {} | 90 | intensities = {} | ||
| 90 | for effect in self._effects: | 91 | for effect in self._effects: | ||
| 91 | if effect in other._effects: | 92 | if effect in other._effects: | ||
| 92 | new_effect_intensity = self._intensities.get(effect) - other._intensities.get(effect) | 93 | new_effect_intensity = self._intensities.get(effect) - other._intensities.get(effect) | ||
| 93 | if new_effect_intensity > 0: | 94 | if new_effect_intensity > 0: | ||
| 94 | effects[effect] = self._effects[effect] | 95 | effects[effect] = self._effects[effect] | ||
| 95 | intensities[effect] = new_effect_intensity | 96 | intensities[effect] = new_effect_intensity | ||
| 96 | else: | 97 | else: | ||
| 97 | effects[effect] = self._effects[effect] | 98 | effects[effect] = self._effects[effect] | ||
| 98 | intensities[effect] = self._intensities[effect] | 99 | intensities[effect] = self._intensities[effect] | ||
| 99 | 100 | ||||
| 100 | return Potion(effects, duration, intensities) | 101 | return Potion(effects, duration, intensities) | ||
| 101 | 102 | ||||
| 102 | @is_merged_decorator | 103 | @is_merged_decorator | ||
| 103 | def __mul__(self, intensity_growth): | 104 | def __mul__(self, intensity_growth): | ||
| 104 | try: | 105 | try: | ||
| 105 | float(intensity_growth) | 106 | float(intensity_growth) | ||
| 106 | except ValueError: | 107 | except ValueError: | ||
| 107 | raise TypeError('Potions can only be multiplied by integers') | 108 | raise TypeError('Potions can only be multiplied by integers') | ||
| 108 | 109 | ||||
| 109 | effects = {**self._effects} | 110 | effects = {**self._effects} | ||
| 110 | duration = self._duration | 111 | duration = self._duration | ||
| 111 | intensities = { | 112 | intensities = { | ||
| 112 | effect: round(self._intensities.get(effect, 0) * intensity_growth) | 113 | effect: round(self._intensities.get(effect, 0) * intensity_growth) | ||
| 113 | for effect in effects | 114 | for effect in effects | ||
| 114 | } | 115 | } | ||
| 115 | return Potion(effects, duration, intensities) | 116 | return Potion(effects, duration, intensities) | ||
| 116 | 117 | ||||
| 117 | @is_merged_decorator | 118 | @is_merged_decorator | ||
| 118 | def __truediv__(self, new_potions_count): | 119 | def __truediv__(self, new_potions_count): | ||
| 119 | try: | 120 | try: | ||
| 120 | int(new_potions_count) | 121 | int(new_potions_count) | ||
| 121 | except ValueError: | 122 | except ValueError: | ||
| 122 | raise TypeError('Potions can only be divided by integers') | 123 | raise TypeError('Potions can only be divided by integers') | ||
| 123 | 124 | ||||
| 124 | return tuple([Potion({**self._effects}, | 125 | return tuple([Potion({**self._effects}, | ||
| 125 | self._duration, | 126 | self._duration, | ||
| 126 | {effect: round(self._intensities.get(effect, 0) / new_potions_count) | 127 | {effect: round(self._intensities.get(effect, 0) / new_potions_count) | ||
| 127 | for effect in self._intensities} | 128 | for effect in self._intensities} | ||
| 128 | ) for _ in range(new_potions_count)]) | 129 | ) for _ in range(new_potions_count)]) | ||
| 129 | 130 | ||||
| 130 | def __eq__(self, other): | 131 | def __eq__(self, other): | ||
| 131 | if not isinstance(other, Potion): | 132 | if not isinstance(other, Potion): | ||
| 132 | return False | 133 | return False | ||
| 133 | 134 | ||||
| 134 | return self._effects.keys() - other._effects.keys() == set() and all( | 135 | return self._effects.keys() - other._effects.keys() == set() and all( | ||
| 135 | [self._intensities[effect] == other._intensities[effect] for effect in self._effects.keys()] | 136 | [self._intensities[effect] == other._intensities[effect] for effect in self._effects.keys()] | ||
| 136 | ) | 137 | ) | ||
| 137 | 138 | ||||
| 138 | def __lt__(self, other): | 139 | def __lt__(self, other): | ||
| 139 | if not isinstance(other, Potion): | 140 | if not isinstance(other, Potion): | ||
| 140 | return False | 141 | return False | ||
| 141 | 142 | ||||
| 142 | return sum(self._intensities.values()) < sum(other._intensities.values()) | 143 | return sum(self._intensities.values()) < sum(other._intensities.values()) | ||
| 143 | 144 | ||||
| 144 | def __gt__(self, other): | 145 | def __gt__(self, other): | ||
| 145 | if not isinstance(other, Potion): | 146 | if not isinstance(other, Potion): | ||
| 146 | return False | 147 | return False | ||
| 147 | 148 | ||||
| 148 | return not self == other and not self < other | 149 | return not self == other and not self < other | ||
| 149 | 150 | ||||
| 150 | 151 | ||||
| 151 | class ГоспожатаПоХимия: | 152 | class ГоспожатаПоХимия: | ||
| 152 | def __init__(self): | 153 | def __init__(self): | ||
| 153 | self._target = None | 154 | self._target = None | ||
| 154 | self._target_public_attributes = None | 155 | self._target_public_attributes = None | ||
| 155 | self._current_tick_duration = None | 156 | self._current_tick_duration = None | ||
| 156 | 157 | ||||
| 157 | def _sort_effects_by_molecule_mass(self, effects): | 158 | def _sort_effects_by_molecule_mass(self, effects): | ||
| 158 | new_effects = list(effects)[:] | 159 | new_effects = list(effects)[:] | ||
| 159 | return sorted(new_effects, key=lambda effect: sum(ord(ch) for ch in effect), reverse=True) | 160 | return sorted(new_effects, key=lambda effect: sum(ord(ch) for ch in effect), reverse=True) | ||
| 160 | 161 | ||||
| 161 | def _get_public_attributes(self, target): | 162 | def _get_public_attributes(self, target): | ||
| 162 | all_attributes = vars(target) | 163 | all_attributes = vars(target) | ||
| 163 | return {key: value for key, value in all_attributes.items() if | 164 | return {key: value for key, value in all_attributes.items() if | ||
| 164 | not key.startswith('_')} | 165 | not key.startswith('_')} | ||
| 165 | 166 | ||||
| 166 | def apply(self, target, potion): | 167 | def apply(self, target, potion): | ||
| 167 | self._target_public_attributes = self._get_public_attributes(target) | 168 | self._target_public_attributes = self._get_public_attributes(target) | ||
| 168 | self._target = target | 169 | self._target = target | ||
| 169 | self._current_tick_duration = potion.duration | 170 | self._current_tick_duration = potion.duration | ||
| 170 | 171 | ||||
| 171 | for effect in self._sort_effects_by_molecule_mass(potion.effects): | 172 | for effect in self._sort_effects_by_molecule_mass(potion.effects): | ||
| 172 | potion[effect](target) | 173 | potion[effect](target) | ||
| 173 | 174 | ||||
| 174 | if set(potion.depleted) == set(potion.effects): | 175 | if set(potion.depleted) == set(potion.effects): | ||
| n | 175 | potion.deplete('Potion is depleted.') | n | 176 | potion.deplete() |
| 176 | 177 | ||||
| 177 | def tick(self): | 178 | def tick(self): | ||
| 178 | if not self._current_tick_duration: | 179 | if not self._current_tick_duration: | ||
| 179 | raise Exception('No potion applied') | 180 | raise Exception('No potion applied') | ||
| 180 | 181 | ||||
| 181 | self._current_tick_duration -= 1 | 182 | self._current_tick_duration -= 1 | ||
| 182 | 183 | ||||
| 183 | if self._current_tick_duration != 0: | 184 | if self._current_tick_duration != 0: | ||
| 184 | return | 185 | return | ||
| 185 | 186 | ||||
| 186 | current_public_attributes = self._get_public_attributes(self._target) | 187 | current_public_attributes = self._get_public_attributes(self._target) | ||
| 187 | for key in current_public_attributes: | 188 | for key in current_public_attributes: | ||
| 188 | if key in self._target_public_attributes: | 189 | if key in self._target_public_attributes: | ||
| 189 | setattr(self._target, key, self._target_public_attributes[key]) | 190 | setattr(self._target, key, self._target_public_attributes[key]) | ||
| 190 | else: | 191 | else: | ||
| 191 | del self._target.__dict__[key] | 192 | del self._target.__dict__[key] | ||
| t | 192 | t | |||
| 193 |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | def times_decorator(func, times): | f | 1 | def times_decorator(func, times): |
| 2 | def wrapper(target): | 2 | def wrapper(target): | ||
| 3 | for _ in range(times): | 3 | for _ in range(times): | ||
| 4 | func(target) | 4 | func(target) | ||
| 5 | 5 | ||||
| 6 | return wrapper | 6 | return wrapper | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| 9 | def is_merged_decorator(func): | 9 | def is_merged_decorator(func): | ||
| 10 | def wrapper(*args, **kwargs): | 10 | def wrapper(*args, **kwargs): | ||
| 11 | self = args[0] | 11 | self = args[0] | ||
| 12 | if self._is_merged: | 12 | if self._is_merged: | ||
| 13 | raise TypeError('Potion is now part of something bigger than itself.') | 13 | raise TypeError('Potion is now part of something bigger than itself.') | ||
| 14 | try: | 14 | try: | ||
| 15 | return func(*args, **kwargs) | 15 | return func(*args, **kwargs) | ||
| 16 | finally: | 16 | finally: | ||
| 17 | if func.__name__ != '__getattr__': | 17 | if func.__name__ != '__getattr__': | ||
| 18 | self._is_merged = True | 18 | self._is_merged = True | ||
| 19 | 19 | ||||
| 20 | return wrapper | 20 | return wrapper | ||
| 21 | 21 | ||||
| 22 | 22 | ||||
| 23 | class Potion: | 23 | class Potion: | ||
| 24 | def __init__(self, effects, duration, intensities=None): | 24 | def __init__(self, effects, duration, intensities=None): | ||
| 25 | self._is_merged = False | 25 | self._is_merged = False | ||
| 26 | self._effects = effects | 26 | self._effects = effects | ||
| 27 | self._intensities = intensities or {effect: 1 for effect in effects} | 27 | self._intensities = intensities or {effect: 1 for effect in effects} | ||
| 28 | self._depleted = set() | 28 | self._depleted = set() | ||
| 29 | self._duration = duration | 29 | self._duration = duration | ||
| 30 | self._depleted_message = 'Effect is depleted' | 30 | self._depleted_message = 'Effect is depleted' | ||
| 31 | self._is_depleted = False | 31 | self._is_depleted = False | ||
| 32 | 32 | ||||
| 33 | @property | 33 | @property | ||
| 34 | def duration(self): | 34 | def duration(self): | ||
| 35 | return self._duration | 35 | return self._duration | ||
| 36 | 36 | ||||
| 37 | @property | 37 | @property | ||
| 38 | def effects(self): | 38 | def effects(self): | ||
| 39 | return list(self._effects.keys()) | 39 | return list(self._effects.keys()) | ||
| 40 | 40 | ||||
| 41 | @property | 41 | @property | ||
| 42 | def depleted(self): | 42 | def depleted(self): | ||
| 43 | return self._depleted | 43 | return self._depleted | ||
| 44 | 44 | ||||
| 45 | def __getitem__(self, item): | 45 | def __getitem__(self, item): | ||
| 46 | return self.__getattr__(item) | 46 | return self.__getattr__(item) | ||
| 47 | 47 | ||||
| 48 | def deplete(self, depleted_message): | 48 | def deplete(self, depleted_message): | ||
| 49 | self._depleted_message = depleted_message | 49 | self._depleted_message = depleted_message | ||
| 50 | self._is_depleted = True | 50 | self._is_depleted = True | ||
| 51 | 51 | ||||
| 52 | def __repr__(self): | 52 | def __repr__(self): | ||
| 53 | return f'Potion({self._effects}, {self._duration})' | 53 | return f'Potion({self._effects}, {self._duration})' | ||
| 54 | 54 | ||||
| 55 | @is_merged_decorator | 55 | @is_merged_decorator | ||
| 56 | def __getattr__(self, effect_name): | 56 | def __getattr__(self, effect_name): | ||
| 57 | if effect_name not in self._effects: | 57 | if effect_name not in self._effects: | ||
| 58 | raise AttributeError | 58 | raise AttributeError | ||
| 59 | 59 | ||||
| 60 | if effect_name in self._depleted or self._is_depleted: | 60 | if effect_name in self._depleted or self._is_depleted: | ||
| 61 | raise TypeError(self._depleted_message) | 61 | raise TypeError(self._depleted_message) | ||
| 62 | 62 | ||||
| 63 | self._depleted.add(effect_name) | 63 | self._depleted.add(effect_name) | ||
| 64 | return times_decorator(self._effects[effect_name], self._intensities[effect_name]) | 64 | return times_decorator(self._effects[effect_name], self._intensities[effect_name]) | ||
| 65 | 65 | ||||
| 66 | @is_merged_decorator | 66 | @is_merged_decorator | ||
| 67 | def __add__(self, other): | 67 | def __add__(self, other): | ||
| 68 | if not isinstance(other, Potion): | 68 | if not isinstance(other, Potion): | ||
| 69 | raise TypeError('Potions can only be combined with other potions') | 69 | raise TypeError('Potions can only be combined with other potions') | ||
| 70 | 70 | ||||
| 71 | effects = {**self._effects, **other._effects} | 71 | effects = {**self._effects, **other._effects} | ||
| 72 | duration = max(self._duration, other._duration) | 72 | duration = max(self._duration, other._duration) | ||
| 73 | intensities = { | 73 | intensities = { | ||
| 74 | effect: (self._intensities.get(effect, 0)) + (other._intensities.get(effect, 0)) | 74 | effect: (self._intensities.get(effect, 0)) + (other._intensities.get(effect, 0)) | ||
| 75 | for effect in effects | 75 | for effect in effects | ||
| 76 | } | 76 | } | ||
| 77 | return Potion(effects, duration, intensities) | 77 | return Potion(effects, duration, intensities) | ||
| 78 | 78 | ||||
| 79 | @is_merged_decorator | 79 | @is_merged_decorator | ||
| 80 | def __sub__(self, other): | 80 | def __sub__(self, other): | ||
| 81 | if not isinstance(other, Potion): | 81 | if not isinstance(other, Potion): | ||
| 82 | raise TypeError('Potions can only be combined with other potions') | 82 | raise TypeError('Potions can only be combined with other potions') | ||
| 83 | 83 | ||||
| t | 84 | if len(other._effects.keys() - self._effects.keys()): | t | 84 | if other._effects.keys() - self._effects.keys() != set(): |
| 85 | raise TypeError('All effects of the right potion must be present in the left potion') | 85 | raise TypeError('All effects of the right potion must be present in the left potion') | ||
| 86 | 86 | ||||
| 87 | duration = self._duration | 87 | duration = self._duration | ||
| 88 | effects = {} | 88 | effects = {} | ||
| 89 | intensities = {} | 89 | intensities = {} | ||
| 90 | for effect in self._effects: | 90 | for effect in self._effects: | ||
| 91 | if effect in other._effects: | 91 | if effect in other._effects: | ||
| 92 | new_effect_intensity = self._intensities.get(effect) - other._intensities.get(effect) | 92 | new_effect_intensity = self._intensities.get(effect) - other._intensities.get(effect) | ||
| 93 | if new_effect_intensity > 0: | 93 | if new_effect_intensity > 0: | ||
| 94 | effects[effect] = self._effects[effect] | 94 | effects[effect] = self._effects[effect] | ||
| 95 | intensities[effect] = new_effect_intensity | 95 | intensities[effect] = new_effect_intensity | ||
| 96 | else: | 96 | else: | ||
| 97 | effects[effect] = self._effects[effect] | 97 | effects[effect] = self._effects[effect] | ||
| 98 | intensities[effect] = self._intensities[effect] | 98 | intensities[effect] = self._intensities[effect] | ||
| 99 | 99 | ||||
| 100 | return Potion(effects, duration, intensities) | 100 | return Potion(effects, duration, intensities) | ||
| 101 | 101 | ||||
| 102 | @is_merged_decorator | 102 | @is_merged_decorator | ||
| 103 | def __mul__(self, intensity_growth): | 103 | def __mul__(self, intensity_growth): | ||
| 104 | try: | 104 | try: | ||
| 105 | float(intensity_growth) | 105 | float(intensity_growth) | ||
| 106 | except ValueError: | 106 | except ValueError: | ||
| 107 | raise TypeError('Potions can only be multiplied by integers') | 107 | raise TypeError('Potions can only be multiplied by integers') | ||
| 108 | 108 | ||||
| 109 | effects = {**self._effects} | 109 | effects = {**self._effects} | ||
| 110 | duration = self._duration | 110 | duration = self._duration | ||
| 111 | intensities = { | 111 | intensities = { | ||
| 112 | effect: round(self._intensities.get(effect, 0) * intensity_growth) | 112 | effect: round(self._intensities.get(effect, 0) * intensity_growth) | ||
| 113 | for effect in effects | 113 | for effect in effects | ||
| 114 | } | 114 | } | ||
| 115 | return Potion(effects, duration, intensities) | 115 | return Potion(effects, duration, intensities) | ||
| 116 | 116 | ||||
| 117 | @is_merged_decorator | 117 | @is_merged_decorator | ||
| 118 | def __truediv__(self, new_potions_count): | 118 | def __truediv__(self, new_potions_count): | ||
| 119 | try: | 119 | try: | ||
| 120 | int(new_potions_count) | 120 | int(new_potions_count) | ||
| 121 | except ValueError: | 121 | except ValueError: | ||
| 122 | raise TypeError('Potions can only be divided by integers') | 122 | raise TypeError('Potions can only be divided by integers') | ||
| 123 | 123 | ||||
| 124 | return tuple([Potion({**self._effects}, | 124 | return tuple([Potion({**self._effects}, | ||
| 125 | self._duration, | 125 | self._duration, | ||
| 126 | {effect: round(self._intensities.get(effect, 0) / new_potions_count) | 126 | {effect: round(self._intensities.get(effect, 0) / new_potions_count) | ||
| 127 | for effect in self._intensities} | 127 | for effect in self._intensities} | ||
| 128 | ) for _ in range(new_potions_count)]) | 128 | ) for _ in range(new_potions_count)]) | ||
| 129 | 129 | ||||
| 130 | def __eq__(self, other): | 130 | def __eq__(self, other): | ||
| 131 | if not isinstance(other, Potion): | 131 | if not isinstance(other, Potion): | ||
| 132 | return False | 132 | return False | ||
| 133 | 133 | ||||
| 134 | return self._effects.keys() - other._effects.keys() == set() and all( | 134 | return self._effects.keys() - other._effects.keys() == set() and all( | ||
| 135 | [self._intensities[effect] == other._intensities[effect] for effect in self._effects.keys()] | 135 | [self._intensities[effect] == other._intensities[effect] for effect in self._effects.keys()] | ||
| 136 | ) | 136 | ) | ||
| 137 | 137 | ||||
| 138 | def __lt__(self, other): | 138 | def __lt__(self, other): | ||
| 139 | if not isinstance(other, Potion): | 139 | if not isinstance(other, Potion): | ||
| 140 | return False | 140 | return False | ||
| 141 | 141 | ||||
| 142 | return sum(self._intensities.values()) < sum(other._intensities.values()) | 142 | return sum(self._intensities.values()) < sum(other._intensities.values()) | ||
| 143 | 143 | ||||
| 144 | def __gt__(self, other): | 144 | def __gt__(self, other): | ||
| 145 | if not isinstance(other, Potion): | 145 | if not isinstance(other, Potion): | ||
| 146 | return False | 146 | return False | ||
| 147 | 147 | ||||
| 148 | return not self == other and not self < other | 148 | return not self == other and not self < other | ||
| 149 | 149 | ||||
| 150 | 150 | ||||
| 151 | class ГоспожатаПоХимия: | 151 | class ГоспожатаПоХимия: | ||
| 152 | def __init__(self): | 152 | def __init__(self): | ||
| 153 | self._target = None | 153 | self._target = None | ||
| 154 | self._target_public_attributes = None | 154 | self._target_public_attributes = None | ||
| 155 | self._current_tick_duration = None | 155 | self._current_tick_duration = None | ||
| 156 | 156 | ||||
| 157 | def _sort_effects_by_molecule_mass(self, effects): | 157 | def _sort_effects_by_molecule_mass(self, effects): | ||
| 158 | new_effects = list(effects)[:] | 158 | new_effects = list(effects)[:] | ||
| 159 | return sorted(new_effects, key=lambda effect: sum(ord(ch) for ch in effect), reverse=True) | 159 | return sorted(new_effects, key=lambda effect: sum(ord(ch) for ch in effect), reverse=True) | ||
| 160 | 160 | ||||
| 161 | def _get_public_attributes(self, target): | 161 | def _get_public_attributes(self, target): | ||
| 162 | all_attributes = vars(target) | 162 | all_attributes = vars(target) | ||
| 163 | return {key: value for key, value in all_attributes.items() if | 163 | return {key: value for key, value in all_attributes.items() if | ||
| 164 | not key.startswith('_')} | 164 | not key.startswith('_')} | ||
| 165 | 165 | ||||
| 166 | def apply(self, target, potion): | 166 | def apply(self, target, potion): | ||
| 167 | self._target_public_attributes = self._get_public_attributes(target) | 167 | self._target_public_attributes = self._get_public_attributes(target) | ||
| 168 | self._target = target | 168 | self._target = target | ||
| 169 | self._current_tick_duration = potion.duration | 169 | self._current_tick_duration = potion.duration | ||
| 170 | 170 | ||||
| 171 | for effect in self._sort_effects_by_molecule_mass(potion.effects): | 171 | for effect in self._sort_effects_by_molecule_mass(potion.effects): | ||
| 172 | potion[effect](target) | 172 | potion[effect](target) | ||
| 173 | 173 | ||||
| 174 | if set(potion.depleted) == set(potion.effects): | 174 | if set(potion.depleted) == set(potion.effects): | ||
| 175 | potion.deplete('Potion is depleted.') | 175 | potion.deplete('Potion is depleted.') | ||
| 176 | 176 | ||||
| 177 | def tick(self): | 177 | def tick(self): | ||
| 178 | if not self._current_tick_duration: | 178 | if not self._current_tick_duration: | ||
| 179 | raise Exception('No potion applied') | 179 | raise Exception('No potion applied') | ||
| 180 | 180 | ||||
| 181 | self._current_tick_duration -= 1 | 181 | self._current_tick_duration -= 1 | ||
| 182 | 182 | ||||
| 183 | if self._current_tick_duration != 0: | 183 | if self._current_tick_duration != 0: | ||
| 184 | return | 184 | return | ||
| 185 | 185 | ||||
| 186 | current_public_attributes = self._get_public_attributes(self._target) | 186 | current_public_attributes = self._get_public_attributes(self._target) | ||
| 187 | for key in current_public_attributes: | 187 | for key in current_public_attributes: | ||
| 188 | if key in self._target_public_attributes: | 188 | if key in self._target_public_attributes: | ||
| 189 | setattr(self._target, key, self._target_public_attributes[key]) | 189 | setattr(self._target, key, self._target_public_attributes[key]) | ||
| 190 | else: | 190 | else: | ||
| 191 | del self._target.__dict__[key] | 191 | del self._target.__dict__[key] | ||
| 192 | 192 | ||||
| 193 | 193 |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
03.12.2023 13:35
03.12.2023 13:42