Домашни > Another brick in the wall > Решения > Решението на Стивън Александров

Резултати
8 точки от тестове
0 точки от учител

8 точки общо

8 успешни теста
2 неуспешни теста
Код (начална версия, докато не се усетя, че не работи)
Скрий всички коментари

  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)

Дискусия
История
Това решение има само една версия.