1class BuildingMaterial:
2 """Represents a generic building material."""
3
4 def __init__(self, mass, factory=None):
5 """
6 Initialize a BuildingMaterial instance.
7
8 Args:
9 mass (float): The mass of the material.
10 factory (Factory, optional): The factory that created the material.
11 """
12 self._mass = mass
13 self._used = False
14 self._factory = factory
15
16 @property
17 def mass(self):
18 return self._mass
19
20 @property
21 def factory(self):
22 return self._factory
23
24 @factory.setter
25 def factory(self, other_factory):
26 self._factory = other_factory
27
28 @property
29 def volume(self):
30 """Calculate the volume of the material based on its density."""
31 return self.mass / self.density
32
33
34class Concrete(BuildingMaterial):
35 """Represents concrete with a specific density."""
36
37 density = 2500
38
39
40class Brick(BuildingMaterial):
41 """Represents brick with a specific density."""
42
43 density = 2000
44
45
46class Stone(BuildingMaterial):
47 """Represents stone with a specific density."""
48
49 density = 1600
50
51
52class Wood(BuildingMaterial):
53 """Represents wood with a specific density."""
54
55 density = 600
56
57
58class Steel(BuildingMaterial):
59 """Represents steel with a specific density."""
60
61 density = 7700
62
63
64class Factory:
65 """Represents a factory that produces building materials and alloys."""
66
67 _VALID_MATERIALS = {
68 "Concrete": Concrete,
69 "Brick": Brick,
70 "Stone": Stone,
71 "Wood": Wood,
72 "Steel": Steel
73 }
74
75 _created_alloy_types = {}
76 _total_volume_sum = 0
77
78 def __init__(self):
79 """Initialize a Factory instance."""
80 self._instance_volume_sum = 0
81
82 def _decrease_volume_sums(self, vol):
83 self._instance_volume_sum -= vol
84 Factory._total_volume_sum -= vol
85
86 def _increase_volume_sums(self, vol):
87 self._instance_volume_sum += vol
88 Factory._total_volume_sum += vol
89
90 def _create_single_material(self, material_name, mass):
91 if material_name in Factory._VALID_MATERIALS:
92 return Factory._VALID_MATERIALS[material_name](mass, self)
93 elif material_name in Factory._created_alloy_types:
94 return Factory._created_alloy_types[material_name](mass, self)
95 else:
96 raise ValueError("Keyword arguments must have valid materials as keys!")
97
98 def _create_materials(self, **kwargs):
99 result = [self._create_single_material(key, value) for key, value in kwargs.items()]
100 self._increase_volume_sums(sum(m.volume for m in result))
101 return tuple(result)
102
103 @staticmethod
104 def _generate_alloy_name(materials):
105 names = []
106 for material in set(materials):
107 names.extend(material.__class__.__name__.split("_"))
108 return "_".join(sorted(names))
109
110 @classmethod
111 def _add_new_alloy_type(cls, alloy_name):
112 material_types = [cls._VALID_MATERIALS[type_name] for type_name in alloy_name.split("_")]
113 avg_density = sum(m.density for m in material_types) / len(material_types)
114 cls._created_alloy_types[alloy_name] = type(
115 alloy_name,
116 (BuildingMaterial,),
117 {
118 "density": avg_density,
119 }
120 )
121
122 @staticmethod
123 def _invalidate_materials(materials):
124 for m in materials:
125 m._used = True
126 if m.factory:
127 m.factory._decrease_volume_sums(m.volume)
128 m.factory = None
129
130 def _create_dynamic_alloy(self, *materials):
131 for m in materials:
132 if m._used:
133 raise AssertionError("BuildingMaterial objects can't be reused!")
134
135 alloy_name = Factory._generate_alloy_name(materials)
136 mass_sum = sum(m.mass for m in materials)
137
138 if alloy_name not in Factory._created_alloy_types:
139 Factory._add_new_alloy_type(alloy_name)
140
141 Factory._invalidate_materials(materials)
142
143 alloy = Factory._created_alloy_types[alloy_name](mass_sum, self)
144 self._increase_volume_sums(alloy.volume)
145 return alloy
146
147 @staticmethod
148 def _validate_call_arguments(*args, **kwargs):
149 if args and kwargs:
150 raise ValueError("Factory can't be called with both positional and keyword arguments!")
151 if not args and not kwargs:
152 raise ValueError("Factory object can't be called without arguments!")
153
154 def __call__(self, *args, **kwargs):
155 """
156 Create materials or an alloy using the factory.
157
158 Args:
159 *args: Positional arguments for dynamic alloys.
160 **kwargs: Keyword arguments for creating materials.
161 """
162 Factory._validate_call_arguments(*args, **kwargs)
163 if kwargs:
164 return self._create_materials(**kwargs)
165 elif args:
166 alloy = self._create_dynamic_alloy(*args)
167 return alloy
168
169 def can_build(self, volume_target):
170 """Check if the factory can build with the specified volume."""
171 return self._instance_volume_sum >= volume_target
172
173 @classmethod
174 def can_build_together(cls, volume_target):
175 """Check if all factories combined can build with the specified volume."""
176 return cls._total_volume_sum >= volume_target
.......F..
======================================================================
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.015s
FAILED (failures=1)
Виктор Бечев
23.11.2024 23:08Хубаво разделение на функции и като цяло чист код, браво.
|
| f | 1 | class BuildingMaterial: | f | 1 | class BuildingMaterial: |
| 2 | """Represents a generic building material.""" | 2 | """Represents a generic building material.""" | ||
| 3 | 3 | ||||
| 4 | def __init__(self, mass, factory=None): | 4 | def __init__(self, mass, factory=None): | ||
| 5 | """ | 5 | """ | ||
| 6 | Initialize a BuildingMaterial instance. | 6 | Initialize a BuildingMaterial instance. | ||
| 7 | 7 | ||||
| 8 | Args: | 8 | Args: | ||
| 9 | mass (float): The mass of the material. | 9 | mass (float): The mass of the material. | ||
| 10 | factory (Factory, optional): The factory that created the material. | 10 | factory (Factory, optional): The factory that created the material. | ||
| 11 | """ | 11 | """ | ||
| 12 | self._mass = mass | 12 | self._mass = mass | ||
| 13 | self._used = False | 13 | self._used = False | ||
| n | 14 | self.factory = factory | n | 14 | self._factory = factory |
| 15 | 15 | ||||
| 16 | @property | 16 | @property | ||
| 17 | def mass(self): | 17 | def mass(self): | ||
| 18 | return self._mass | 18 | return self._mass | ||
| t | t | 19 | |||
| 20 | @property | ||||
| 21 | def factory(self): | ||||
| 22 | return self._factory | ||||
| 23 | |||||
| 24 | @factory.setter | ||||
| 25 | def factory(self, other_factory): | ||||
| 26 | self._factory = other_factory | ||||
| 19 | 27 | ||||
| 20 | @property | 28 | @property | ||
| 21 | def volume(self): | 29 | def volume(self): | ||
| 22 | """Calculate the volume of the material based on its density.""" | 30 | """Calculate the volume of the material based on its density.""" | ||
| 23 | return self.mass / self.density | 31 | return self.mass / self.density | ||
| 24 | 32 | ||||
| 25 | 33 | ||||
| 26 | class Concrete(BuildingMaterial): | 34 | class Concrete(BuildingMaterial): | ||
| 27 | """Represents concrete with a specific density.""" | 35 | """Represents concrete with a specific density.""" | ||
| 28 | 36 | ||||
| 29 | density = 2500 | 37 | density = 2500 | ||
| 30 | 38 | ||||
| 31 | 39 | ||||
| 32 | class Brick(BuildingMaterial): | 40 | class Brick(BuildingMaterial): | ||
| 33 | """Represents brick with a specific density.""" | 41 | """Represents brick with a specific density.""" | ||
| 34 | 42 | ||||
| 35 | density = 2000 | 43 | density = 2000 | ||
| 36 | 44 | ||||
| 37 | 45 | ||||
| 38 | class Stone(BuildingMaterial): | 46 | class Stone(BuildingMaterial): | ||
| 39 | """Represents stone with a specific density.""" | 47 | """Represents stone with a specific density.""" | ||
| 40 | 48 | ||||
| 41 | density = 1600 | 49 | density = 1600 | ||
| 42 | 50 | ||||
| 43 | 51 | ||||
| 44 | class Wood(BuildingMaterial): | 52 | class Wood(BuildingMaterial): | ||
| 45 | """Represents wood with a specific density.""" | 53 | """Represents wood with a specific density.""" | ||
| 46 | 54 | ||||
| 47 | density = 600 | 55 | density = 600 | ||
| 48 | 56 | ||||
| 49 | 57 | ||||
| 50 | class Steel(BuildingMaterial): | 58 | class Steel(BuildingMaterial): | ||
| 51 | """Represents steel with a specific density.""" | 59 | """Represents steel with a specific density.""" | ||
| 52 | 60 | ||||
| 53 | density = 7700 | 61 | density = 7700 | ||
| 54 | 62 | ||||
| 55 | 63 | ||||
| 56 | class Factory: | 64 | class Factory: | ||
| 57 | """Represents a factory that produces building materials and alloys.""" | 65 | """Represents a factory that produces building materials and alloys.""" | ||
| 58 | 66 | ||||
| 59 | _VALID_MATERIALS = { | 67 | _VALID_MATERIALS = { | ||
| 60 | "Concrete": Concrete, | 68 | "Concrete": Concrete, | ||
| 61 | "Brick": Brick, | 69 | "Brick": Brick, | ||
| 62 | "Stone": Stone, | 70 | "Stone": Stone, | ||
| 63 | "Wood": Wood, | 71 | "Wood": Wood, | ||
| 64 | "Steel": Steel | 72 | "Steel": Steel | ||
| 65 | } | 73 | } | ||
| 66 | 74 | ||||
| 67 | _created_alloy_types = {} | 75 | _created_alloy_types = {} | ||
| 68 | _total_volume_sum = 0 | 76 | _total_volume_sum = 0 | ||
| 69 | 77 | ||||
| 70 | def __init__(self): | 78 | def __init__(self): | ||
| 71 | """Initialize a Factory instance.""" | 79 | """Initialize a Factory instance.""" | ||
| 72 | self._instance_volume_sum = 0 | 80 | self._instance_volume_sum = 0 | ||
| 73 | 81 | ||||
| 74 | def _decrease_volume_sums(self, vol): | 82 | def _decrease_volume_sums(self, vol): | ||
| 75 | self._instance_volume_sum -= vol | 83 | self._instance_volume_sum -= vol | ||
| 76 | Factory._total_volume_sum -= vol | 84 | Factory._total_volume_sum -= vol | ||
| 77 | 85 | ||||
| 78 | def _increase_volume_sums(self, vol): | 86 | def _increase_volume_sums(self, vol): | ||
| 79 | self._instance_volume_sum += vol | 87 | self._instance_volume_sum += vol | ||
| 80 | Factory._total_volume_sum += vol | 88 | Factory._total_volume_sum += vol | ||
| 81 | 89 | ||||
| 82 | def _create_single_material(self, material_name, mass): | 90 | def _create_single_material(self, material_name, mass): | ||
| 83 | if material_name in Factory._VALID_MATERIALS: | 91 | if material_name in Factory._VALID_MATERIALS: | ||
| 84 | return Factory._VALID_MATERIALS[material_name](mass, self) | 92 | return Factory._VALID_MATERIALS[material_name](mass, self) | ||
| 85 | elif material_name in Factory._created_alloy_types: | 93 | elif material_name in Factory._created_alloy_types: | ||
| 86 | return Factory._created_alloy_types[material_name](mass, self) | 94 | return Factory._created_alloy_types[material_name](mass, self) | ||
| 87 | else: | 95 | else: | ||
| 88 | raise ValueError("Keyword arguments must have valid materials as keys!") | 96 | raise ValueError("Keyword arguments must have valid materials as keys!") | ||
| 89 | 97 | ||||
| 90 | def _create_materials(self, **kwargs): | 98 | def _create_materials(self, **kwargs): | ||
| 91 | result = [self._create_single_material(key, value) for key, value in kwargs.items()] | 99 | result = [self._create_single_material(key, value) for key, value in kwargs.items()] | ||
| 92 | self._increase_volume_sums(sum(m.volume for m in result)) | 100 | self._increase_volume_sums(sum(m.volume for m in result)) | ||
| 93 | return tuple(result) | 101 | return tuple(result) | ||
| 94 | 102 | ||||
| 95 | @staticmethod | 103 | @staticmethod | ||
| 96 | def _generate_alloy_name(materials): | 104 | def _generate_alloy_name(materials): | ||
| 97 | names = [] | 105 | names = [] | ||
| 98 | for material in set(materials): | 106 | for material in set(materials): | ||
| 99 | names.extend(material.__class__.__name__.split("_")) | 107 | names.extend(material.__class__.__name__.split("_")) | ||
| 100 | return "_".join(sorted(names)) | 108 | return "_".join(sorted(names)) | ||
| 101 | 109 | ||||
| 102 | @classmethod | 110 | @classmethod | ||
| 103 | def _add_new_alloy_type(cls, alloy_name): | 111 | def _add_new_alloy_type(cls, alloy_name): | ||
| 104 | material_types = [cls._VALID_MATERIALS[type_name] for type_name in alloy_name.split("_")] | 112 | material_types = [cls._VALID_MATERIALS[type_name] for type_name in alloy_name.split("_")] | ||
| 105 | avg_density = sum(m.density for m in material_types) / len(material_types) | 113 | avg_density = sum(m.density for m in material_types) / len(material_types) | ||
| 106 | cls._created_alloy_types[alloy_name] = type( | 114 | cls._created_alloy_types[alloy_name] = type( | ||
| 107 | alloy_name, | 115 | alloy_name, | ||
| 108 | (BuildingMaterial,), | 116 | (BuildingMaterial,), | ||
| 109 | { | 117 | { | ||
| 110 | "density": avg_density, | 118 | "density": avg_density, | ||
| 111 | } | 119 | } | ||
| 112 | ) | 120 | ) | ||
| 113 | 121 | ||||
| 114 | @staticmethod | 122 | @staticmethod | ||
| 115 | def _invalidate_materials(materials): | 123 | def _invalidate_materials(materials): | ||
| 116 | for m in materials: | 124 | for m in materials: | ||
| 117 | m._used = True | 125 | m._used = True | ||
| 118 | if m.factory: | 126 | if m.factory: | ||
| 119 | m.factory._decrease_volume_sums(m.volume) | 127 | m.factory._decrease_volume_sums(m.volume) | ||
| 120 | m.factory = None | 128 | m.factory = None | ||
| 121 | 129 | ||||
| 122 | def _create_dynamic_alloy(self, *materials): | 130 | def _create_dynamic_alloy(self, *materials): | ||
| 123 | for m in materials: | 131 | for m in materials: | ||
| 124 | if m._used: | 132 | if m._used: | ||
| 125 | raise AssertionError("BuildingMaterial objects can't be reused!") | 133 | raise AssertionError("BuildingMaterial objects can't be reused!") | ||
| 126 | 134 | ||||
| 127 | alloy_name = Factory._generate_alloy_name(materials) | 135 | alloy_name = Factory._generate_alloy_name(materials) | ||
| 128 | mass_sum = sum(m.mass for m in materials) | 136 | mass_sum = sum(m.mass for m in materials) | ||
| 129 | 137 | ||||
| 130 | if alloy_name not in Factory._created_alloy_types: | 138 | if alloy_name not in Factory._created_alloy_types: | ||
| 131 | Factory._add_new_alloy_type(alloy_name) | 139 | Factory._add_new_alloy_type(alloy_name) | ||
| 132 | 140 | ||||
| 133 | Factory._invalidate_materials(materials) | 141 | Factory._invalidate_materials(materials) | ||
| 134 | 142 | ||||
| 135 | alloy = Factory._created_alloy_types[alloy_name](mass_sum, self) | 143 | alloy = Factory._created_alloy_types[alloy_name](mass_sum, self) | ||
| 136 | self._increase_volume_sums(alloy.volume) | 144 | self._increase_volume_sums(alloy.volume) | ||
| 137 | return alloy | 145 | return alloy | ||
| 138 | 146 | ||||
| 139 | @staticmethod | 147 | @staticmethod | ||
| 140 | def _validate_call_arguments(*args, **kwargs): | 148 | def _validate_call_arguments(*args, **kwargs): | ||
| 141 | if args and kwargs: | 149 | if args and kwargs: | ||
| 142 | raise ValueError("Factory can't be called with both positional and keyword arguments!") | 150 | raise ValueError("Factory can't be called with both positional and keyword arguments!") | ||
| 143 | if not args and not kwargs: | 151 | if not args and not kwargs: | ||
| 144 | raise ValueError("Factory object can't be called without arguments!") | 152 | raise ValueError("Factory object can't be called without arguments!") | ||
| 145 | 153 | ||||
| 146 | def __call__(self, *args, **kwargs): | 154 | def __call__(self, *args, **kwargs): | ||
| 147 | """ | 155 | """ | ||
| 148 | Create materials or an alloy using the factory. | 156 | Create materials or an alloy using the factory. | ||
| 149 | 157 | ||||
| 150 | Args: | 158 | Args: | ||
| 151 | *args: Positional arguments for dynamic alloys. | 159 | *args: Positional arguments for dynamic alloys. | ||
| 152 | **kwargs: Keyword arguments for creating materials. | 160 | **kwargs: Keyword arguments for creating materials. | ||
| 153 | """ | 161 | """ | ||
| 154 | Factory._validate_call_arguments(*args, **kwargs) | 162 | Factory._validate_call_arguments(*args, **kwargs) | ||
| 155 | if kwargs: | 163 | if kwargs: | ||
| 156 | return self._create_materials(**kwargs) | 164 | return self._create_materials(**kwargs) | ||
| 157 | elif args: | 165 | elif args: | ||
| 158 | alloy = self._create_dynamic_alloy(*args) | 166 | alloy = self._create_dynamic_alloy(*args) | ||
| 159 | return alloy | 167 | return alloy | ||
| 160 | 168 | ||||
| 161 | def can_build(self, volume_target): | 169 | def can_build(self, volume_target): | ||
| 162 | """Check if the factory can build with the specified volume.""" | 170 | """Check if the factory can build with the specified volume.""" | ||
| 163 | return self._instance_volume_sum >= volume_target | 171 | return self._instance_volume_sum >= volume_target | ||
| 164 | 172 | ||||
| 165 | @classmethod | 173 | @classmethod | ||
| 166 | def can_build_together(cls, volume_target): | 174 | def can_build_together(cls, volume_target): | ||
| 167 | """Check if all factories combined can build with the specified volume.""" | 175 | """Check if all factories combined can build with the specified volume.""" | ||
| 168 | return cls._total_volume_sum >= volume_target | 176 | return cls._total_volume_sum >= volume_target |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | class BuildingMaterial: | f | 1 | class BuildingMaterial: |
| 2 | """Represents a generic building material.""" | 2 | """Represents a generic building material.""" | ||
| 3 | 3 | ||||
| 4 | def __init__(self, mass, factory=None): | 4 | def __init__(self, mass, factory=None): | ||
| 5 | """ | 5 | """ | ||
| 6 | Initialize a BuildingMaterial instance. | 6 | Initialize a BuildingMaterial instance. | ||
| 7 | 7 | ||||
| 8 | Args: | 8 | Args: | ||
| 9 | mass (float): The mass of the material. | 9 | mass (float): The mass of the material. | ||
| 10 | factory (Factory, optional): The factory that created the material. | 10 | factory (Factory, optional): The factory that created the material. | ||
| 11 | """ | 11 | """ | ||
| 12 | self._mass = mass | 12 | self._mass = mass | ||
| 13 | self._used = False | 13 | self._used = False | ||
| n | 14 | self._factory = factory | n | 14 | self.factory = factory |
| 15 | 15 | ||||
| 16 | @property | 16 | @property | ||
| 17 | def mass(self): | 17 | def mass(self): | ||
| 18 | return self._mass | 18 | return self._mass | ||
| 19 | 19 | ||||
| 20 | @property | 20 | @property | ||
| 21 | def volume(self): | 21 | def volume(self): | ||
| 22 | """Calculate the volume of the material based on its density.""" | 22 | """Calculate the volume of the material based on its density.""" | ||
| 23 | return self.mass / self.density | 23 | return self.mass / self.density | ||
| 24 | 24 | ||||
| 25 | 25 | ||||
| 26 | class Concrete(BuildingMaterial): | 26 | class Concrete(BuildingMaterial): | ||
| 27 | """Represents concrete with a specific density.""" | 27 | """Represents concrete with a specific density.""" | ||
| 28 | 28 | ||||
| 29 | density = 2500 | 29 | density = 2500 | ||
| 30 | 30 | ||||
| 31 | 31 | ||||
| 32 | class Brick(BuildingMaterial): | 32 | class Brick(BuildingMaterial): | ||
| 33 | """Represents brick with a specific density.""" | 33 | """Represents brick with a specific density.""" | ||
| 34 | 34 | ||||
| 35 | density = 2000 | 35 | density = 2000 | ||
| 36 | 36 | ||||
| 37 | 37 | ||||
| 38 | class Stone(BuildingMaterial): | 38 | class Stone(BuildingMaterial): | ||
| 39 | """Represents stone with a specific density.""" | 39 | """Represents stone with a specific density.""" | ||
| 40 | 40 | ||||
| 41 | density = 1600 | 41 | density = 1600 | ||
| 42 | 42 | ||||
| 43 | 43 | ||||
| 44 | class Wood(BuildingMaterial): | 44 | class Wood(BuildingMaterial): | ||
| 45 | """Represents wood with a specific density.""" | 45 | """Represents wood with a specific density.""" | ||
| 46 | 46 | ||||
| 47 | density = 600 | 47 | density = 600 | ||
| 48 | 48 | ||||
| 49 | 49 | ||||
| 50 | class Steel(BuildingMaterial): | 50 | class Steel(BuildingMaterial): | ||
| 51 | """Represents steel with a specific density.""" | 51 | """Represents steel with a specific density.""" | ||
| 52 | 52 | ||||
| 53 | density = 7700 | 53 | density = 7700 | ||
| 54 | 54 | ||||
| 55 | 55 | ||||
| 56 | class Factory: | 56 | class Factory: | ||
| 57 | """Represents a factory that produces building materials and alloys.""" | 57 | """Represents a factory that produces building materials and alloys.""" | ||
| 58 | 58 | ||||
| 59 | _VALID_MATERIALS = { | 59 | _VALID_MATERIALS = { | ||
| 60 | "Concrete": Concrete, | 60 | "Concrete": Concrete, | ||
| 61 | "Brick": Brick, | 61 | "Brick": Brick, | ||
| 62 | "Stone": Stone, | 62 | "Stone": Stone, | ||
| 63 | "Wood": Wood, | 63 | "Wood": Wood, | ||
| 64 | "Steel": Steel | 64 | "Steel": Steel | ||
| 65 | } | 65 | } | ||
| 66 | 66 | ||||
| 67 | _created_alloy_types = {} | 67 | _created_alloy_types = {} | ||
| 68 | _total_volume_sum = 0 | 68 | _total_volume_sum = 0 | ||
| 69 | 69 | ||||
| 70 | def __init__(self): | 70 | def __init__(self): | ||
| 71 | """Initialize a Factory instance.""" | 71 | """Initialize a Factory instance.""" | ||
| 72 | self._instance_volume_sum = 0 | 72 | self._instance_volume_sum = 0 | ||
| 73 | 73 | ||||
| 74 | def _decrease_volume_sums(self, vol): | 74 | def _decrease_volume_sums(self, vol): | ||
| 75 | self._instance_volume_sum -= vol | 75 | self._instance_volume_sum -= vol | ||
| 76 | Factory._total_volume_sum -= vol | 76 | Factory._total_volume_sum -= vol | ||
| 77 | 77 | ||||
| 78 | def _increase_volume_sums(self, vol): | 78 | def _increase_volume_sums(self, vol): | ||
| 79 | self._instance_volume_sum += vol | 79 | self._instance_volume_sum += vol | ||
| 80 | Factory._total_volume_sum += vol | 80 | Factory._total_volume_sum += vol | ||
| 81 | 81 | ||||
| 82 | def _create_single_material(self, material_name, mass): | 82 | def _create_single_material(self, material_name, mass): | ||
| 83 | if material_name in Factory._VALID_MATERIALS: | 83 | if material_name in Factory._VALID_MATERIALS: | ||
| 84 | return Factory._VALID_MATERIALS[material_name](mass, self) | 84 | return Factory._VALID_MATERIALS[material_name](mass, self) | ||
| 85 | elif material_name in Factory._created_alloy_types: | 85 | elif material_name in Factory._created_alloy_types: | ||
| 86 | return Factory._created_alloy_types[material_name](mass, self) | 86 | return Factory._created_alloy_types[material_name](mass, self) | ||
| 87 | else: | 87 | else: | ||
| 88 | raise ValueError("Keyword arguments must have valid materials as keys!") | 88 | raise ValueError("Keyword arguments must have valid materials as keys!") | ||
| 89 | 89 | ||||
| 90 | def _create_materials(self, **kwargs): | 90 | def _create_materials(self, **kwargs): | ||
| 91 | result = [self._create_single_material(key, value) for key, value in kwargs.items()] | 91 | result = [self._create_single_material(key, value) for key, value in kwargs.items()] | ||
| n | 92 | self._increase_volume_sums(sum([m.volume for m in result])) | n | 92 | self._increase_volume_sums(sum(m.volume for m in result)) |
| 93 | return tuple(result) | 93 | return tuple(result) | ||
| 94 | 94 | ||||
| 95 | @staticmethod | 95 | @staticmethod | ||
| 96 | def _generate_alloy_name(materials): | 96 | def _generate_alloy_name(materials): | ||
| 97 | names = [] | 97 | names = [] | ||
| 98 | for material in set(materials): | 98 | for material in set(materials): | ||
| 99 | names.extend(material.__class__.__name__.split("_")) | 99 | names.extend(material.__class__.__name__.split("_")) | ||
| 100 | return "_".join(sorted(names)) | 100 | return "_".join(sorted(names)) | ||
| 101 | 101 | ||||
| 102 | @classmethod | 102 | @classmethod | ||
| 103 | def _add_new_alloy_type(cls, alloy_name): | 103 | def _add_new_alloy_type(cls, alloy_name): | ||
| 104 | material_types = [cls._VALID_MATERIALS[type_name] for type_name in alloy_name.split("_")] | 104 | material_types = [cls._VALID_MATERIALS[type_name] for type_name in alloy_name.split("_")] | ||
| 105 | avg_density = sum(m.density for m in material_types) / len(material_types) | 105 | avg_density = sum(m.density for m in material_types) / len(material_types) | ||
| n | 106 | alloy_type = type( | n | 106 | cls._created_alloy_types[alloy_name] = type( |
| 107 | alloy_name, | 107 | alloy_name, | ||
| 108 | (BuildingMaterial,), | 108 | (BuildingMaterial,), | ||
| 109 | { | 109 | { | ||
| n | 110 | "__init__": lambda self, mass, factory: super(alloy_type, self).__init__(mass, factory), | n | ||
| 111 | "density": avg_density, | 110 | "density": avg_density, | ||
| 112 | } | 111 | } | ||
| 113 | ) | 112 | ) | ||
| n | 114 | cls._created_alloy_types[alloy_name] = alloy_type | n | ||
| 115 | 113 | ||||
| 116 | @staticmethod | 114 | @staticmethod | ||
| 117 | def _invalidate_materials(materials): | 115 | def _invalidate_materials(materials): | ||
| 118 | for m in materials: | 116 | for m in materials: | ||
| 119 | m._used = True | 117 | m._used = True | ||
| t | 120 | if m._factory is not None: | t | 118 | if m.factory: |
| 121 | m._factory._decrease_volume_sums(m.volume) | 119 | m.factory._decrease_volume_sums(m.volume) | ||
| 122 | m._factory = None | 120 | m.factory = None | ||
| 123 | 121 | ||||
| 124 | def _create_dynamic_alloy(self, *materials): | 122 | def _create_dynamic_alloy(self, *materials): | ||
| 125 | for m in materials: | 123 | for m in materials: | ||
| 126 | if m._used: | 124 | if m._used: | ||
| 127 | raise AssertionError("BuildingMaterial objects can't be reused!") | 125 | raise AssertionError("BuildingMaterial objects can't be reused!") | ||
| 128 | 126 | ||||
| 129 | alloy_name = Factory._generate_alloy_name(materials) | 127 | alloy_name = Factory._generate_alloy_name(materials) | ||
| 130 | mass_sum = sum(m.mass for m in materials) | 128 | mass_sum = sum(m.mass for m in materials) | ||
| 131 | 129 | ||||
| 132 | if alloy_name not in Factory._created_alloy_types: | 130 | if alloy_name not in Factory._created_alloy_types: | ||
| 133 | Factory._add_new_alloy_type(alloy_name) | 131 | Factory._add_new_alloy_type(alloy_name) | ||
| 134 | 132 | ||||
| 135 | Factory._invalidate_materials(materials) | 133 | Factory._invalidate_materials(materials) | ||
| 136 | 134 | ||||
| 137 | alloy = Factory._created_alloy_types[alloy_name](mass_sum, self) | 135 | alloy = Factory._created_alloy_types[alloy_name](mass_sum, self) | ||
| 138 | self._increase_volume_sums(alloy.volume) | 136 | self._increase_volume_sums(alloy.volume) | ||
| 139 | return alloy | 137 | return alloy | ||
| 140 | 138 | ||||
| 141 | @staticmethod | 139 | @staticmethod | ||
| 142 | def _validate_call_arguments(*args, **kwargs): | 140 | def _validate_call_arguments(*args, **kwargs): | ||
| 143 | if args and kwargs: | 141 | if args and kwargs: | ||
| 144 | raise ValueError("Factory can't be called with both positional and keyword arguments!") | 142 | raise ValueError("Factory can't be called with both positional and keyword arguments!") | ||
| 145 | if not args and not kwargs: | 143 | if not args and not kwargs: | ||
| 146 | raise ValueError("Factory object can't be called without arguments!") | 144 | raise ValueError("Factory object can't be called without arguments!") | ||
| 147 | 145 | ||||
| 148 | def __call__(self, *args, **kwargs): | 146 | def __call__(self, *args, **kwargs): | ||
| 149 | """ | 147 | """ | ||
| 150 | Create materials or an alloy using the factory. | 148 | Create materials or an alloy using the factory. | ||
| 151 | 149 | ||||
| 152 | Args: | 150 | Args: | ||
| 153 | *args: Positional arguments for dynamic alloys. | 151 | *args: Positional arguments for dynamic alloys. | ||
| 154 | **kwargs: Keyword arguments for creating materials. | 152 | **kwargs: Keyword arguments for creating materials. | ||
| 155 | """ | 153 | """ | ||
| 156 | Factory._validate_call_arguments(*args, **kwargs) | 154 | Factory._validate_call_arguments(*args, **kwargs) | ||
| 157 | if kwargs: | 155 | if kwargs: | ||
| 158 | return self._create_materials(**kwargs) | 156 | return self._create_materials(**kwargs) | ||
| 159 | elif args: | 157 | elif args: | ||
| 160 | alloy = self._create_dynamic_alloy(*args) | 158 | alloy = self._create_dynamic_alloy(*args) | ||
| 161 | return alloy | 159 | return alloy | ||
| 162 | 160 | ||||
| 163 | def can_build(self, volume_target): | 161 | def can_build(self, volume_target): | ||
| 164 | """Check if the factory can build with the specified volume.""" | 162 | """Check if the factory can build with the specified volume.""" | ||
| 165 | return self._instance_volume_sum >= volume_target | 163 | return self._instance_volume_sum >= volume_target | ||
| 166 | 164 | ||||
| 167 | @classmethod | 165 | @classmethod | ||
| 168 | def can_build_together(cls, volume_target): | 166 | def can_build_together(cls, volume_target): | ||
| 169 | """Check if all factories combined can build with the specified volume.""" | 167 | """Check if all factories combined can build with the specified volume.""" | ||
| 170 | return cls._total_volume_sum >= volume_target | 168 | return cls._total_volume_sum >= volume_target |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||