1class Material:
2 density = None
3
4 def __init__(self, mass):
5 self.mass = mass
6 self._used = False
7
8 @property
9 def volume(self):
10 return self.mass / self.density
11
12
13class Concrete(Material):
14 density = 2500
15
16
17class Brick(Material):
18 density = 2000
19
20
21class Stone(Material):
22 density = 1600
23
24
25class Wood(Material):
26 density = 600
27
28
29class Steel(Material):
30 density = 7700
31
32class Factory:
33 all_valid_materials = []
34 dynamic_classes = {}
35
36 def __init__(self):
37 self.materials = {
38 "Concrete": Concrete,
39 "Brick": Brick,
40 "Stone": Stone,
41 "Wood": Wood,
42 "Steel": Steel,
43 }
44 self.valid_materials = []
45 self.invalid_materials = []
46
47 def _mark_as_used(self, material):
48 if material._used:
49 raise AssertionError("Материалът вече е използван в тази сплав.")
50 material._used = True
51
52 def _create_dynamic_class(self, materials):
53 base_classes = sorted([mat.__class__.__name__ for mat in materials])
54 class_name = "_".join(base_classes)
55
56 if class_name not in Factory.dynamic_classes:
57 avg_density = sum(mat.density for mat in materials) / len(materials)
58 new_class = type(
59 class_name,
60 (Material,),
61 {"density": avg_density}
62 )
63 Factory.dynamic_classes[class_name] = new_class
64
65 return Factory.dynamic_classes[class_name]
66
67 def handle_args(self, args):
68 for material in args:
69 self._mark_as_used(material)
70 dynamic_class = self._create_dynamic_class(args)
71 new_instance = dynamic_class(sum([material.mass for material in args]))
72 self.invalid_materials.extend(args)
73 return new_instance
74
75 def handle_kwargs(self, kwargs):
76 instances = []
77 for name, mass in kwargs.items():
78 material_class = self.materials.get(name) or Factory.dynamic_classes.get(name)
79 if material_class is None:
80 raise ValueError("Невалидно име на материал")
81 material_instance = material_class(mass)
82 instances.append(material_instance)
83 self.valid_materials.append(material_instance)
84 return tuple(instances)
85
86 def __call__(self, *args, **kwargs):
87 if args and kwargs:
88 raise ValueError("Фабриката не може да се извика и с двата типа аргументи.")
89 if not args and not kwargs:
90 raise ValueError("Фабриката трябва да се извика или с позиционен, или с именуван параметър.")
91 if args:
92 if isinstance(args[0], Material):
93 return self.handle_args(args)
94 else:
95 raise ValueError("Невалидни аргументи.")
96 if kwargs:
97 return self.handle_kwargs(kwargs)
98
99 def can_build(self, required_volume):
100 total_volume = sum(mat.volume for mat in self.valid_materials if not mat._used)
101 return total_volume >= required_volume
102
103 @classmethod
104 def can_build_together(cls, required_volume):
105 total_volume = sum(mat.volume for factory in Factory.all_valid_materials for mat in factory.valid_materials)
106 return total_volume >= required_volume
.F...FFF..
======================================================================
FAIL: test_can_build (test.TestFactory.test_can_build)
Test can_build methods.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 176, in test_can_build
self.assertTrue(self.factory2.can_build(4.0))
AssertionError: False is not true
======================================================================
FAIL: test_positional_arguments_multiple_argument_from_initial_set (test.TestFactory.test_positional_arguments_multiple_argument_from_initial_set)
Test calling a factory using multiple positional arguments.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 116, in test_positional_arguments_multiple_argument_from_initial_set
concrete_wood = self.factory1(concrete, wood)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 93, in __call__
return self.handle_args(args)
^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 69, in handle_args
self._mark_as_used(material)
File "/tmp/solution.py", line 49, in _mark_as_used
raise AssertionError("Материалът вече е използван в тази сплав.")
AssertionError: Материалът вече е използван в тази сплав.
======================================================================
FAIL: test_positional_arguments_multiple_argument_with_dynamics (test.TestFactory.test_positional_arguments_multiple_argument_with_dynamics)
Test calling a factory using multiple positional arguments.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 128, in test_positional_arguments_multiple_argument_with_dynamics
self.assertEqual(mega_material.__class__.__name__,
AssertionError: 'Brick_Concrete_Stone_Steel_Wood' != 'Brick_Concrete_Steel_Stone_Wood'
- Brick_Concrete_Stone_Steel_Wood
? ------
+ Brick_Concrete_Steel_Stone_Wood
? ++++++
======================================================================
FAIL: test_positional_arguments_single_argument (test.TestFactory.test_positional_arguments_single_argument)
Test calling a factory using a sigle positional argument.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 80, in test_positional_arguments_single_argument
self.assertIs(type(concrete2), solution.Concrete)
AssertionError: <class 'solution.Concrete'> is not <class 'solution.Concrete'>
----------------------------------------------------------------------
Ran 10 tests in 0.010s
FAILED (failures=4)
Цветелина Гоцова
22.11.2024 16:08Да, да, аз си питам информативно, мерси!
|
Георги Кунчев
22.11.2024 15:51Имаш опцията да използваш неща от `abc` модула.
https://docs.python.org/3/library/abc.html
Дори да налседиш `abc.ABC`, това не значи, че няма да можеш да го инстанцираш. Целта е да дефинираш абстрактни методи, които наследниците ще са длъжни да имплементират.
За твоя случай е достатъчно да направиш обикновен клас и да го наследиш, защото едва ли наследниците ще имплементират нещо особено.
|
Цветелина Гоцова
22.11.2024 15:00Всъщност да, добавих такъв *абстрактен* клас впоследствие, явно след като съм качила кода тук.
Реално съществуват ли абстрактни класове, както е в C++, трябва ли да се спомене някъде, че не може да се правят инстанции от този клас и ако да, как?
|
Георги Кунчев
22.11.2024 09:45"Съгражданинът" не слуша и проверява :smile:
Давам идея да направиш клас , който всички материали да наследяват. Така ще си спетиш дефиницията на двата метода, която е една и съща за всички материали.
Освен това имай предвид, че не сме баш съграждани. Аз съм от [Селановци](https://bg.wikipedia.org/wiki/%D0%A1%D0%B5%D0%BB%D0%B0%D0%BD%D0%BE%D0%B2%D1%86%D0%B8).
Просто учих в ПМГ-то у Враца.
|
n | 1 | class Concrete: | n | 1 | class Material: |
2 | density = 2500 | 2 | density = None | ||
3 | 3 | ||||
4 | def __init__(self, mass): | 4 | def __init__(self, mass): | ||
5 | self.mass = mass | 5 | self.mass = mass | ||
n | n | 6 | self._used = False | ||
6 | 7 | ||||
7 | @property | 8 | @property | ||
8 | def volume(self): | 9 | def volume(self): | ||
9 | return self.mass / self.density | 10 | return self.mass / self.density | ||
10 | 11 | ||||
11 | 12 | ||||
n | 12 | class Brick: | n | 13 | class Concrete(Material): |
14 | density = 2500 | ||||
15 | |||||
16 | |||||
17 | class Brick(Material): | ||||
13 | density = 2000 | 18 | density = 2000 | ||
14 | 19 | ||||
n | 15 | def __init__(self, mass): | n | ||
16 | self.mass = mass | ||||
17 | 20 | ||||
n | 18 | @property | n | 21 | class Stone(Material): |
19 | def volume(self): | ||||
20 | return self.mass / self.density | ||||
21 | |||||
22 | |||||
23 | class Stone: | ||||
24 | density = 1600 | 22 | density = 1600 | ||
25 | 23 | ||||
n | 26 | def __init__(self, mass): | n | ||
27 | self.mass = mass | ||||
28 | 24 | ||||
n | 29 | @property | n | 25 | class Wood(Material): |
30 | def volume(self): | ||||
31 | return self.mass / self.density | ||||
32 | |||||
33 | |||||
34 | class Wood: | ||||
35 | density = 600 | 26 | density = 600 | ||
36 | 27 | ||||
n | 37 | def __init__(self, mass): | n | ||
38 | self.mass = mass | ||||
39 | 28 | ||||
n | 40 | @property | n | 29 | class Steel(Material): |
41 | def volume(self): | ||||
42 | return self.mass / self.density | ||||
43 | |||||
44 | |||||
45 | class Steel: | ||||
46 | density = 7700 | 30 | density = 7700 | ||
47 | 31 | ||||
n | 48 | def __init__(self, mass: int): | n | 32 | class Factory: |
49 | self.mass = mass | 33 | all_valid_materials = [] | ||
34 | dynamic_classes = {} | ||||
50 | 35 | ||||
n | 51 | @property | n | 36 | def __init__(self): |
52 | def volume(self): | 37 | self.materials = { | ||
53 | return self.mass / self.density | 38 | "Concrete": Concrete, | ||
54 | 39 | "Brick": Brick, | |||
40 | "Stone": Stone, | ||||
41 | "Wood": Wood, | ||||
42 | "Steel": Steel, | ||||
43 | } | ||||
44 | self.valid_materials = [] | ||||
45 | self.invalid_materials = [] | ||||
55 | 46 | ||||
n | 56 | class Factory: | n | 47 | def _mark_as_used(self, material): |
57 | def __init__(self): | 48 | if material._used: | ||
58 | pass | 49 | raise AssertionError("Материалът вече е използван в тази сплав.") | ||
59 | 50 | material._used = True | |||
51 | |||||
52 | def _create_dynamic_class(self, materials): | ||||
53 | base_classes = sorted([mat.__class__.__name__ for mat in materials]) | ||||
54 | class_name = "_".join(base_classes) | ||||
55 | |||||
56 | if class_name not in Factory.dynamic_classes: | ||||
57 | avg_density = sum(mat.density for mat in materials) / len(materials) | ||||
58 | new_class = type( | ||||
59 | class_name, | ||||
60 | (Material,), | ||||
61 | {"density": avg_density} | ||||
62 | ) | ||||
63 | Factory.dynamic_classes[class_name] = new_class | ||||
64 | |||||
65 | return Factory.dynamic_classes[class_name] | ||||
66 | |||||
67 | def handle_args(self, args): | ||||
68 | for material in args: | ||||
69 | self._mark_as_used(material) | ||||
70 | dynamic_class = self._create_dynamic_class(args) | ||||
71 | new_instance = dynamic_class(sum([material.mass for material in args])) | ||||
72 | self.invalid_materials.extend(args) | ||||
73 | return new_instance | ||||
74 | |||||
75 | def handle_kwargs(self, kwargs): | ||||
76 | instances = [] | ||||
77 | for name, mass in kwargs.items(): | ||||
78 | material_class = self.materials.get(name) or Factory.dynamic_classes.get(name) | ||||
79 | if material_class is None: | ||||
80 | raise ValueError("Невалидно име на материал") | ||||
81 | material_instance = material_class(mass) | ||||
82 | instances.append(material_instance) | ||||
83 | self.valid_materials.append(material_instance) | ||||
84 | return tuple(instances) | ||||
85 | |||||
60 | def __call__(self, *args, **kwargs): | 86 | def __call__(self, *args, **kwargs): | ||
n | n | 87 | if args and kwargs: | ||
88 | raise ValueError("Фабриката не може да се извика и с двата типа аргументи.") | ||||
61 | if not args and not kwargs: | 89 | if not args and not kwargs: | ||
n | 62 | raise ValueError("Фабриката трябва да се извика или с позиционни, или с именувани параметри") | n | 90 | raise ValueError("Фабриката трябва да се извика или с позиционен, или с именуван параметър.") |
91 | if args: | ||||
92 | if isinstance(args[0], Material): | ||||
93 | return self.handle_args(args) | ||||
94 | else: | ||||
95 | raise ValueError("Невалидни аргументи.") | ||||
96 | if kwargs: | ||||
97 | return self.handle_kwargs(kwargs) | ||||
63 | 98 | ||||
t | 64 | if args and kwargs: | t | 99 | def can_build(self, required_volume): |
65 | raise ValueError("Фабриката не може да се извиква и с позиционни, и с именувани параметри едновременно") | 100 | total_volume = sum(mat.volume for mat in self.valid_materials if not mat._used) | ||
101 | return total_volume >= required_volume | ||||
102 | |||||
103 | @classmethod | ||||
104 | def can_build_together(cls, required_volume): | ||||
105 | total_volume = sum(mat.volume for factory in Factory.all_valid_materials for mat in factory.valid_materials) | ||||
106 | return total_volume >= required_volume |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | class Concrete: | f | 1 | class Concrete: |
2 | density = 2500 | 2 | density = 2500 | ||
3 | 3 | ||||
4 | def __init__(self, mass): | 4 | def __init__(self, mass): | ||
5 | self.mass = mass | 5 | self.mass = mass | ||
6 | 6 | ||||
7 | @property | 7 | @property | ||
8 | def volume(self): | 8 | def volume(self): | ||
9 | return self.mass / self.density | 9 | return self.mass / self.density | ||
10 | 10 | ||||
11 | 11 | ||||
12 | class Brick: | 12 | class Brick: | ||
13 | density = 2000 | 13 | density = 2000 | ||
14 | 14 | ||||
15 | def __init__(self, mass): | 15 | def __init__(self, mass): | ||
16 | self.mass = mass | 16 | self.mass = mass | ||
17 | 17 | ||||
18 | @property | 18 | @property | ||
19 | def volume(self): | 19 | def volume(self): | ||
20 | return self.mass / self.density | 20 | return self.mass / self.density | ||
21 | 21 | ||||
22 | 22 | ||||
23 | class Stone: | 23 | class Stone: | ||
24 | density = 1600 | 24 | density = 1600 | ||
25 | 25 | ||||
26 | def __init__(self, mass): | 26 | def __init__(self, mass): | ||
27 | self.mass = mass | 27 | self.mass = mass | ||
28 | 28 | ||||
29 | @property | 29 | @property | ||
30 | def volume(self): | 30 | def volume(self): | ||
31 | return self.mass / self.density | 31 | return self.mass / self.density | ||
32 | 32 | ||||
33 | 33 | ||||
34 | class Wood: | 34 | class Wood: | ||
35 | density = 600 | 35 | density = 600 | ||
36 | 36 | ||||
37 | def __init__(self, mass): | 37 | def __init__(self, mass): | ||
38 | self.mass = mass | 38 | self.mass = mass | ||
39 | 39 | ||||
40 | @property | 40 | @property | ||
41 | def volume(self): | 41 | def volume(self): | ||
42 | return self.mass / self.density | 42 | return self.mass / self.density | ||
43 | 43 | ||||
44 | 44 | ||||
45 | class Steel: | 45 | class Steel: | ||
46 | density = 7700 | 46 | density = 7700 | ||
47 | 47 | ||||
48 | def __init__(self, mass: int): | 48 | def __init__(self, mass: int): | ||
49 | self.mass = mass | 49 | self.mass = mass | ||
50 | 50 | ||||
51 | @property | 51 | @property | ||
52 | def volume(self): | 52 | def volume(self): | ||
53 | return self.mass / self.density | 53 | return self.mass / self.density | ||
54 | 54 | ||||
55 | 55 | ||||
t | 56 | brick = Brick(2000) | t | 56 | class Factory: |
57 | print(brick.volume) | 57 | def __init__(self): | ||
58 | pass | ||||
59 | |||||
60 | def __call__(self, *args, **kwargs): | ||||
61 | if not args and not kwargs: | ||||
62 | raise ValueError("Фабриката трябва да се извика или с позиционни, или с именувани параметри") | ||||
63 | |||||
64 | if args and kwargs: | ||||
65 | raise ValueError("Фабриката не може да се извиква и с позиционни, и с именувани параметри едновременно") |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
26.11.2024 12:22
26.11.2024 12:25
26.11.2024 12:23