Домашни > Another brick in the wall > Решения > Решението на Димитър Танков

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

6 точки общо

6 успешни теста
4 неуспешни теста
Код
Скрий всички коментари

  1class Material:
  2    def __init__(self, density, mass):
  3        self.density = density
  4        self.mass = mass
  5        self.base_materials = None
  6        self.was_used = False
  7
  8    @property
  9    def volume(self):
 10        return self.mass / self.density
 11
 12    def is_composite(self):
 13        return self.base_materials is not None
 14
 15    @staticmethod
 16    def subclass_from_name(subclass_name):
 17        for subclass in Material.__subclasses__():
 18            if subclass.__name__ == subclass_name:
 19                return subclass
 20
 21        return type(subclass_name, (Material,), {"__init__": lambda self, mass: (setattr(self, "mass", mass),
 22                                                                                 setattr(self, "base_materials", None),
 23                                                                                 setattr(self, "was_used", False),
 24                                                                                 None)[3]})
 25
 26    @staticmethod
 27    def __get_new_base_materials(materials):
 28        new_base_materials = {}
 29
 30        for material in materials:
 31            assert not material.was_used, "Given material has already been used"
 32            material.was_used = True
 33
 34            if material.is_composite():
 35                for base_material in material.base_materials.values():
 36                    if new_base_materials.get(base_material.__class__) is None:
 37                        new_base_materials[base_material.__class__] = base_material
 38                    else:
 39                        new_base_materials[base_material.__class__].mass += base_material.mass
 40            else:
 41                if new_base_materials.get(material.__class__) is None:
 42                    new_base_materials[material.__class__] = material
 43                else:
 44                    new_base_materials[material.__class__].mass += material.mass
 45
 46        return new_base_materials
 47
 48    @staticmethod
 49    def create_composite_material(*args):
 50        assert all(isinstance(arg, Material) for arg in args)
 51        assert all(not arg.was_used for arg in args), "Given material has already been used"
 52
 53        if len(args) == 1:
 54            if not args[0].was_used:
 55                return args[0]
 56            else:
 57                raise AssertionError("Given material has already been used")
 58
 59        new_base_materials = Material.__get_new_base_materials(args)
 60
 61        new_base_names = sorted({base_material_class.__name__ for base_material_class in new_base_materials})
 62        composite_class_name = "_".join(new_base_names)
 63
 64        composite_density = (sum([base_material.density for base_material in new_base_materials.values()])
 65                             / len(new_base_materials))
 66        composite_mass = sum([base_material.mass for base_material in new_base_materials.values()])
 67
 68        composite_material = Material.subclass_from_name(composite_class_name)(composite_mass)
 69        composite_material.density = composite_density
 70        composite_material.base_materials = new_base_materials
 71
 72        return composite_material
 73
 74
 75class Concrete(Material):
 76    def __init__(self, mass):
 77        super().__init__(2500, mass)
 78
 79
 80class Brick(Material):
 81    def __init__(self, mass):
 82        super().__init__(2000, mass)
 83
 84
 85class Stone(Material):
 86    def __init__(self, mass):
 87        super().__init__(1600, mass)
 88
 89
 90class Wood(Material):
 91    def __init__(self, mass):
 92        super().__init__(600, mass)
 93
 94
 95class Steel(Material):
 96    def __init__(self, mass):
 97        super().__init__(7700, mass)
 98
 99
100class Factory:
101    all_factories = []
102
103    def __init__(self):
104        self.own_materials = []
105        self.__class__.all_factories.append(self)
106
107    def can_build(self, target):
108        cumulative_volume = 0
109
110        for material in self.own_materials:
111            if not material.was_used:
112                cumulative_volume += material.volume
113
114        return cumulative_volume >= target
115
116    @classmethod
117    def can_build_together(cls, target):
118        cumulative_volume = 0
119
120        for factory in cls.all_factories:
121            for material in factory.own_materials:
122                cumulative_volume += material.volume
123
124        return cumulative_volume >= target
125
126    def __call__(self, *args, **kwargs):
127        if args and kwargs:
128            raise ValueError("Cannot call factory object with positional and named arguments")
129
130        if args:
131            composite_material = Material.create_composite_material(*args)
132            self.own_materials.append(composite_material)
133
134            return composite_material
135
136        elif kwargs:
137            materials = []
138            for key, value in kwargs.items():
139                subclass_found = False
140                for subclass in Material.__subclasses__():
141                    if key == subclass.__name__:
142                        materials.append(subclass(value))
143                        subclass_found = True
144                        break
145
146                if not subclass_found:
147                    raise ValueError("Unknown material {}".format(key))
148
149            self.own_materials.extend(materials)
150            return tuple(materials)
151
152        else:
153            raise ValueError("Cannot call factory object with no arguments")

.FF..E.F..
======================================================================
ERROR: 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)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 10, in volume
return self.mass / self.density
^^^^^^^^^^^^
AttributeError: 'Brick_Concrete_Wood' object has no attribute 'density'

======================================================================
FAIL: test_can_build (test.TestFactory.test_can_build)
Test can_build methods.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 177, in test_can_build
self.assertFalse(solution.Factory.can_build_together(6.0001))
AssertionError: True is not false

======================================================================
FAIL: test_materials_between_factories (test.TestFactory.test_materials_between_factories)
Test materials sharing.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 161, in test_materials_between_factories
with self.assertRaises(AssertionError):
AssertionError: AssertionError not raised

======================================================================
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 83, in test_positional_arguments_single_argument
with self.assertRaises(AssertionError):
AssertionError: AssertionError not raised

----------------------------------------------------------------------
Ran 10 tests in 0.012s

FAILED (failures=3, errors=1)

Дискусия
Георги Кунчев
22.11.2024 10:24

Да съм честен - `__get_new_base_materials` ме обърка. Използваш го хем да събереш класовете, хем масите на инстанциите. Бих направил така, че един алгоритъм да събере имената на клсасовете, а друг само да събере масите. Ще се улесни. Ще имаш single responsibility. Като цяло ми харесва, че си направил така, че `Material` да върши повечето работа. Фабриката е доста проста. Но пък материала става доста сложен.
История
Това решение има само една версия.