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

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

6 точки общо

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

  1class Material:
  2    """ A PARENT CLASS SO AS TO AVOID WRITING THE SAME CODE """
  3    _DENSITY = 1
  4
  5    def __init__(self, mass):
  6        self.mass = mass
  7        self._is_valid = True  # mark the material as valid to use (for the Alloy class)
  8
  9    @property
 10    def volume(self):
 11        return self.mass / self._DENSITY
 12
 13    def invalidate(self):
 14        """Mark this instance as invalid"""
 15        self._is_valid = False
 16
 17    def assert_valid(self):
 18        """Ensure this instance is still valid"""
 19        assert self._is_valid, "This material instance is no longer valid for use"
 20
 21
 22class Concrete(Material):
 23    _DENSITY = 2500
 24
 25
 26class Brick(Material):
 27    _DENSITY = 2000
 28
 29
 30class Stone(Material):
 31    _DENSITY = 1600
 32
 33
 34class Wood(Material):
 35    _DENSITY = 600
 36
 37
 38class Steel(Material):
 39    _DENSITY = 7700
 40
 41
 42class Alloy(Material):
 43    _created_classes = {}
 44
 45    def __new__(cls, *Materials):
 46        # ensure that all materials are valid before using them
 47        if not Materials: #because I got a few cases of / by 0
 48            return
 49        for m in Materials:
 50            m.assert_valid()
 51        all_base_materials = set()
 52        for m in Materials:
 53            if isinstance(m, Alloy):
 54                all_base_materials.update(m._materials)
 55            else:
 56                all_base_materials.add(m)
 57        material_classes = sorted(type(m).__name__ for m in all_base_materials)
 58        class_name = "_".join(material_classes)
 59        #check if the class already exists
 60        if class_name not in cls._created_classes:
 61            density = sum(m._DENSITY for m in all_base_materials) / len(all_base_materials)
 62            new_class = type(class_name, (cls,), {
 63                '_DENSITY': density,
 64                '_materials': tuple(all_base_materials)
 65            })
 66            cls._created_classes[class_name] = new_class
 67
 68        return super().__new__(cls._created_classes[class_name])
 69
 70    def __init__(self, *Materials):
 71        #avoid reinitialization if the instance has already been created
 72        if hasattr(self, '_initialized') and self._initialized:
 73            return
 74        #invalidate the materials used
 75        for m in Materials:
 76            m.invalidate()
 77        self.mass = sum(m.mass for m in Materials)
 78        self._is_valid = True #mark the instance as valid
 79        self._initialized = True
 80
 81
 82class Factory:
 83    _instances = []
 84
 85    def __init__(self):
 86        self._materials = [] #so as to keep track and make the serialization work correctly for multiple parents
 87        Factory._instances.append(self)
 88
 89    def __serialize(self, key, val):
 90        material_classes = {
 91            'Concrete': Concrete,
 92            'Brick': Brick,
 93            'Stone': Stone,
 94            'Wood': Wood,
 95            'Steel': Steel
 96        }
 97        if key in material_classes:
 98            material = material_classes[key](val)
 99            self._materials.append(material)
100            return [material]  #returning an iterable becuase -> error
101        if key in Alloy._created_classes:
102            cls = Alloy._created_classes[key]
103            material = cls.__new__(cls)
104            material.__init__(val)
105            self._materials.append(material)
106            return [material]
107        raise ValueError(f"Invalid key {key}")
108
109    def __call__(self, *args, **kwargs):
110        if (len(args) > 0 and len(kwargs) > 0) or (len(args) == 0 and len(kwargs) == 0):
111            raise ValueError("The given arguments are invalid")
112        elif len(kwargs) > 0:
113            res = []
114            for key in kwargs:
115                res.extend(self.__serialize(key, kwargs[key]))
116            return tuple(res)
117        elif len(args) > 0:
118            material = Alloy(*args)
119            self._materials.append(material)
120            return material
121
122    def can_build(self, required_volume):
123        total_volume = sum(m.volume for m in self._materials if m._is_valid)
124        return total_volume >= required_volume
125
126    @classmethod
127    def can_build_together(cls, required_volume):
128        total_volume = sum(
129            m.volume for factory in cls._instances for m in factory._materials if m._is_valid
130        )
131        return total_volume >= required_volume

..F.FE.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 97, in test_positional_arguments_multiple_argument_from_initial_set
brick_concrete_wood = type(result)(1700)
^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 50, in __new__
m.assert_valid()
^^^^^^^^^^^^^^
AttributeError: 'int' object has no attribute 'assert_valid'

======================================================================
FAIL: test_materials_between_factories (test.TestFactory.test_materials_between_factories)
Test materials sharing.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 157, in test_materials_between_factories
self.assertIs(type(result1), type(result2))
AssertionError: <class 'solution.Brick_Concrete_Steel_Stone_Wood'> is not <class 'NoneType'>

======================================================================
FAIL: 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 148, in test_named_arguments_with_dynamically_created_classes
self.assertIs(type(result1), type(result2))
AssertionError: <class 'solution.Brick_Concrete_Steel_Stone_Wood'> is not <class 'NoneType'>

======================================================================
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.047s

FAILED (failures=3, errors=1)

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