1CONCRETE_DENSITY = 2500
2BRICK_DENSITY = 2000
3STONE_DENSITY = 1600
4WOOD_DENSITY = 600
5STEEL_DENSITY = 7700
6
7SHARED_CLASSES = {
8 "Concrete": lambda mass: Concrete(mass),
9 "Brick": lambda mass: Brick(mass),
10 "Stone": lambda mass: Stone(mass),
11 "Wood": lambda mass: Wood(mass),
12 "Steel": lambda mass: Steel(mass),
13}
14
15ALL_FACTORIES = []
16
17class Material:
18 def __init__(self, mass: int, density: int):
19 self.mass = mass
20 self._is_valid = True
21 self.base_densities = [density]
22
23 @property
24 def is_valid(self) -> bool:
25 return self._is_valid
26
27 def invalidate(self):
28 self._is_valid = False
29
30 @property
31 def density(self):
32 return sum(self.base_densities) / len(self.base_densities)
33
34 @property
35 def volume(self) -> float:
36 return self.mass / self.density
37
38class Concrete(Material):
39 def __init__(self, mass: int):
40 super().__init__(mass, CONCRETE_DENSITY)
41
42class Brick(Material):
43 def __init__(self, mass: int):
44 super().__init__(mass, BRICK_DENSITY)
45
46class Stone(Material):
47 def __init__(self, mass: int):
48 super().__init__(mass, STONE_DENSITY)
49
50class Wood(Material):
51 def __init__(self, mass: int):
52 super().__init__(mass, WOOD_DENSITY)
53
54class Steel(Material):
55 def __init__(self, mass: int):
56 super().__init__(mass, STEEL_DENSITY)
57
58class Factory:
59 _all_factories = []
60
61 def __init__(self):
62 self.materials = []
63 Factory._all_factories.append(self)
64
65 def __call__(self, *args, **kwargs):
66 if args and kwargs:
67 raise ValueError("Cannot mix positional and keyword arguments.")
68 if not args and not kwargs:
69 raise ValueError("Factory must be called with arguments.")
70 if kwargs:
71 return self._create_from_kwargs(kwargs)
72 if args:
73 return self._create_from_args(args)
74
75 def _create_from_kwargs(self, kwargs):
76 result = []
77 for material_name, mass in kwargs.items():
78 if material_name not in SHARED_CLASSES:
79 raise ValueError(f"Invalid material: {material_name}")
80 material_creator = SHARED_CLASSES[material_name]
81 material = material_creator(mass)
82 self.materials.append(material)
83 result.append(material)
84 return tuple(result)
85
86 def _create_from_args(self, args):
87 for arg in args:
88 assert arg.is_valid, "Material has already been used and is invalid."
89 arg.invalidate()
90 material_classes = sorted(arg.__class__.__name__ for arg in args)
91 class_name = "_".join(material_classes)
92 if class_name in SHARED_CLASSES:
93 alloy_class = SHARED_CLASSES[class_name]
94 else:
95 combined_densities = self._get_combined_densities(args)
96 alloy_class = self._create_alloy_class(class_name, combined_densities)
97 total_mass = sum(arg.mass for arg in args)
98 alloy_instance = alloy_class(total_mass)
99 self.materials.append(alloy_instance)
100 return alloy_instance
101
102 def _get_combined_densities(self, args):
103 combined_densities = []
104 for arg in args:
105 combined_densities.extend(arg.base_densities)
106 return combined_densities
107
108 def _create_alloy_class(self, class_name, combined_densities):
109 average_density = sum(combined_densities) / len(combined_densities)
110 class Alloy(Material):
111 def __init__(self, mass: int):
112 super().__init__(mass, average_density)
113 Alloy.__name__ = class_name
114 SHARED_CLASSES[class_name] = lambda mass: Alloy(mass)
115 return Alloy
116
117 def can_build(self, required_volume: int) -> bool:
118 total_volume = sum(material.volume for material in self.materials if material.is_valid)
119 return total_volume >= required_volume
120
121 @classmethod
122 def can_build_together(cls, required_volume: int) -> bool:
123 total_volume = sum(
124 material.volume
125 for factory in cls._all_factories
126 for material in factory.materials
127 if material.is_valid
128 )
129 return total_volume >= required_volume
.....FF...
======================================================================
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 73, in __call__
return self._create_from_args(args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 88, in _create_from_args
assert arg.is_valid, "Material has already been used and is invalid."
^^^^^^^^^^^^
AssertionError: Material has already been used and is invalid.
======================================================================
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
? ++++++
----------------------------------------------------------------------
Ran 10 tests in 0.016s
FAILED (failures=2)
Димитър Илиев
23.11.2024 14:46Благодаря за препоръките!
|
| f | 1 | CONCRETE_DENSITY = 2500 | f | 1 | CONCRETE_DENSITY = 2500 |
| 2 | BRICK_DENSITY = 2000 | 2 | BRICK_DENSITY = 2000 | ||
| 3 | STONE_DENSITY = 1600 | 3 | STONE_DENSITY = 1600 | ||
| 4 | WOOD_DENSITY = 600 | 4 | WOOD_DENSITY = 600 | ||
| 5 | STEEL_DENSITY = 7700 | 5 | STEEL_DENSITY = 7700 | ||
| 6 | 6 | ||||
| 7 | SHARED_CLASSES = { | 7 | SHARED_CLASSES = { | ||
| 8 | "Concrete": lambda mass: Concrete(mass), | 8 | "Concrete": lambda mass: Concrete(mass), | ||
| 9 | "Brick": lambda mass: Brick(mass), | 9 | "Brick": lambda mass: Brick(mass), | ||
| 10 | "Stone": lambda mass: Stone(mass), | 10 | "Stone": lambda mass: Stone(mass), | ||
| 11 | "Wood": lambda mass: Wood(mass), | 11 | "Wood": lambda mass: Wood(mass), | ||
| 12 | "Steel": lambda mass: Steel(mass), | 12 | "Steel": lambda mass: Steel(mass), | ||
| 13 | } | 13 | } | ||
| 14 | 14 | ||||
| 15 | ALL_FACTORIES = [] | 15 | ALL_FACTORIES = [] | ||
| 16 | 16 | ||||
| n | 17 | n | |||
| 18 | class Material: | 17 | class Material: | ||
| n | 19 | def __init__(self, mass: int, base_densities: list): | n | 18 | def __init__(self, mass: int, density: int): |
| 20 | self.mass = mass | 19 | self.mass = mass | ||
| 21 | self._is_valid = True | 20 | self._is_valid = True | ||
| n | 22 | self.base_densities = base_densities | n | 21 | self.base_densities = [density] |
| 23 | 22 | ||||
| 24 | @property | 23 | @property | ||
| 25 | def is_valid(self) -> bool: | 24 | def is_valid(self) -> bool: | ||
| 26 | return self._is_valid | 25 | return self._is_valid | ||
| 27 | 26 | ||||
| 28 | def invalidate(self): | 27 | def invalidate(self): | ||
| 29 | self._is_valid = False | 28 | self._is_valid = False | ||
| 30 | 29 | ||||
| 31 | @property | 30 | @property | ||
| 32 | def density(self): | 31 | def density(self): | ||
| n | 33 | """Calculate the density based on base densities.""" | n | ||
| 34 | return sum(self.base_densities) / len(self.base_densities) | 32 | return sum(self.base_densities) / len(self.base_densities) | ||
| n | 35 | n | |||
| 36 | |||||
| 37 | class Concrete(Material): | ||||
| 38 | def __init__(self, mass: int): | ||||
| 39 | super().__init__(mass, [CONCRETE_DENSITY]) | ||||
| 40 | 33 | ||||
| 41 | @property | 34 | @property | ||
| 42 | def volume(self) -> float: | 35 | def volume(self) -> float: | ||
| 43 | return self.mass / self.density | 36 | return self.mass / self.density | ||
| 44 | 37 | ||||
| n | n | 38 | class Concrete(Material): | ||
| 39 | def __init__(self, mass: int): | ||||
| 40 | super().__init__(mass, CONCRETE_DENSITY) | ||||
| 45 | 41 | ||||
| 46 | class Brick(Material): | 42 | class Brick(Material): | ||
| 47 | def __init__(self, mass: int): | 43 | def __init__(self, mass: int): | ||
| n | 48 | super().__init__(mass, [BRICK_DENSITY]) | n | 44 | super().__init__(mass, BRICK_DENSITY) |
| 49 | |||||
| 50 | @property | ||||
| 51 | def volume(self) -> float: | ||||
| 52 | return self.mass / self.density | ||||
| 53 | |||||
| 54 | 45 | ||||
| 55 | class Stone(Material): | 46 | class Stone(Material): | ||
| 56 | def __init__(self, mass: int): | 47 | def __init__(self, mass: int): | ||
| n | 57 | super().__init__(mass, [STONE_DENSITY]) | n | 48 | super().__init__(mass, STONE_DENSITY) |
| 58 | |||||
| 59 | @property | ||||
| 60 | def volume(self) -> float: | ||||
| 61 | return self.mass / self.density | ||||
| 62 | |||||
| 63 | 49 | ||||
| 64 | class Wood(Material): | 50 | class Wood(Material): | ||
| 65 | def __init__(self, mass: int): | 51 | def __init__(self, mass: int): | ||
| n | 66 | super().__init__(mass, [WOOD_DENSITY]) | n | 52 | super().__init__(mass, WOOD_DENSITY) |
| 67 | |||||
| 68 | @property | ||||
| 69 | def volume(self) -> float: | ||||
| 70 | return self.mass / self.density | ||||
| 71 | |||||
| 72 | 53 | ||||
| 73 | class Steel(Material): | 54 | class Steel(Material): | ||
| 74 | def __init__(self, mass: int): | 55 | def __init__(self, mass: int): | ||
| n | 75 | super().__init__(mass, [STEEL_DENSITY]) | n | 56 | super().__init__(mass, STEEL_DENSITY) |
| 76 | |||||
| 77 | @property | ||||
| 78 | def volume(self) -> float: | ||||
| 79 | return self.mass / self.density | ||||
| 80 | |||||
| 81 | 57 | ||||
| 82 | class Factory: | 58 | class Factory: | ||
| n | n | 59 | _all_factories = [] | ||
| 60 | |||||
| 83 | def __init__(self): | 61 | def __init__(self): | ||
| 84 | self.materials = [] | 62 | self.materials = [] | ||
| n | 85 | ALL_FACTORIES.append(self) | n | 63 | Factory._all_factories.append(self) |
| 86 | 64 | ||||
| 87 | def __call__(self, *args, **kwargs): | 65 | def __call__(self, *args, **kwargs): | ||
| 88 | if args and kwargs: | 66 | if args and kwargs: | ||
| 89 | raise ValueError("Cannot mix positional and keyword arguments.") | 67 | raise ValueError("Cannot mix positional and keyword arguments.") | ||
| 90 | if not args and not kwargs: | 68 | if not args and not kwargs: | ||
| 91 | raise ValueError("Factory must be called with arguments.") | 69 | raise ValueError("Factory must be called with arguments.") | ||
| n | n | 70 | if kwargs: | ||
| 71 | return self._create_from_kwargs(kwargs) | ||||
| 72 | if args: | ||||
| 73 | return self._create_from_args(args) | ||||
| 92 | 74 | ||||
| n | 93 | if kwargs: | n | 75 | def _create_from_kwargs(self, kwargs): |
| 94 | result = [] | 76 | result = [] | ||
| 95 | for material_name, mass in kwargs.items(): | 77 | for material_name, mass in kwargs.items(): | ||
| 96 | if material_name not in SHARED_CLASSES: | 78 | if material_name not in SHARED_CLASSES: | ||
| 97 | raise ValueError(f"Invalid material: {material_name}") | 79 | raise ValueError(f"Invalid material: {material_name}") | ||
| 98 | material_creator = SHARED_CLASSES[material_name] | 80 | material_creator = SHARED_CLASSES[material_name] | ||
| 99 | material = material_creator(mass) | 81 | material = material_creator(mass) | ||
| 100 | self.materials.append(material) | 82 | self.materials.append(material) | ||
| 101 | result.append(material) | 83 | result.append(material) | ||
| 102 | return tuple(result) if len(result) > 1 else result[0] | 84 | return tuple(result) | ||
| 103 | 85 | ||||
| n | 104 | if args: | n | 86 | def _create_from_args(self, args): |
| 105 | for arg in args: | 87 | for arg in args: | ||
| 106 | assert arg.is_valid, "Material has already been used and is invalid." | 88 | assert arg.is_valid, "Material has already been used and is invalid." | ||
| 107 | arg.invalidate() | 89 | arg.invalidate() | ||
| 90 | material_classes = sorted(arg.__class__.__name__ for arg in args) | ||||
| 91 | class_name = "_".join(material_classes) | ||||
| 92 | if class_name in SHARED_CLASSES: | ||||
| 93 | alloy_class = SHARED_CLASSES[class_name] | ||||
| 94 | else: | ||||
| 95 | combined_densities = self._get_combined_densities(args) | ||||
| 96 | alloy_class = self._create_alloy_class(class_name, combined_densities) | ||||
| 97 | total_mass = sum(arg.mass for arg in args) | ||||
| 98 | alloy_instance = alloy_class(total_mass) | ||||
| 99 | self.materials.append(alloy_instance) | ||||
| 100 | return alloy_instance | ||||
| 108 | 101 | ||||
| n | 109 | material_classes = sorted(arg.__class__.__name__ for arg in args) | n | 102 | def _get_combined_densities(self, args): |
| 110 | class_name = "_".join(material_classes) | 103 | combined_densities = [] | ||
| 104 | for arg in args: | ||||
| 105 | combined_densities.extend(arg.base_densities) | ||||
| 106 | return combined_densities | ||||
| 111 | 107 | ||||
| n | 112 | if class_name in SHARED_CLASSES: | n | 108 | def _create_alloy_class(self, class_name, combined_densities): |
| 113 | alloy_class = SHARED_CLASSES[class_name] | ||||
| 114 | else: | ||||
| 115 | combined_densities = [] | ||||
| 116 | for arg in args: | ||||
| 117 | combined_densities.extend(arg.base_densities) | ||||
| 118 | |||||
| 119 | average_density = sum(combined_densities) / len(combined_densities) | 109 | average_density = sum(combined_densities) / len(combined_densities) | ||
| 120 | |||||
| 121 | class Alloy(Material): | 110 | class Alloy(Material): | ||
| 122 | def __init__(self, mass: int): | 111 | def __init__(self, mass: int): | ||
| 123 | super().__init__(mass, combined_densities) | 112 | super().__init__(mass, average_density) | ||
| 124 | |||||
| 125 | @property | ||||
| 126 | def volume(self) -> float: | ||||
| 127 | return self.mass / self.density | ||||
| 128 | |||||
| 129 | Alloy.__name__ = class_name | 113 | Alloy.__name__ = class_name | ||
| 130 | SHARED_CLASSES[class_name] = lambda mass: Alloy(mass) | 114 | SHARED_CLASSES[class_name] = lambda mass: Alloy(mass) | ||
| 131 | alloy_class = Alloy | 115 | return Alloy | ||
| 132 | |||||
| 133 | total_mass = sum(arg.mass for arg in args) | ||||
| 134 | alloy_instance = alloy_class(total_mass) | ||||
| 135 | self.materials.append(alloy_instance) | ||||
| 136 | return alloy_instance | ||||
| 137 | 116 | ||||
| 138 | def can_build(self, required_volume: int) -> bool: | 117 | def can_build(self, required_volume: int) -> bool: | ||
| 139 | total_volume = sum(material.volume for material in self.materials if material.is_valid) | 118 | total_volume = sum(material.volume for material in self.materials if material.is_valid) | ||
| 140 | return total_volume >= required_volume | 119 | return total_volume >= required_volume | ||
| 141 | 120 | ||||
| 142 | @classmethod | 121 | @classmethod | ||
| 143 | def can_build_together(cls, required_volume: int) -> bool: | 122 | def can_build_together(cls, required_volume: int) -> bool: | ||
| 144 | total_volume = sum( | 123 | total_volume = sum( | ||
| 145 | material.volume | 124 | material.volume | ||
| n | 146 | for factory in ALL_FACTORIES | n | 125 | for factory in cls._all_factories |
| 147 | for material in factory.materials | 126 | for material in factory.materials | ||
| 148 | if material.is_valid | 127 | if material.is_valid | ||
| 149 | ) | 128 | ) | ||
| 150 | return total_volume >= required_volume | 129 | return total_volume >= required_volume | ||
| t | t | 130 |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||