1class Material:
  2    THICKNESS = 0
  3    def __init__(self, mass: int):
  4        self.mass = mass
  5
  6    @property
  7    def volume(self):
  8        return float(self.mass / self.THICKNESS)
  9
 10
 11class Concrete(Material):
 12    THICKNESS = 2500
 13
 14
 15class Brick(Material):
 16    THICKNESS = 2000
 17
 18
 19class Stone(Material):
 20    THICKNESS = 1600
 21
 22
 23class Wood(Material):
 24    THICKNESS = 600
 25
 26
 27class Steel(Material):
 28    THICKNESS = 7700
 29
 30
 31class Factory:
 32    _dynamic_classes = {}
 33    all_created_materials = set()  
 34    used_materials = set()  
 35
 36    def __init__(self):
 37        self.current_materials = set()  
 38
 39    def __call__(self, *args, **kwargs):
 40        if args and kwargs:
 41            raise ValueError("Cannot mix positional and keyword arguments.")
 42        if not args and not kwargs:
 43            raise ValueError("Cannot call the function without arguments.")
 44
 45        if args:
 46            return self._call_with_positional_args(args)
 47        if kwargs:
 48            return self._call_with_keyword_args(kwargs)
 49
 50    def _call_with_keyword_args(self, kwargs):
 51        ALLOWED_NAMES = {
 52            "Concrete": Concrete,
 53            "Brick": Brick,
 54            "Stone": Stone,
 55            "Wood": Wood,
 56            "Steel": Steel,
 57        }
 58
 59        result = []
 60        for name, mass in kwargs.items():
 61            if name not in ALLOWED_NAMES and name not in Factory._dynamic_classes:
 62                raise ValueError(f"Invalid material type: {name}")
 63            material_class = (
 64                ALLOWED_NAMES.get(name) or Factory._dynamic_classes[name]
 65            )
 66            material = material_class(mass)
 67            self.current_materials.add(material)
 68            Factory.all_created_materials.add(material)
 69            result.append(material)
 70
 71        return tuple(result)
 72
 73    def _call_with_positional_args(self, args):
 74        for material in args:
 75            if material in Factory.used_materials:
 76                raise AssertionError("This material has already been used.")
 77            if not isinstance(material, Material):
 78                raise ValueError("Positional arguments must be instances of Materials.")
 79            Factory.used_materials.add(material)
 80
 81        base_classes_names = []
 82        for arg in args:
 83            base_classes_names += type(arg).__name__.split('_')
 84
 85        sorted_materials_names = sorted(base_classes_names)
 86        new_class_name = '_'.join(sorted_materials_names)
 87
 88        if new_class_name not in Factory._dynamic_classes:
 89            avg_thickness = sum(material.THICKNESS for material in args) / len(args)
 90            new_class = type(new_class_name, (Material,), {"THICKNESS": avg_thickness})
 91            Factory._dynamic_classes[new_class_name] = new_class
 92
 93        new_class = Factory._dynamic_classes[new_class_name]
 94        total_mass = sum(material.mass for material in args)
 95        new_material = new_class(total_mass)
 96
 97        self.current_materials.add(new_material)
 98        Factory.all_created_materials.add(new_material)
 99        return new_material
100
101    def can_build(self, necessary_volume: int):
102        total_volume = sum(
103            instance.volume
104            for instance in self.current_materials
105            if instance not in Factory.used_materials
106        )
107        return total_volume >= necessary_volume
108
109    @classmethod
110    def can_build_together(cls, necessary_volume: int):
111        total_volume = sum(
112            instance.volume
113            for instance in cls.all_created_materials
114            if instance not in cls.used_materials or isinstance(instance, Material)
115        )
116        return total_volume >= necessary_volume
.F...FFF..
======================================================================
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_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 116, in test_positional_arguments_multiple_argument_from_initial_set
    concrete_wood = self.factory1(concrete, wood)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/solution.py", line 46, in __call__
    return self._call_with_positional_args(args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/solution.py", line 76, in _call_with_positional_args
    raise AssertionError("This material has already been used.")
AssertionError: This material has already been used.
======================================================================
FAIL: test_positional_arguments_multiple_argument_with_dynamics (test.TestFactory.test_positional_arguments_multiple_argument_with_dynamics)
Test calling a factory using multiple positional arguments.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/test.py", line 130, in test_positional_arguments_multiple_argument_with_dynamics
    self.assertAlmostEqual(mega_material.volume, 6.77, places=2)  # mass=19500, density=2880
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 5.661290322580645 != 6.77 within 2 places (1.1087096774193546 difference)
======================================================================
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.011s
FAILED (failures=4)
| n | 1 | class Materials: | n | 1 | class Material: | 
| 2 | THICKNESS = 0 | ||||
| 2 | def __init__(self, mass: int): | 3 | def __init__(self, mass: int): | ||
| 3 | self.mass = mass | 4 | self.mass = mass | ||
| 4 | 5 | ||||
| 5 | @property | 6 | @property | ||
| 6 | def volume(self): | 7 | def volume(self): | ||
| n | 7 | return float(self.mass / self.thickness) | n | 8 | return float(self.mass / self.THICKNESS) | 
| 8 | 9 | ||||
| 9 | 10 | ||||
| n | 10 | class Concrete(Materials): | n | 11 | class Concrete(Material): | 
| 11 | thickness = 2500 | 12 | THICKNESS = 2500 | ||
| 12 | 13 | ||||
| 13 | 14 | ||||
| n | 14 | class Brick(Materials): | n | 15 | class Brick(Material): | 
| 15 | thickness = 2000 | 16 | THICKNESS = 2000 | ||
| 16 | 17 | ||||
| 17 | 18 | ||||
| n | 18 | class Stone(Materials): | n | 19 | class Stone(Material): | 
| 19 | thickness = 1600 | 20 | THICKNESS = 1600 | ||
| 20 | 21 | ||||
| 21 | 22 | ||||
| n | 22 | class Wood(Materials): | n | 23 | class Wood(Material): | 
| 23 | thickness = 600 | 24 | THICKNESS = 600 | ||
| 24 | 25 | ||||
| 25 | 26 | ||||
| n | 26 | class Steel(Materials): | n | 27 | class Steel(Material): | 
| 27 | thickness = 7700 | 28 | THICKNESS = 7700 | ||
| 28 | 29 | ||||
| 29 | 30 | ||||
| 30 | class Factory: | 31 | class Factory: | ||
| 31 | _dynamic_classes = {} | 32 | _dynamic_classes = {} | ||
| 32 | all_created_materials = set() | 33 | all_created_materials = set() | ||
| 33 | used_materials = set() | 34 | used_materials = set() | ||
| 34 | 35 | ||||
| 35 | def __init__(self): | 36 | def __init__(self): | ||
| 36 | self.current_materials = set() | 37 | self.current_materials = set() | ||
| 37 | 38 | ||||
| 38 | def __call__(self, *args, **kwargs): | 39 | def __call__(self, *args, **kwargs): | ||
| 39 | if args and kwargs: | 40 | if args and kwargs: | ||
| 40 | raise ValueError("Cannot mix positional and keyword arguments.") | 41 | raise ValueError("Cannot mix positional and keyword arguments.") | ||
| 41 | if not args and not kwargs: | 42 | if not args and not kwargs: | ||
| 42 | raise ValueError("Cannot call the function without arguments.") | 43 | raise ValueError("Cannot call the function without arguments.") | ||
| 43 | 44 | ||||
| 44 | if args: | 45 | if args: | ||
| 45 | return self._call_with_positional_args(args) | 46 | return self._call_with_positional_args(args) | ||
| 46 | if kwargs: | 47 | if kwargs: | ||
| 47 | return self._call_with_keyword_args(kwargs) | 48 | return self._call_with_keyword_args(kwargs) | ||
| 48 | 49 | ||||
| 49 | def _call_with_keyword_args(self, kwargs): | 50 | def _call_with_keyword_args(self, kwargs): | ||
| n | 50 | allowed_names = { | n | 51 | ALLOWED_NAMES = { | 
| 51 | "Concrete": Concrete, | 52 | "Concrete": Concrete, | ||
| 52 | "Brick": Brick, | 53 | "Brick": Brick, | ||
| 53 | "Stone": Stone, | 54 | "Stone": Stone, | ||
| 54 | "Wood": Wood, | 55 | "Wood": Wood, | ||
| 55 | "Steel": Steel, | 56 | "Steel": Steel, | ||
| 56 | } | 57 | } | ||
| 57 | 58 | ||||
| 58 | result = [] | 59 | result = [] | ||
| 59 | for name, mass in kwargs.items(): | 60 | for name, mass in kwargs.items(): | ||
| n | 60 | if name not in allowed_names and name not in Factory._dynamic_classes: | n | 61 | if name not in ALLOWED_NAMES and name not in Factory._dynamic_classes: | 
| 61 | raise ValueError(f"Invalid material type: {name}") | 62 | raise ValueError(f"Invalid material type: {name}") | ||
| 62 | material_class = ( | 63 | material_class = ( | ||
| n | 63 | allowed_names.get(name) or Factory._dynamic_classes[name] | n | 64 | ALLOWED_NAMES.get(name) or Factory._dynamic_classes[name] | 
| 64 | ) | 65 | ) | ||
| 65 | material = material_class(mass) | 66 | material = material_class(mass) | ||
| 66 | self.current_materials.add(material) | 67 | self.current_materials.add(material) | ||
| 67 | Factory.all_created_materials.add(material) | 68 | Factory.all_created_materials.add(material) | ||
| 68 | result.append(material) | 69 | result.append(material) | ||
| 69 | 70 | ||||
| 70 | return tuple(result) | 71 | return tuple(result) | ||
| 71 | 72 | ||||
| 72 | def _call_with_positional_args(self, args): | 73 | def _call_with_positional_args(self, args): | ||
| 73 | for material in args: | 74 | for material in args: | ||
| 74 | if material in Factory.used_materials: | 75 | if material in Factory.used_materials: | ||
| 75 | raise AssertionError("This material has already been used.") | 76 | raise AssertionError("This material has already been used.") | ||
| n | 76 | if not isinstance(material, Materials): | n | 77 | if not isinstance(material, Material): | 
| 77 | raise ValueError("Positional arguments must be instances of Materials.") | 78 | raise ValueError("Positional arguments must be instances of Materials.") | ||
| 78 | Factory.used_materials.add(material) | 79 | Factory.used_materials.add(material) | ||
| 79 | 80 | ||||
| 80 | base_classes_names = [] | 81 | base_classes_names = [] | ||
| 81 | for arg in args: | 82 | for arg in args: | ||
| 82 | base_classes_names += type(arg).__name__.split('_') | 83 | base_classes_names += type(arg).__name__.split('_') | ||
| 83 | 84 | ||||
| 84 | sorted_materials_names = sorted(base_classes_names) | 85 | sorted_materials_names = sorted(base_classes_names) | ||
| 85 | new_class_name = '_'.join(sorted_materials_names) | 86 | new_class_name = '_'.join(sorted_materials_names) | ||
| 86 | 87 | ||||
| 87 | if new_class_name not in Factory._dynamic_classes: | 88 | if new_class_name not in Factory._dynamic_classes: | ||
| n | 88 | avg_thickness = sum(material.thickness for material in args) / len(args) | n | 89 | avg_thickness = sum(material.THICKNESS for material in args) / len(args) | 
| 89 | new_class = type(new_class_name, (Materials,), {"thickness": avg_thickness}) | 90 | new_class = type(new_class_name, (Material,), {"THICKNESS": avg_thickness}) | ||
| 90 | Factory._dynamic_classes[new_class_name] = new_class | 91 | Factory._dynamic_classes[new_class_name] = new_class | ||
| 91 | 92 | ||||
| 92 | new_class = Factory._dynamic_classes[new_class_name] | 93 | new_class = Factory._dynamic_classes[new_class_name] | ||
| 93 | total_mass = sum(material.mass for material in args) | 94 | total_mass = sum(material.mass for material in args) | ||
| 94 | new_material = new_class(total_mass) | 95 | new_material = new_class(total_mass) | ||
| 95 | 96 | ||||
| 96 | self.current_materials.add(new_material) | 97 | self.current_materials.add(new_material) | ||
| 97 | Factory.all_created_materials.add(new_material) | 98 | Factory.all_created_materials.add(new_material) | ||
| 98 | return new_material | 99 | return new_material | ||
| 99 | 100 | ||||
| 100 | def can_build(self, necessary_volume: int): | 101 | def can_build(self, necessary_volume: int): | ||
| 101 | total_volume = sum( | 102 | total_volume = sum( | ||
| 102 | instance.volume | 103 | instance.volume | ||
| 103 | for instance in self.current_materials | 104 | for instance in self.current_materials | ||
| 104 | if instance not in Factory.used_materials | 105 | if instance not in Factory.used_materials | ||
| 105 | ) | 106 | ) | ||
| 106 | return total_volume >= necessary_volume | 107 | return total_volume >= necessary_volume | ||
| 107 | 108 | ||||
| 108 | @classmethod | 109 | @classmethod | ||
| 109 | def can_build_together(cls, necessary_volume: int): | 110 | def can_build_together(cls, necessary_volume: int): | ||
| 110 | total_volume = sum( | 111 | total_volume = sum( | ||
| 111 | instance.volume | 112 | instance.volume | ||
| 112 | for instance in cls.all_created_materials | 113 | for instance in cls.all_created_materials | ||
| t | 113 | if instance not in cls.used_materials or isinstance(instance, Materials) | t | 114 | if instance not in cls.used_materials or isinstance(instance, Material) | 
| 114 | ) | 115 | ) | ||
| 115 | return total_volume >= necessary_volume | 116 | return total_volume >= necessary_volume | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
 
  | 
              
  |  |||||||||