1class Material:
2 def __init__(self, mass):
3 self.mass = mass
4 self._used = False
5
6 @property
7 def volume(self):
8 return self.mass / self.density
9
10 def is_used(self):
11 if self._used:
12 raise AssertionError("Material has already been used.")
13 self._used = True
14
15
16class Concrete(Material):
17 density = 2500
18
19
20class Brick(Material):
21 density = 2000
22
23
24class Stone(Material):
25 density = 1600
26
27
28class Wood(Material):
29 density = 600
30
31
32class Steel(Material):
33 density = 7700
34
35
36class Factory:
37 _dynamic_classes = {}
38
39 def __init__(self):
40 self._materials = []
41 self._used_materials = set()
42
43 def __call__(self, *args, **kwargs):
44 if args and kwargs:
45 raise ValueError("Can't mix positional and keyword arguments.")
46 if not args and not kwargs:
47 raise ValueError("Must provide either positional or keyword arguments.")
48
49 if kwargs:
50 return self._handle_named_arguments(kwargs)
51 return self._handle_positional_arguments(args)
52
53 def _handle_named_arguments(self, kwargs):
54 instances = []
55 for name, mass in kwargs.items():
56 cls = self._get_material_class(name)
57 if not cls:
58 raise ValueError(f"Invalid material name: {name}")
59 instance = cls(mass)
60 instances.append(instance)
61 self._materials.append(instance)
62 return tuple(instances)
63
64 def _handle_positional_arguments(self, args):
65 for material in args:
66 material.is_used()
67
68 material_classes = sorted(set(type(arg) for arg in args), key=lambda cls: cls.__name__)
69 class_name = "_".join(cls.__name__ for cls in material_classes)
70 if class_name not in Factory._dynamic_classes:
71 density = sum(cls.density for cls in material_classes) / len(material_classes)
72 Factory._dynamic_classes[class_name] = type(
73 class_name, (Material,), {"density": density}
74 )
75 dynamic_class = Factory._dynamic_classes[class_name]
76 total_mass = sum(arg.mass for arg in args)
77 instance = dynamic_class(total_mass)
78 self._materials.append(instance)
79 return instance
80
81 def _get_material_class(self, name):
82 return globals().get(name, Factory._dynamic_classes.get(name))
83
84 def can_build(self, volume):
85 total_volume = sum(m.volume for m in self._materials if not m._used)
86 return total_volume >= volume
87
88 @classmethod
89 def can_build_together(cls, volume):
90 total_volume = sum(
91 m.volume
92 for factory in Factory._all_instances()
93 for m in factory._materials
94 if not m._used
95 )
96 return total_volume >= volume
97
98 @staticmethod
99 def _all_instances():
100 if not hasattr(Factory, "_instances"):
101 Factory._instances = []
102 return Factory._instances
103
104 def __new__(cls, *args, **kwargs):
105 instance = super().__new__(cls)
106 Factory._all_instances().append(instance)
107 return instance
108
109
.....FFF..
======================================================================
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 51, in __call__
return self._handle_positional_arguments(args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 66, in _handle_positional_arguments
material.is_used()
File "/tmp/solution.py", line 12, in is_used
raise AssertionError("Material has already been used.")
AssertionError: 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 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 'solution.Concrete'> is not <class 'solution.Concrete'>
----------------------------------------------------------------------
Ran 10 tests in 0.011s
FAILED (failures=3)
Виктор Бечев
26.11.2024 17:23Отвъд двете дребни забележки по-горе - решението изглежда добре.
|
26.11.2024 17:14
26.11.2024 17:13