1from abc import ABC, abstractmethod
2
3
4class Material(ABC):
5 """Abstract class for materials."""
6 def __init__(self, mass):
7 self.mass = mass
8 self.used = False
9
10 @property
11 @abstractmethod
12 def density(self):
13 """Define density."""
14 pass
15
16 @property
17 def volume(self):
18 """Calculate volume."""
19 return self.mass / self.density
20
21
22class Concrete(Material):
23 DENSITY = 2500
24
25 @property
26 def density(self):
27 return self.DENSITY
28
29
30class Brick(Material):
31 DENSITY = 2000
32
33 @property
34 def density(self):
35 return self.DENSITY
36
37
38class Stone(Material):
39 DENSITY = 1600
40
41 @property
42 def density(self):
43 return self.DENSITY
44
45
46class Wood(Material):
47 DENSITY = 600
48
49 @property
50 def density(self):
51 return self.DENSITY
52
53
54class Steel(Material):
55 DENSITY = 7700
56
57 @property
58 def density(self):
59 return self.DENSITY
60
61
62class Factory:
63 instances = []
64 alloys = {}
65
66 def __init__(self):
67 self.created_materials = set()
68 Factory.instances.append(self)
69
70 def __call__(self, *args, **kwargs):
71 """Handles creation of materials or alloys."""
72 if not args and not kwargs:
73 raise ValueError("Factory must be called with arguments.")
74
75 if args and kwargs:
76 raise ValueError("Cannot mix args and kwargs.")
77
78 if kwargs:
79 return self._create_materials(**kwargs)
80
81 if args:
82 return self._create_alloy(*args)
83
84 def _create_materials(self, **kwargs):
85 """Create materials based on provided keyword arguments."""
86 material_classes = Material.__subclasses__()
87 result = []
88
89 for name, mass in kwargs.items():
90 matching_classes = [cls for cls in material_classes if cls.__name__ == name]
91 if not matching_classes:
92 raise ValueError(f"Invalid material: {name}")
93
94 material_class = matching_classes[0]
95 material_instance = material_class(mass)
96 self.created_materials.add(material_instance)
97 result.append(material_instance)
98
99 return tuple(result)
100
101 def _create_alloy(self, *args):
102 """Create an alloy by combining existing materials."""
103 for material in args:
104 if not isinstance(material, Material):
105 raise ValueError(f"Invalid object: {material}.")
106 if material.used:
107 raise ValueError(f"Material {material.__class__.__name__} has already been used.")
108
109 material_names = sorted(set(arg.__class__.__name__ for arg in args))
110 alloy_class_name = "_".join(material_names)
111
112 if alloy_class_name not in Factory.alloys:
113 densities = [material.density for material in args]
114 average_density = sum(densities) / len(densities)
115
116 Alloy = type(alloy_class_name, (Material,), {
117 'DENSITY': average_density,
118 '__init__': lambda self, mass: super(Alloy, self).__init__(mass),
119 'density': property(lambda self: self.DENSITY),
120 })
121
122 Factory.alloys[alloy_class_name] = Alloy
123
124 for material in args:
125 material.used = True
126
127 total_mass = sum(material.mass for material in args)
128 alloy_class = Factory.alloys[alloy_class_name]
129 alloy_instance = alloy_class(total_mass)
130 self.created_materials.add(alloy_instance)
131 return alloy_instance
132
133 def can_build(self, wall_volume):
134 """Check if this factory can build a wall with the specified volume."""
135 total_volume = sum(material.volume for material in self.created_materials if not material.used)
136 return total_volume >= wall_volume
137
138 @staticmethod
139 def can_build_together(wall_volume):
140 """Check if all factories together can build a wall with the specified volume."""
141 total_volume = sum(
142 material.volume
143 for factory in Factory.instances
144 for material in factory.created_materials
145 if not material.used
146 )
147 return total_volume >= wall_volume
..E..EFF..
======================================================================
ERROR: test_materials_between_factories (test.TestFactory.test_materials_between_factories)
Test materials sharing.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 162, in test_materials_between_factories
self.factory2(wood)
File "/tmp/solution.py", line 82, in __call__
return self._create_alloy(*args)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 107, in _create_alloy
raise ValueError(f"Material {material.__class__.__name__} has already been used.")
ValueError: Material Wood has already been used.
======================================================================
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 102, in test_positional_arguments_multiple_argument_from_initial_set
self.factory1(wood, steel)
File "/tmp/solution.py", line 82, in __call__
return self._create_alloy(*args)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 107, in _create_alloy
raise ValueError(f"Material {material.__class__.__name__} has already been used.")
ValueError: Material Wood 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 128, in test_positional_arguments_multiple_argument_with_dynamics
self.assertEqual(mega_material.__class__.__name__,
AssertionError: 'Brick_Concrete_Stone_Steel_Wood' != 'Brick_Concrete_Steel_Stone_Wood'
- Brick_Concrete_Stone_Steel_Wood
? ------
+ 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 'abc.Concrete'> is not <class 'solution.Concrete'>
----------------------------------------------------------------------
Ran 10 tests in 0.012s
FAILED (failures=2, errors=2)
26.11.2024 16:34
26.11.2024 16:35
26.11.2024 16:36