Домашни > Another brick in the wall > Решения > Решението на Виктория Гарджева

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

6 точки общо

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

  1class Material:
  2    """Represents a base class with all the shared functionalities between the base materials."""
  3
  4    DENSITY = None
  5
  6    def __init__(self, mass: int):
  7        self.mass = mass
  8
  9    @property
 10    def volume(self) -> float:
 11        return self.mass / self.DENSITY
 12
 13
 14class Concrete(Material):
 15    DENSITY = 2500
 16
 17
 18class Brick(Material):
 19    DENSITY = 2000
 20
 21
 22class Stone(Material):
 23    DENSITY = 1600
 24
 25
 26class Wood(Material):
 27    DENSITY = 600
 28
 29
 30class Steel(Material):
 31    DENSITY = 7700
 32
 33
 34class Factory:
 35    """Factory that creates dynamic classes - alloys, and materials."""
 36
 37    BASE_MATERIALS = {
 38        "Concrete": Concrete,
 39        "Brick": Brick,
 40        "Stone": Stone,
 41        "Wood": Wood,
 42        "Steel": Steel
 43    }
 44
 45    # A dictionary to store by keys the names of all created alloys
 46    all_created_alloys = {}
 47
 48    # A list of all materials used by any factory
 49    used_materials = []
 50
 51    # A list of all instances of the Factory class
 52    all_factories = []
 53
 54    def __init__(self):
 55        self.materials = []
 56        Factory.all_factories.append(self)
 57
 58    def _process_named_arguments(self, kwargs):
 59        """Processes named arguments."""
 60        material_instances = []
 61        for name, mass in kwargs.items():
 62            if name in Factory.BASE_MATERIALS or name in Factory.all_created_alloys:
 63                material_class = Factory.BASE_MATERIALS[name] or Factory.all_created_alloys[name]
 64                new_material = material_class(mass)
 65                material_instances.append(new_material)
 66                self.materials.append(new_material)
 67            else:
 68                raise ValueError("Incorrect material type: {name}.")
 69        return tuple(material_instances)
 70
 71    def _process_positional_arguments(self, args):
 72        """Processes positional arguments."""
 73        for arg in args:
 74            for used_material in Factory.used_materials:
 75                if arg is used_material:
 76                    raise AssertionError("Material has already been used.")
 77        # In case materials are valid, enlists them as used.
 78        for arg in args:
 79            Factory.used_materials.append(arg)
 80
 81        # Retrieves all base material types for the alloy.
 82        alloy_materials = []
 83        for arg in args:
 84            material_type = type(arg)
 85            if material_type in Factory.BASE_MATERIALS.values():
 86                alloy_materials.append(material_type)
 87            elif material_type in Factory.all_created_alloys.values():
 88                alloy_materials.extend(arg.base_materials)
 89
 90        alloy_name = "_".join(sorted(cls.__name__ for cls in alloy_materials))
 91        if alloy_name not in Factory.all_created_alloys:
 92            alloy_density = sum(cls.DENSITY for cls in alloy_materials) / len(alloy_materials)
 93
 94            class Alloy(Material):
 95
 96                DENSITY = alloy_density
 97                base_materials = alloy_materials
 98
 99            Alloy.__name__ = alloy_name
100            Factory.all_created_alloys[alloy_name] = Alloy
101
102        # Retrieves information and create an alloy instance with it.
103        alloy_class = Factory.all_created_alloys[alloy_name]
104        alloy_mass = sum(arg.mass for arg in args)
105        alloy_instance = alloy_class(alloy_mass)
106        self.materials.append(alloy_instance)
107        return alloy_instance
108
109    def __call__(self, *args, **kwargs):
110        """Handles the calling of the factory class."""
111        if (args and kwargs) or (not args and not kwargs):
112            raise ValueError("Incorrect calling of object.")
113        elif kwargs:
114            return self._process_named_arguments(kwargs)
115        else:
116            return self._process_positional_arguments(args)
117
118    def can_build(self, to_build: int):
119        """Checks if the current instance has enough materials to build a specific amount."""
120        total_volume = sum(
121            material.volume for material in self.materials
122            if material not in self.used_materials
123        )
124        return total_volume >= to_build
125
126    @classmethod
127    def can_build_together(cls, to_build: int):
128        """Checks if all factories have enough materials to build a specific amount."""
129        total_volume = sum(
130            instance.material.volume for instance in cls.all_factories
131            if instance not in cls.used_materials
132        )
133        return total_volume >= to_build

.EE.E..F..
======================================================================
ERROR: 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))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 129, in can_build_together
total_volume = sum(
^^^^
File "/tmp/solution.py", line 130, in <genexpr>
instance.material.volume for instance in cls.all_factories
^^^^^^^^^^^^^^^^^
AttributeError: 'Factory' object has no attribute 'material'. Did you mean: 'materials'?

======================================================================
ERROR: test_materials_between_factories (test.TestFactory.test_materials_between_factories)
Test materials sharing.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 156, in test_materials_between_factories
result2, = self.factory2(Brick_Concrete_Steel_Stone_Wood=2)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 114, in __call__
return self._process_named_arguments(kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 63, in _process_named_arguments
material_class = Factory.BASE_MATERIALS[name] or Factory.all_created_alloys[name]
~~~~~~~~~~~~~~~~~~~~~~^^^^^^
KeyError: 'Brick_Concrete_Steel_Stone_Wood'

======================================================================
ERROR: test_named_arguments_with_dynamically_created_classes (test.TestFactory.test_named_arguments_with_dynamically_created_classes)
Test dynamically created classes uniqueness.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 145, in test_named_arguments_with_dynamically_created_classes
result2, = self.factory1(Brick_Concrete_Steel_Stone_Wood=2)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 114, in __call__
return self._process_named_arguments(kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 63, in _process_named_arguments
material_class = Factory.BASE_MATERIALS[name] or Factory.all_created_alloys[name]
~~~~~~~~~~~~~~~~~~~~~~^^^^^^
KeyError: '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.Factory._process_positional_arguments.<locals>.Alloy'> is not <class 'solution.Concrete'>

----------------------------------------------------------------------
Ran 10 tests in 0.011s

FAILED (failures=1, errors=3)

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