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 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|