1class Material:
2 def __init__(self, mass, density, materials_count = 1, factory = None):
3 self.mass = mass
4 self.density = density
5 self.materials_count = materials_count
6 self.is_valid = True
7 self.factory = factory
8
9 @property
10 def volume(self):
11 return float(self.mass / self.density)
12
13
14class Concrete(Material):
15 def __init__(self, mass):
16 super().__init__(mass, density=2500)
17
18
19class Brick(Material):
20 def __init__(self, mass):
21 super().__init__(mass, density=2000)
22
23
24class Stone(Material):
25 def __init__(self, mass):
26 super().__init__(mass, density=1600)
27
28
29class Wood(Material):
30 def __init__(self, mass):
31 super().__init__(mass, density=600)
32
33
34class Steel(Material):
35 def __init__(self, mass):
36 super().__init__(mass, density=7700)
37
38class Factory:
39 MATERIALS = {
40 "Concrete": Concrete,
41 "Brick": Brick,
42 "Stone": Stone,
43 "Wood": Wood,
44 "Steel": Steel
45 }
46
47 global_classes = {}
48 all_created_materials = []
49
50 def __init__(self):
51 self.classes = {}
52 self.created_materials = []
53
54 def __call__(self, *args, **kwargs):
55 if args and kwargs:
56 raise ValueError("Unvalid calling of Factory with args and kwargs")
57
58 if not args and not kwargs:
59 raise ValueError("Unvalid calling of factory without any params")
60
61 if args:
62 if not all(isinstance(arg, Material) for arg in args):
63 raise ValueError("All positional arguments must be instances of Material.")
64 if not all(arg.is_valid for arg in args):
65 raise AssertionError("All positional arguments must be valid materials.")
66 # Generate class name
67 material_names = sorted(arg.__class__.__name__ for arg in args)
68 new_material_class_name = "_".join(material_names)
69 new_materials_count = 0
70 # If not already existing - calc avrg density and create new class
71 if new_material_class_name not in Factory.global_classes:
72 new_materials_count = sum(arg.materials_count for arg in args)
73 total_density = sum(arg.density * arg.materials_count for arg in args)
74 average_density = total_density / new_materials_count
75
76 class NewMaterial(Material):
77 def __init__(self, mass):
78 super().__init__(mass=mass, density=average_density, materials_count=new_materials_count)
79
80 NewMaterial.__name__ = new_material_class_name
81 Factory.global_classes[new_material_class_name] = NewMaterial
82 # Mark the materials as already used and return instance of the new class
83 for arg in args:
84 arg.is_valid = False
85 total_mass = sum(arg.mass for arg in args)
86 new_instance = Factory.global_classes[new_material_class_name](total_mass)
87 new_instance.factory = self
88 self.created_materials.append(new_instance)
89 Factory.all_created_materials.append(new_instance)
90 return new_instance
91
92 if kwargs:
93 instances = []
94 for key, value in kwargs.items():
95 if key not in self.MATERIALS and key not in Factory.global_classes:
96 raise ValueError("Invalid material name.")
97 material_class = self.MATERIALS.get(key) or Factory.global_classes[key]
98 instance = material_class(value)
99 instance.factory = self
100 instances.append(instance)
101 self.created_materials.append(instance)
102 Factory.all_created_materials.append(instance)
103 return tuple(instances)
104
105 def can_build(self, required_volume):
106 total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid)
107 return total_volume >= required_volume
108
109 @classmethod
110 def can_build_together(cls, required_volume):
111 total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid)
112 return total_volume >= required_volume
......FF..
======================================================================
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.Factory.__call__.<locals>.NewMaterial'> is not <class 'solution.Concrete'>
----------------------------------------------------------------------
Ran 10 tests in 0.013s
FAILED (failures=2)
| f | 1 | class Material: | f | 1 | class Material: |
| 2 | def __init__(self, mass, density, materials_count = 1, factory = None): | 2 | def __init__(self, mass, density, materials_count = 1, factory = None): | ||
| 3 | self.mass = mass | 3 | self.mass = mass | ||
| 4 | self.density = density | 4 | self.density = density | ||
| 5 | self.materials_count = materials_count | 5 | self.materials_count = materials_count | ||
| 6 | self.is_valid = True | 6 | self.is_valid = True | ||
| n | 7 | self.factory = factory | n | 7 | self.factory = factory |
| 8 | 8 | ||||
| 9 | @property | 9 | @property | ||
| 10 | def volume(self): | 10 | def volume(self): | ||
| 11 | return float(self.mass / self.density) | 11 | return float(self.mass / self.density) | ||
| 12 | 12 | ||||
| 13 | 13 | ||||
| 14 | class Concrete(Material): | 14 | class Concrete(Material): | ||
| 15 | def __init__(self, mass): | 15 | def __init__(self, mass): | ||
| 16 | super().__init__(mass, density=2500) | 16 | super().__init__(mass, density=2500) | ||
| 17 | 17 | ||||
| 18 | 18 | ||||
| 19 | class Brick(Material): | 19 | class Brick(Material): | ||
| 20 | def __init__(self, mass): | 20 | def __init__(self, mass): | ||
| 21 | super().__init__(mass, density=2000) | 21 | super().__init__(mass, density=2000) | ||
| 22 | 22 | ||||
| 23 | 23 | ||||
| 24 | class Stone(Material): | 24 | class Stone(Material): | ||
| 25 | def __init__(self, mass): | 25 | def __init__(self, mass): | ||
| 26 | super().__init__(mass, density=1600) | 26 | super().__init__(mass, density=1600) | ||
| 27 | 27 | ||||
| 28 | 28 | ||||
| 29 | class Wood(Material): | 29 | class Wood(Material): | ||
| 30 | def __init__(self, mass): | 30 | def __init__(self, mass): | ||
| 31 | super().__init__(mass, density=600) | 31 | super().__init__(mass, density=600) | ||
| 32 | 32 | ||||
| 33 | 33 | ||||
| 34 | class Steel(Material): | 34 | class Steel(Material): | ||
| 35 | def __init__(self, mass): | 35 | def __init__(self, mass): | ||
| 36 | super().__init__(mass, density=7700) | 36 | super().__init__(mass, density=7700) | ||
| 37 | 37 | ||||
| 38 | class Factory: | 38 | class Factory: | ||
| 39 | MATERIALS = { | 39 | MATERIALS = { | ||
| 40 | "Concrete": Concrete, | 40 | "Concrete": Concrete, | ||
| 41 | "Brick": Brick, | 41 | "Brick": Brick, | ||
| 42 | "Stone": Stone, | 42 | "Stone": Stone, | ||
| 43 | "Wood": Wood, | 43 | "Wood": Wood, | ||
| 44 | "Steel": Steel | 44 | "Steel": Steel | ||
| 45 | } | 45 | } | ||
| 46 | 46 | ||||
| 47 | global_classes = {} | 47 | global_classes = {} | ||
| 48 | all_created_materials = [] | 48 | all_created_materials = [] | ||
| 49 | 49 | ||||
| 50 | def __init__(self): | 50 | def __init__(self): | ||
| 51 | self.classes = {} | 51 | self.classes = {} | ||
| 52 | self.created_materials = [] | 52 | self.created_materials = [] | ||
| 53 | 53 | ||||
| 54 | def __call__(self, *args, **kwargs): | 54 | def __call__(self, *args, **kwargs): | ||
| 55 | if args and kwargs: | 55 | if args and kwargs: | ||
| 56 | raise ValueError("Unvalid calling of Factory with args and kwargs") | 56 | raise ValueError("Unvalid calling of Factory with args and kwargs") | ||
| 57 | 57 | ||||
| 58 | if not args and not kwargs: | 58 | if not args and not kwargs: | ||
| 59 | raise ValueError("Unvalid calling of factory without any params") | 59 | raise ValueError("Unvalid calling of factory without any params") | ||
| 60 | 60 | ||||
| 61 | if args: | 61 | if args: | ||
| 62 | if not all(isinstance(arg, Material) for arg in args): | 62 | if not all(isinstance(arg, Material) for arg in args): | ||
| 63 | raise ValueError("All positional arguments must be instances of Material.") | 63 | raise ValueError("All positional arguments must be instances of Material.") | ||
| 64 | if not all(arg.is_valid for arg in args): | 64 | if not all(arg.is_valid for arg in args): | ||
| t | 65 | raise AssertionError("All positional arguments must be valid materials.") | t | 65 | raise AssertionError("All positional arguments must be valid materials.") |
| 66 | if not all(arg.factory is self for arg in args): | ||||
| 67 | raise AssertionError("Materials can only be reused in the factory that created them.") | ||||
| 68 | # Generate class name | 66 | # Generate class name | ||
| 69 | material_names = sorted(arg.__class__.__name__ for arg in args) | 67 | material_names = sorted(arg.__class__.__name__ for arg in args) | ||
| 70 | new_material_class_name = "_".join(material_names) | 68 | new_material_class_name = "_".join(material_names) | ||
| 71 | new_materials_count = 0 | 69 | new_materials_count = 0 | ||
| 72 | # If not already existing - calc avrg density and create new class | 70 | # If not already existing - calc avrg density and create new class | ||
| 73 | if new_material_class_name not in Factory.global_classes: | 71 | if new_material_class_name not in Factory.global_classes: | ||
| 74 | new_materials_count = sum(arg.materials_count for arg in args) | 72 | new_materials_count = sum(arg.materials_count for arg in args) | ||
| 75 | total_density = sum(arg.density * arg.materials_count for arg in args) | 73 | total_density = sum(arg.density * arg.materials_count for arg in args) | ||
| 76 | average_density = total_density / new_materials_count | 74 | average_density = total_density / new_materials_count | ||
| 77 | 75 | ||||
| 78 | class NewMaterial(Material): | 76 | class NewMaterial(Material): | ||
| 79 | def __init__(self, mass): | 77 | def __init__(self, mass): | ||
| 80 | super().__init__(mass=mass, density=average_density, materials_count=new_materials_count) | 78 | super().__init__(mass=mass, density=average_density, materials_count=new_materials_count) | ||
| 81 | 79 | ||||
| 82 | NewMaterial.__name__ = new_material_class_name | 80 | NewMaterial.__name__ = new_material_class_name | ||
| 83 | Factory.global_classes[new_material_class_name] = NewMaterial | 81 | Factory.global_classes[new_material_class_name] = NewMaterial | ||
| 84 | # Mark the materials as already used and return instance of the new class | 82 | # Mark the materials as already used and return instance of the new class | ||
| 85 | for arg in args: | 83 | for arg in args: | ||
| 86 | arg.is_valid = False | 84 | arg.is_valid = False | ||
| 87 | total_mass = sum(arg.mass for arg in args) | 85 | total_mass = sum(arg.mass for arg in args) | ||
| 88 | new_instance = Factory.global_classes[new_material_class_name](total_mass) | 86 | new_instance = Factory.global_classes[new_material_class_name](total_mass) | ||
| 89 | new_instance.factory = self | 87 | new_instance.factory = self | ||
| 90 | self.created_materials.append(new_instance) | 88 | self.created_materials.append(new_instance) | ||
| 91 | Factory.all_created_materials.append(new_instance) | 89 | Factory.all_created_materials.append(new_instance) | ||
| 92 | return new_instance | 90 | return new_instance | ||
| 93 | 91 | ||||
| 94 | if kwargs: | 92 | if kwargs: | ||
| 95 | instances = [] | 93 | instances = [] | ||
| 96 | for key, value in kwargs.items(): | 94 | for key, value in kwargs.items(): | ||
| 97 | if key not in self.MATERIALS and key not in Factory.global_classes: | 95 | if key not in self.MATERIALS and key not in Factory.global_classes: | ||
| 98 | raise ValueError("Invalid material name.") | 96 | raise ValueError("Invalid material name.") | ||
| 99 | material_class = self.MATERIALS.get(key) or Factory.global_classes[key] | 97 | material_class = self.MATERIALS.get(key) or Factory.global_classes[key] | ||
| 100 | instance = material_class(value) | 98 | instance = material_class(value) | ||
| 101 | instance.factory = self | 99 | instance.factory = self | ||
| 102 | instances.append(instance) | 100 | instances.append(instance) | ||
| 103 | self.created_materials.append(instance) | 101 | self.created_materials.append(instance) | ||
| 104 | Factory.all_created_materials.append(instance) | 102 | Factory.all_created_materials.append(instance) | ||
| 105 | return tuple(instances) | 103 | return tuple(instances) | ||
| 106 | 104 | ||||
| 107 | def can_build(self, required_volume): | 105 | def can_build(self, required_volume): | ||
| 108 | total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid) | 106 | total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid) | ||
| 109 | return total_volume >= required_volume | 107 | return total_volume >= required_volume | ||
| 110 | 108 | ||||
| 111 | @classmethod | 109 | @classmethod | ||
| 112 | def can_build_together(cls, required_volume): | 110 | def can_build_together(cls, required_volume): | ||
| 113 | total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid) | 111 | total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid) | ||
| 114 | return total_volume >= required_volume | 112 | return total_volume >= required_volume |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | class Material: | f | 1 | class Material: |
| 2 | def __init__(self, mass, density, materials_count = 1, factory = None): | 2 | def __init__(self, mass, density, materials_count = 1, factory = None): | ||
| 3 | self.mass = mass | 3 | self.mass = mass | ||
| 4 | self.density = density | 4 | self.density = density | ||
| 5 | self.materials_count = materials_count | 5 | self.materials_count = materials_count | ||
| 6 | self.is_valid = True | 6 | self.is_valid = True | ||
| 7 | self.factory = factory | 7 | self.factory = factory | ||
| 8 | 8 | ||||
| 9 | @property | 9 | @property | ||
| 10 | def volume(self): | 10 | def volume(self): | ||
| 11 | return float(self.mass / self.density) | 11 | return float(self.mass / self.density) | ||
| 12 | 12 | ||||
| 13 | 13 | ||||
| 14 | class Concrete(Material): | 14 | class Concrete(Material): | ||
| 15 | def __init__(self, mass): | 15 | def __init__(self, mass): | ||
| 16 | super().__init__(mass, density=2500) | 16 | super().__init__(mass, density=2500) | ||
| 17 | 17 | ||||
| 18 | 18 | ||||
| 19 | class Brick(Material): | 19 | class Brick(Material): | ||
| 20 | def __init__(self, mass): | 20 | def __init__(self, mass): | ||
| 21 | super().__init__(mass, density=2000) | 21 | super().__init__(mass, density=2000) | ||
| 22 | 22 | ||||
| 23 | 23 | ||||
| 24 | class Stone(Material): | 24 | class Stone(Material): | ||
| 25 | def __init__(self, mass): | 25 | def __init__(self, mass): | ||
| 26 | super().__init__(mass, density=1600) | 26 | super().__init__(mass, density=1600) | ||
| 27 | 27 | ||||
| 28 | 28 | ||||
| 29 | class Wood(Material): | 29 | class Wood(Material): | ||
| 30 | def __init__(self, mass): | 30 | def __init__(self, mass): | ||
| 31 | super().__init__(mass, density=600) | 31 | super().__init__(mass, density=600) | ||
| 32 | 32 | ||||
| 33 | 33 | ||||
| 34 | class Steel(Material): | 34 | class Steel(Material): | ||
| 35 | def __init__(self, mass): | 35 | def __init__(self, mass): | ||
| 36 | super().__init__(mass, density=7700) | 36 | super().__init__(mass, density=7700) | ||
| 37 | 37 | ||||
| 38 | class Factory: | 38 | class Factory: | ||
| 39 | MATERIALS = { | 39 | MATERIALS = { | ||
| 40 | "Concrete": Concrete, | 40 | "Concrete": Concrete, | ||
| 41 | "Brick": Brick, | 41 | "Brick": Brick, | ||
| 42 | "Stone": Stone, | 42 | "Stone": Stone, | ||
| 43 | "Wood": Wood, | 43 | "Wood": Wood, | ||
| 44 | "Steel": Steel | 44 | "Steel": Steel | ||
| 45 | } | 45 | } | ||
| 46 | 46 | ||||
| 47 | global_classes = {} | 47 | global_classes = {} | ||
| 48 | all_created_materials = [] | 48 | all_created_materials = [] | ||
| 49 | 49 | ||||
| 50 | def __init__(self): | 50 | def __init__(self): | ||
| 51 | self.classes = {} | 51 | self.classes = {} | ||
| 52 | self.created_materials = [] | 52 | self.created_materials = [] | ||
| 53 | 53 | ||||
| 54 | def __call__(self, *args, **kwargs): | 54 | def __call__(self, *args, **kwargs): | ||
| 55 | if args and kwargs: | 55 | if args and kwargs: | ||
| 56 | raise ValueError("Unvalid calling of Factory with args and kwargs") | 56 | raise ValueError("Unvalid calling of Factory with args and kwargs") | ||
| 57 | 57 | ||||
| 58 | if not args and not kwargs: | 58 | if not args and not kwargs: | ||
| 59 | raise ValueError("Unvalid calling of factory without any params") | 59 | raise ValueError("Unvalid calling of factory without any params") | ||
| 60 | 60 | ||||
| 61 | if args: | 61 | if args: | ||
| 62 | if not all(isinstance(arg, Material) for arg in args): | 62 | if not all(isinstance(arg, Material) for arg in args): | ||
| 63 | raise ValueError("All positional arguments must be instances of Material.") | 63 | raise ValueError("All positional arguments must be instances of Material.") | ||
| 64 | if not all(arg.is_valid for arg in args): | 64 | if not all(arg.is_valid for arg in args): | ||
| 65 | raise AssertionError("All positional arguments must be valid materials.") | 65 | raise AssertionError("All positional arguments must be valid materials.") | ||
| 66 | if not all(arg.factory is self for arg in args): | 66 | if not all(arg.factory is self for arg in args): | ||
| 67 | raise AssertionError("Materials can only be reused in the factory that created them.") | 67 | raise AssertionError("Materials can only be reused in the factory that created them.") | ||
| 68 | # Generate class name | 68 | # Generate class name | ||
| 69 | material_names = sorted(arg.__class__.__name__ for arg in args) | 69 | material_names = sorted(arg.__class__.__name__ for arg in args) | ||
| 70 | new_material_class_name = "_".join(material_names) | 70 | new_material_class_name = "_".join(material_names) | ||
| 71 | new_materials_count = 0 | 71 | new_materials_count = 0 | ||
| 72 | # If not already existing - calc avrg density and create new class | 72 | # If not already existing - calc avrg density and create new class | ||
| 73 | if new_material_class_name not in Factory.global_classes: | 73 | if new_material_class_name not in Factory.global_classes: | ||
| 74 | new_materials_count = sum(arg.materials_count for arg in args) | 74 | new_materials_count = sum(arg.materials_count for arg in args) | ||
| 75 | total_density = sum(arg.density * arg.materials_count for arg in args) | 75 | total_density = sum(arg.density * arg.materials_count for arg in args) | ||
| 76 | average_density = total_density / new_materials_count | 76 | average_density = total_density / new_materials_count | ||
| 77 | 77 | ||||
| 78 | class NewMaterial(Material): | 78 | class NewMaterial(Material): | ||
| 79 | def __init__(self, mass): | 79 | def __init__(self, mass): | ||
| 80 | super().__init__(mass=mass, density=average_density, materials_count=new_materials_count) | 80 | super().__init__(mass=mass, density=average_density, materials_count=new_materials_count) | ||
| 81 | 81 | ||||
| 82 | NewMaterial.__name__ = new_material_class_name | 82 | NewMaterial.__name__ = new_material_class_name | ||
| 83 | Factory.global_classes[new_material_class_name] = NewMaterial | 83 | Factory.global_classes[new_material_class_name] = NewMaterial | ||
| 84 | # Mark the materials as already used and return instance of the new class | 84 | # Mark the materials as already used and return instance of the new class | ||
| 85 | for arg in args: | 85 | for arg in args: | ||
| 86 | arg.is_valid = False | 86 | arg.is_valid = False | ||
| 87 | total_mass = sum(arg.mass for arg in args) | 87 | total_mass = sum(arg.mass for arg in args) | ||
| 88 | new_instance = Factory.global_classes[new_material_class_name](total_mass) | 88 | new_instance = Factory.global_classes[new_material_class_name](total_mass) | ||
| 89 | new_instance.factory = self | 89 | new_instance.factory = self | ||
| 90 | self.created_materials.append(new_instance) | 90 | self.created_materials.append(new_instance) | ||
| 91 | Factory.all_created_materials.append(new_instance) | 91 | Factory.all_created_materials.append(new_instance) | ||
| 92 | return new_instance | 92 | return new_instance | ||
| 93 | 93 | ||||
| 94 | if kwargs: | 94 | if kwargs: | ||
| 95 | instances = [] | 95 | instances = [] | ||
| 96 | for key, value in kwargs.items(): | 96 | for key, value in kwargs.items(): | ||
| 97 | if key not in self.MATERIALS and key not in Factory.global_classes: | 97 | if key not in self.MATERIALS and key not in Factory.global_classes: | ||
| 98 | raise ValueError("Invalid material name.") | 98 | raise ValueError("Invalid material name.") | ||
| 99 | material_class = self.MATERIALS.get(key) or Factory.global_classes[key] | 99 | material_class = self.MATERIALS.get(key) or Factory.global_classes[key] | ||
| 100 | instance = material_class(value) | 100 | instance = material_class(value) | ||
| 101 | instance.factory = self | 101 | instance.factory = self | ||
| 102 | instances.append(instance) | 102 | instances.append(instance) | ||
| 103 | self.created_materials.append(instance) | 103 | self.created_materials.append(instance) | ||
| 104 | Factory.all_created_materials.append(instance) | 104 | Factory.all_created_materials.append(instance) | ||
| 105 | return tuple(instances) | 105 | return tuple(instances) | ||
| 106 | 106 | ||||
| 107 | def can_build(self, required_volume): | 107 | def can_build(self, required_volume): | ||
| 108 | total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid) | 108 | total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid) | ||
| 109 | return total_volume >= required_volume | 109 | return total_volume >= required_volume | ||
| t | 110 | t | |||
| 111 | 110 | ||||
| 112 | @classmethod | 111 | @classmethod | ||
| 113 | def can_build_together(cls, required_volume): | 112 | def can_build_together(cls, required_volume): | ||
| 114 | total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid) | 113 | total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid) | ||
| 115 | return total_volume >= required_volume | 114 | return total_volume >= required_volume |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| t | 1 | class Material: | t | 1 | class Material: |
| 2 | def __init__(self, mass, density, materials_count = 1, factory = None): | 2 | def __init__(self, mass, density, materials_count = 1, factory = None): | ||
| 3 | self.mass = mass | 3 | self.mass = mass | ||
| 4 | self.density = density | 4 | self.density = density | ||
| 5 | self.materials_count = materials_count | 5 | self.materials_count = materials_count | ||
| 6 | self.is_valid = True | 6 | self.is_valid = True | ||
| 7 | self.factory = factory | 7 | self.factory = factory | ||
| 8 | 8 | ||||
| 9 | @property | 9 | @property | ||
| 10 | def volume(self): | 10 | def volume(self): | ||
| 11 | return float(self.mass / self.density) | 11 | return float(self.mass / self.density) | ||
| 12 | 12 | ||||
| 13 | 13 | ||||
| 14 | class Concrete(Material): | 14 | class Concrete(Material): | ||
| 15 | def __init__(self, mass): | 15 | def __init__(self, mass): | ||
| 16 | super().__init__(mass, density=2500) | 16 | super().__init__(mass, density=2500) | ||
| 17 | 17 | ||||
| 18 | 18 | ||||
| 19 | class Brick(Material): | 19 | class Brick(Material): | ||
| 20 | def __init__(self, mass): | 20 | def __init__(self, mass): | ||
| 21 | super().__init__(mass, density=2000) | 21 | super().__init__(mass, density=2000) | ||
| 22 | 22 | ||||
| 23 | 23 | ||||
| 24 | class Stone(Material): | 24 | class Stone(Material): | ||
| 25 | def __init__(self, mass): | 25 | def __init__(self, mass): | ||
| 26 | super().__init__(mass, density=1600) | 26 | super().__init__(mass, density=1600) | ||
| 27 | 27 | ||||
| 28 | 28 | ||||
| 29 | class Wood(Material): | 29 | class Wood(Material): | ||
| 30 | def __init__(self, mass): | 30 | def __init__(self, mass): | ||
| 31 | super().__init__(mass, density=600) | 31 | super().__init__(mass, density=600) | ||
| 32 | 32 | ||||
| 33 | 33 | ||||
| 34 | class Steel(Material): | 34 | class Steel(Material): | ||
| 35 | def __init__(self, mass): | 35 | def __init__(self, mass): | ||
| 36 | super().__init__(mass, density=7700) | 36 | super().__init__(mass, density=7700) | ||
| 37 | 37 | ||||
| 38 | class Factory: | 38 | class Factory: | ||
| 39 | MATERIALS = { | 39 | MATERIALS = { | ||
| 40 | "Concrete": Concrete, | 40 | "Concrete": Concrete, | ||
| 41 | "Brick": Brick, | 41 | "Brick": Brick, | ||
| 42 | "Stone": Stone, | 42 | "Stone": Stone, | ||
| 43 | "Wood": Wood, | 43 | "Wood": Wood, | ||
| 44 | "Steel": Steel | 44 | "Steel": Steel | ||
| 45 | } | 45 | } | ||
| 46 | 46 | ||||
| 47 | global_classes = {} | 47 | global_classes = {} | ||
| 48 | all_created_materials = [] | 48 | all_created_materials = [] | ||
| 49 | 49 | ||||
| 50 | def __init__(self): | 50 | def __init__(self): | ||
| 51 | self.classes = {} | 51 | self.classes = {} | ||
| 52 | self.created_materials = [] | 52 | self.created_materials = [] | ||
| 53 | 53 | ||||
| 54 | def __call__(self, *args, **kwargs): | 54 | def __call__(self, *args, **kwargs): | ||
| 55 | if args and kwargs: | 55 | if args and kwargs: | ||
| 56 | raise ValueError("Unvalid calling of Factory with args and kwargs") | 56 | raise ValueError("Unvalid calling of Factory with args and kwargs") | ||
| 57 | 57 | ||||
| 58 | if not args and not kwargs: | 58 | if not args and not kwargs: | ||
| 59 | raise ValueError("Unvalid calling of factory without any params") | 59 | raise ValueError("Unvalid calling of factory without any params") | ||
| 60 | 60 | ||||
| 61 | if args: | 61 | if args: | ||
| 62 | if not all(isinstance(arg, Material) for arg in args): | 62 | if not all(isinstance(arg, Material) for arg in args): | ||
| 63 | raise ValueError("All positional arguments must be instances of Material.") | 63 | raise ValueError("All positional arguments must be instances of Material.") | ||
| 64 | if not all(arg.is_valid for arg in args): | 64 | if not all(arg.is_valid for arg in args): | ||
| 65 | raise AssertionError("All positional arguments must be valid materials.") | 65 | raise AssertionError("All positional arguments must be valid materials.") | ||
| 66 | if not all(arg.factory is self for arg in args): | 66 | if not all(arg.factory is self for arg in args): | ||
| 67 | raise AssertionError("Materials can only be reused in the factory that created them.") | 67 | raise AssertionError("Materials can only be reused in the factory that created them.") | ||
| 68 | # Generate class name | 68 | # Generate class name | ||
| 69 | material_names = sorted(arg.__class__.__name__ for arg in args) | 69 | material_names = sorted(arg.__class__.__name__ for arg in args) | ||
| 70 | new_material_class_name = "_".join(material_names) | 70 | new_material_class_name = "_".join(material_names) | ||
| 71 | new_materials_count = 0 | 71 | new_materials_count = 0 | ||
| 72 | # If not already existing - calc avrg density and create new class | 72 | # If not already existing - calc avrg density and create new class | ||
| 73 | if new_material_class_name not in Factory.global_classes: | 73 | if new_material_class_name not in Factory.global_classes: | ||
| 74 | new_materials_count = sum(arg.materials_count for arg in args) | 74 | new_materials_count = sum(arg.materials_count for arg in args) | ||
| 75 | total_density = sum(arg.density * arg.materials_count for arg in args) | 75 | total_density = sum(arg.density * arg.materials_count for arg in args) | ||
| 76 | average_density = total_density / new_materials_count | 76 | average_density = total_density / new_materials_count | ||
| 77 | 77 | ||||
| 78 | class NewMaterial(Material): | 78 | class NewMaterial(Material): | ||
| 79 | def __init__(self, mass): | 79 | def __init__(self, mass): | ||
| 80 | super().__init__(mass=mass, density=average_density, materials_count=new_materials_count) | 80 | super().__init__(mass=mass, density=average_density, materials_count=new_materials_count) | ||
| 81 | 81 | ||||
| 82 | NewMaterial.__name__ = new_material_class_name | 82 | NewMaterial.__name__ = new_material_class_name | ||
| 83 | Factory.global_classes[new_material_class_name] = NewMaterial | 83 | Factory.global_classes[new_material_class_name] = NewMaterial | ||
| 84 | # Mark the materials as already used and return instance of the new class | 84 | # Mark the materials as already used and return instance of the new class | ||
| 85 | for arg in args: | 85 | for arg in args: | ||
| 86 | arg.is_valid = False | 86 | arg.is_valid = False | ||
| 87 | total_mass = sum(arg.mass for arg in args) | 87 | total_mass = sum(arg.mass for arg in args) | ||
| 88 | new_instance = Factory.global_classes[new_material_class_name](total_mass) | 88 | new_instance = Factory.global_classes[new_material_class_name](total_mass) | ||
| 89 | new_instance.factory = self | 89 | new_instance.factory = self | ||
| 90 | self.created_materials.append(new_instance) | 90 | self.created_materials.append(new_instance) | ||
| 91 | Factory.all_created_materials.append(new_instance) | 91 | Factory.all_created_materials.append(new_instance) | ||
| 92 | return new_instance | 92 | return new_instance | ||
| 93 | 93 | ||||
| 94 | if kwargs: | 94 | if kwargs: | ||
| 95 | instances = [] | 95 | instances = [] | ||
| 96 | for key, value in kwargs.items(): | 96 | for key, value in kwargs.items(): | ||
| 97 | if key not in self.MATERIALS and key not in Factory.global_classes: | 97 | if key not in self.MATERIALS and key not in Factory.global_classes: | ||
| 98 | raise ValueError("Invalid material name.") | 98 | raise ValueError("Invalid material name.") | ||
| 99 | material_class = self.MATERIALS.get(key) or Factory.global_classes[key] | 99 | material_class = self.MATERIALS.get(key) or Factory.global_classes[key] | ||
| 100 | instance = material_class(value) | 100 | instance = material_class(value) | ||
| 101 | instance.factory = self | 101 | instance.factory = self | ||
| 102 | instances.append(instance) | 102 | instances.append(instance) | ||
| 103 | self.created_materials.append(instance) | 103 | self.created_materials.append(instance) | ||
| 104 | Factory.all_created_materials.append(instance) | 104 | Factory.all_created_materials.append(instance) | ||
| 105 | return tuple(instances) | 105 | return tuple(instances) | ||
| 106 | 106 | ||||
| 107 | def can_build(self, required_volume): | 107 | def can_build(self, required_volume): | ||
| 108 | total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid) | 108 | total_volume = sum(obj.volume for obj in self.created_materials if obj.is_valid) | ||
| 109 | return total_volume >= required_volume | 109 | return total_volume >= required_volume | ||
| 110 | 110 | ||||
| 111 | 111 | ||||
| 112 | @classmethod | 112 | @classmethod | ||
| 113 | def can_build_together(cls, required_volume): | 113 | def can_build_together(cls, required_volume): | ||
| 114 | total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid) | 114 | total_volume = sum(obj.volume for obj in cls.all_created_materials if obj.is_valid) | ||
| 115 | return total_volume >= required_volume | 115 | return total_volume >= required_volume |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||