1SEPARATOR = "_"
2DENSITIES_OF_MATERIALS = {
3 "Concrete" : 2500,
4 "Brick" : 2000,
5 "Stone" : 1600,
6 "Wood" : 600,
7 "Steel" : 7700
8}
9
10class Material:
11 DENSITY = 1 # по дефаулт, да не е 0 да не станат некъде проблеми с делението
12
13 def __init__(self, mass):
14 self.is_valid = True # дали даден обект вече е бил използван от фабрика
15 self.mass = mass # масата му
16 self.factory_of_creation = None # неговата родина
17
18 @property
19 def volume(self):
20 if self.DENSITY != 0: # за всеки случай
21 return self.mass / self.DENSITY
22
23 def __bool__(self):
24 return self.is_valid # за репрезентация на това дали обекта е бил използван
25
26 def change_validity(self, flag):
27 self.is_valid = flag
28
29
30class Concrete(Material):
31 DENSITY = DENSITIES_OF_MATERIALS["Concrete"]
32
33
34
35class Brick(Material):
36 DENSITY = DENSITIES_OF_MATERIALS["Brick"]
37
38
39
40class Stone(Material):
41 DENSITY = DENSITIES_OF_MATERIALS["Stone"]
42
43
44
45class Wood(Material):
46 DENSITY = DENSITIES_OF_MATERIALS["Wood"]
47
48
49
50class Steel(Material):
51 DENSITY = DENSITIES_OF_MATERIALS["Steel"]
52
53
54class Factory:
55
56 created_classes = [] # всички класове, които са били правени вече
57 total_sum_volume = 0 # сумата от обемите на материалите, намиращи се из всички фабрики
58
59 def __init__(self):
60 self.sum_volume = 0
61
62 def _create_complex_object(self, mass_value, class_name):
63 for item in Factory.created_classes:
64 if item.__name__ == class_name:
65 factory_object = item(mass_value)
66 factory_object.DENSITY = DENSITIES_OF_MATERIALS[class_name]
67 return factory_object
68
69 def _create_object(self, class_name, mass_value):
70 if class_name == "Concrete":
71 return Concrete(mass_value)
72 elif class_name == "Brick":
73 return Brick(mass_value)
74 elif class_name == "Stone":
75 return Stone(mass_value)
76 elif class_name == "Wood":
77 return Wood(mass_value)
78 elif class_name == "Steel":
79 return Steel(mass_value)
80 else:
81 return self._create_complex_object(mass_value, class_name)
82
83 def _split_material(self, material_object):
84 if not material_object:
85 raise AssertionError("Invalid object!")
86 material_count = 1 # брой материали, помага за смятане на плътността
87 list_of_splitted_materials = [] # за комплексните материали, да се раздели на отделни стрингове
88 current_material_name = ""
89 sum_density = 0
90 for character in type(material_object).__name__:
91 if character == SEPARATOR:
92 list_of_splitted_materials.append(current_material_name)
93 sum_density += DENSITIES_OF_MATERIALS[current_material_name]
94 current_material_name = ""
95 material_count += 1
96 else:
97 current_material_name += character
98 list_of_splitted_materials.append(current_material_name)
99 sum_density += DENSITIES_OF_MATERIALS[current_material_name]
100 return material_count, list_of_splitted_materials, sum_density
101
102 def _separate_materials(self, materials):
103 list_of_separated_materials = []
104 number_of_materials = 0
105 sum_mass = 0
106 sum_density = 0
107 for item in materials:
108 sum_mass += item.mass
109 current_material_type = self._split_material(item)
110 number_of_materials += current_material_type[0]
111 list_of_separated_materials.extend(current_material_type[1])
112 sum_density += current_material_type[2]
113 # в случай, че горния код се е изпълнил и сме стигнали до тук, значи всички елементи са били валидни, и трябва да ги направим сега невалидни
114 for item in materials:
115 item.change_validity(False)
116 if item.factory_of_creation is self: # ако е създаден в сегашната фабрика, то тези невалидни материали се махат
117 self.sum_volume -= item.volume # в последствие ще се добави volume-a на сплавта
118 if item.factory_of_creation != None: # ако току що се създава материала, примерно factory(Brick(8630)), ще бъде None и не искаме да го махаме
119 Factory.total_sum_volume -= item.volume
120 return number_of_materials, list_of_separated_materials, sum_mass, sum_density
121
122
123
124 def __call__(self, *args, **kwargs):
125 if len(args) == 0 and len(kwargs) == 0:
126 raise ValueError("Function called with no arguments!")
127 elif len(args) != 0 and len(kwargs) != 0:
128 raise ValueError("Function called with too many different arguments!")
129 else:
130 list_of_materials = []
131 if len(kwargs) !=0:
132 for element in kwargs.keys():
133 if element not in DENSITIES_OF_MATERIALS.keys():
134 raise ValueError("Unknown material")
135 list_of_materials.append(self._create_object(element, kwargs[element]))
136 for material in list_of_materials:
137 material.factory_of_creation = self
138 self.sum_volume += material.volume
139 Factory.total_sum_volume += material.volume
140 return tuple(list_of_materials)
141 else:
142 number_of_materials, list_class_names, sum_mass, sum_density = self._separate_materials(args)
143 list_class_names.sort()
144 new_class_name = SEPARATOR.join(list_class_names)
145 for item in Factory.created_classes:
146 if item.__name__ == new_class_name:
147 factory_object = item(sum_mass)
148 factory_object.factory_of_creation = self
149 factory_object.DENSITY = DENSITIES_OF_MATERIALS[new_class_name]
150 self.sum_volume += factory_object.volume
151 Factory.total_sum_volume += factory_object.volume
152 return factory_object
153 new_class = type(new_class_name, (Material, ), {
154 "__init__" : Material.__init__,
155 "DENSITY" : Material.DENSITY,
156 "volume" : Material.volume,
157 "__bool__" : Material.__bool__,
158 "change_validity" : Material.change_validity
159 })
160 Factory.created_classes.append(new_class)
161 factory_object = new_class(sum_mass)
162 factory_object.factory_of_creation = self
163 factory_object.DENSITY = sum_density/number_of_materials
164 DENSITIES_OF_MATERIALS[new_class_name] = factory_object.DENSITY
165 self.sum_volume += factory_object.volume
166 Factory.total_sum_volume += factory_object.volume
167 return factory_object
168
169 def can_build(self, height):
170 return self.sum_volume >= height
171
172 @staticmethod
173 def can_build_together(height):
174 return Factory.total_sum_volume >= height
175
.....F.F..
======================================================================
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 98, in test_positional_arguments_multiple_argument_from_initial_set
self.assertEqual(brick_concrete_wood.volume, 1.0)
AssertionError: 1700.0 != 1.0
======================================================================
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.051s
FAILED (failures=2)
23.11.2024 15:39
23.11.2024 15:41
23.11.2024 15:44
23.11.2024 15:44
23.11.2024 15:46
23.11.2024 15:48
23.11.2024 15:52
23.11.2024 15:52
23.11.2024 15:53
23.11.2024 15:56
23.11.2024 15:42
23.11.2024 15:59