1class Material:
2 density = None
3
4 def __init__(self, mass):
5 self.mass = mass
6 self.used = False
7
8 @property
9 def volume(self):
10 return self.mass / self.density
11
12
13class Brick(Material):
14 density = 2000
15
16
17class Concrete(Material):
18 density = 2500
19
20
21class Stone(Material):
22 density = 1600
23
24
25class Wood(Material):
26 density = 600
27
28
29class Steel(Material):
30 density = 7700
31
32
33class Factory:
34 current_material_classes = [Brick, Concrete, Stone, Wood, Steel]
35 factories = []
36
37 def __init__(self):
38 self.generated_materials = []
39 self.factories.append(self)
40
41 def __call__(self, *args, **kwargs):
42 self._validate_call_input(args, kwargs)
43 if kwargs:
44 return self._get_result_from_kwargs(kwargs)
45 return self._get_result_from_args(args)
46
47 def can_build(self, volume):
48 return self.generated_materials_volume >= volume
49
50 @classmethod
51 def can_build_together(cls, volume):
52 generated_volume = sum([factory.generated_materials_volume for factory in cls.factories])
53 return generated_volume >= volume
54
55 @property
56 def generated_materials_volume(self):
57 return sum([mat.volume for mat in self.generated_materials if not mat.used])
58
59 def _get_new_instance(self, base_classes, cls_name, mass):
60 new_density = sum([base_class.density for base_class in base_classes]) / len(base_classes)
61 new_cls = type(
62 cls_name,
63 (Material,),
64 {
65 "density": new_density
66 }
67 )
68 self.current_material_classes.append(new_cls)
69 instance = new_cls(mass)
70 self.generated_materials.append(instance)
71 return instance
72
73 @classmethod
74 def __get_material_class_by_name(cls, name):
75 for cur in cls.current_material_classes:
76 if cur.__name__ == name:
77 return cur
78 raise ValueError()
79
80 @staticmethod
81 def _validate_call_input(args, kwargs):
82 if not args and not kwargs:
83 raise ValueError()
84 if args and kwargs:
85 raise ValueError()
86
87 def _get_result_from_kwargs(self, kwargs):
88 result = []
89 for (current_type_name, mass) in kwargs.items():
90 result_class = self.__get_material_class_by_name(current_type_name)
91 instance = result_class(mass)
92 result.append(instance)
93 self.generated_materials.append(instance)
94 return tuple(result)
95
96 def _get_result_from_args(self, args):
97 self._check_for_material_usage(args)
98 base_classes, new_mass = self._get_new_class_props(args)
99 cls_name = "_".join([base.__name__ for base in base_classes])
100 try:
101 result_class = self.__get_material_class_by_name(cls_name)
102 instance = result_class(new_mass)
103 self.generated_materials.append(instance)
104 return instance
105 except ValueError:
106 return self._get_new_instance(base_classes, cls_name, new_mass)
107
108 @staticmethod
109 def _check_for_material_usage(materials):
110 for material_instance in materials:
111 if material_instance.used:
112 raise AssertionError()
113 material_instance.used = True
114
115 @classmethod
116 def _get_new_class_props(cls, args):
117 new_base_classes = []
118 mass = 0
119 for instance in args:
120 for base_class_name in instance.__class__.__name__.split('_'):
121 new_base_classes.append(cls.__get_material_class_by_name(base_class_name))
122 mass += instance.mass
123 new_base_classes.sort(key=lambda obj: obj.__name__)
124 return new_base_classes, mass
.....F....
======================================================================
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 45, in __call__
return self._get_result_from_args(args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 97, in _get_result_from_args
self._check_for_material_usage(args)
File "/tmp/solution.py", line 112, in _check_for_material_usage
raise AssertionError()
AssertionError
----------------------------------------------------------------------
Ran 10 tests in 0.012s
FAILED (failures=1)
Георги Кунчев
21.11.2024 09:38Добре е да организираш методите на класа си в конкретен ред. Няма строги правила кое след кое да е, но е добре да ги групираш.
Събери всички публични неща и ги сложи последни, или първи (не и преди `__init__` разбира се). Останалите неща би било трудно да групираш, защото имаш доста разновидности и може да се окаже, че са подредени по такъв начин, че да трябва да подскачаш нагоре-надолу, докато четеш, но публичните спокойно можеш да отделиш, за да е ясно на хората, които ползват този клас, какви са му публичните интерфейсите, без да ги търсят измежду останалите неща.
|
n | 1 | n | |||
2 | |||||
3 | class Material: | 1 | class Material: | ||
4 | density = None | 2 | density = None | ||
5 | 3 | ||||
6 | def __init__(self, mass): | 4 | def __init__(self, mass): | ||
7 | self.mass = mass | 5 | self.mass = mass | ||
8 | self.used = False | 6 | self.used = False | ||
9 | 7 | ||||
10 | @property | 8 | @property | ||
11 | def volume(self): | 9 | def volume(self): | ||
n | 12 | return float(f"{self.mass / self.density: .2f}") # надявам се това форматиране да искате, че не е обяснено | n | 10 | return self.mass / self.density |
13 | |||||
14 | 11 | ||||
15 | 12 | ||||
16 | class Brick(Material): | 13 | class Brick(Material): | ||
17 | density = 2000 | 14 | density = 2000 | ||
18 | 15 | ||||
19 | 16 | ||||
20 | class Concrete(Material): | 17 | class Concrete(Material): | ||
21 | density = 2500 | 18 | density = 2500 | ||
22 | 19 | ||||
23 | 20 | ||||
24 | class Stone(Material): | 21 | class Stone(Material): | ||
25 | density = 1600 | 22 | density = 1600 | ||
26 | 23 | ||||
27 | 24 | ||||
28 | class Wood(Material): | 25 | class Wood(Material): | ||
29 | density = 600 | 26 | density = 600 | ||
30 | 27 | ||||
31 | 28 | ||||
32 | class Steel(Material): | 29 | class Steel(Material): | ||
33 | density = 7700 | 30 | density = 7700 | ||
34 | 31 | ||||
35 | 32 | ||||
n | 36 | BASE_MATERIALS = (Brick, Concrete, Stone, Wood, Steel) | n | ||
37 | |||||
38 | |||||
39 | class Factory: | 33 | class Factory: | ||
n | 40 | current_material_classes = list(BASE_MATERIALS).copy() | n | 34 | current_material_classes = [Brick, Concrete, Stone, Wood, Steel] |
41 | factories = [] | 35 | factories = [] | ||
42 | 36 | ||||
43 | def __init__(self): | 37 | def __init__(self): | ||
44 | self.generated_materials = [] | 38 | self.generated_materials = [] | ||
n | 45 | self.__class__.factories.append(self) | n | 39 | self.factories.append(self) |
46 | 40 | ||||
47 | def __call__(self, *args, **kwargs): | 41 | def __call__(self, *args, **kwargs): | ||
48 | self._validate_call_input(args, kwargs) | 42 | self._validate_call_input(args, kwargs) | ||
49 | if kwargs: | 43 | if kwargs: | ||
50 | return self._get_result_from_kwargs(kwargs) | 44 | return self._get_result_from_kwargs(kwargs) | ||
n | 51 | return self._get_result_from_args(list(args)) | n | 45 | return self._get_result_from_args(args) |
46 | |||||
47 | def can_build(self, volume): | ||||
48 | return self.generated_materials_volume >= volume | ||||
49 | |||||
50 | @classmethod | ||||
51 | def can_build_together(cls, volume): | ||||
52 | generated_volume = sum([factory.generated_materials_volume for factory in cls.factories]) | ||||
53 | return generated_volume >= volume | ||||
54 | |||||
55 | @property | ||||
56 | def generated_materials_volume(self): | ||||
57 | return sum([mat.volume for mat in self.generated_materials if not mat.used]) | ||||
52 | 58 | ||||
53 | def _get_new_instance(self, base_classes, cls_name, mass): | 59 | def _get_new_instance(self, base_classes, cls_name, mass): | ||
54 | new_density = sum([base_class.density for base_class in base_classes]) / len(base_classes) | 60 | new_density = sum([base_class.density for base_class in base_classes]) / len(base_classes) | ||
55 | new_cls = type( | 61 | new_cls = type( | ||
56 | cls_name, | 62 | cls_name, | ||
57 | (Material,), | 63 | (Material,), | ||
58 | { | 64 | { | ||
59 | "density": new_density | 65 | "density": new_density | ||
60 | } | 66 | } | ||
61 | ) | 67 | ) | ||
n | 62 | self.__class__.current_material_classes.append(new_cls) | n | 68 | self.current_material_classes.append(new_cls) |
63 | instance = new_cls(mass) | 69 | instance = new_cls(mass) | ||
64 | self.generated_materials.append(instance) | 70 | self.generated_materials.append(instance) | ||
65 | return instance | 71 | return instance | ||
66 | 72 | ||||
n | 67 | @property | n | ||
68 | def generated_materials_volume(self): | ||||
69 | return sum([mat.volume for mat in self.generated_materials if not mat.used]) | ||||
70 | |||||
71 | def can_build(self, volume): | ||||
72 | return self.generated_materials_volume >= volume | ||||
73 | |||||
74 | @classmethod | 73 | @classmethod | ||
n | 75 | def can_build_together(cls, volume): | n | ||
76 | generated_volume = sum([factory.generated_materials_volume for factory in cls.factories]) | ||||
77 | return generated_volume >= volume | ||||
78 | |||||
79 | @classmethod | ||||
80 | def get_material_class_by_name(cls, name): | 74 | def __get_material_class_by_name(cls, name): | ||
81 | for cur in cls.current_material_classes: | 75 | for cur in cls.current_material_classes: | ||
82 | if cur.__name__ == name: | 76 | if cur.__name__ == name: | ||
83 | return cur | 77 | return cur | ||
84 | raise ValueError() | 78 | raise ValueError() | ||
85 | 79 | ||||
86 | @staticmethod | 80 | @staticmethod | ||
87 | def _validate_call_input(args, kwargs): | 81 | def _validate_call_input(args, kwargs): | ||
88 | if not args and not kwargs: | 82 | if not args and not kwargs: | ||
89 | raise ValueError() | 83 | raise ValueError() | ||
90 | if args and kwargs: | 84 | if args and kwargs: | ||
91 | raise ValueError() | 85 | raise ValueError() | ||
92 | 86 | ||||
93 | def _get_result_from_kwargs(self, kwargs): | 87 | def _get_result_from_kwargs(self, kwargs): | ||
94 | result = [] | 88 | result = [] | ||
95 | for (current_type_name, mass) in kwargs.items(): | 89 | for (current_type_name, mass) in kwargs.items(): | ||
n | 96 | result_class = self.get_material_class_by_name(current_type_name) | n | 90 | result_class = self.__get_material_class_by_name(current_type_name) |
97 | instance = result_class(mass) | 91 | instance = result_class(mass) | ||
98 | result.append(instance) | 92 | result.append(instance) | ||
99 | self.generated_materials.append(instance) | 93 | self.generated_materials.append(instance) | ||
100 | return tuple(result) | 94 | return tuple(result) | ||
101 | 95 | ||||
102 | def _get_result_from_args(self, args): | 96 | def _get_result_from_args(self, args): | ||
103 | self._check_for_material_usage(args) | 97 | self._check_for_material_usage(args) | ||
104 | base_classes, new_mass = self._get_new_class_props(args) | 98 | base_classes, new_mass = self._get_new_class_props(args) | ||
105 | cls_name = "_".join([base.__name__ for base in base_classes]) | 99 | cls_name = "_".join([base.__name__ for base in base_classes]) | ||
106 | try: | 100 | try: | ||
n | 107 | result_class = self.get_material_class_by_name(cls_name) | n | 101 | result_class = self.__get_material_class_by_name(cls_name) |
108 | instance = result_class(new_mass) | 102 | instance = result_class(new_mass) | ||
109 | self.generated_materials.append(instance) | 103 | self.generated_materials.append(instance) | ||
110 | return instance | 104 | return instance | ||
111 | except ValueError: | 105 | except ValueError: | ||
112 | return self._get_new_instance(base_classes, cls_name, new_mass) | 106 | return self._get_new_instance(base_classes, cls_name, new_mass) | ||
113 | 107 | ||||
114 | @staticmethod | 108 | @staticmethod | ||
115 | def _check_for_material_usage(materials): | 109 | def _check_for_material_usage(materials): | ||
116 | for material_instance in materials: | 110 | for material_instance in materials: | ||
117 | if material_instance.used: | 111 | if material_instance.used: | ||
118 | raise AssertionError() | 112 | raise AssertionError() | ||
119 | material_instance.used = True | 113 | material_instance.used = True | ||
120 | 114 | ||||
n | n | 115 | @classmethod | ||
121 | def _get_new_class_props(self, args): | 116 | def _get_new_class_props(cls, args): | ||
122 | new_base_classes = [] | 117 | new_base_classes = [] | ||
123 | mass = 0 | 118 | mass = 0 | ||
124 | for instance in args: | 119 | for instance in args: | ||
125 | for base_class_name in instance.__class__.__name__.split('_'): | 120 | for base_class_name in instance.__class__.__name__.split('_'): | ||
n | 126 | new_base_classes.append(self.get_material_class_by_name(base_class_name)) | n | 121 | new_base_classes.append(cls.__get_material_class_by_name(base_class_name)) |
127 | mass += instance.mass | 122 | mass += instance.mass | ||
t | 128 | return list(sorted(list(new_base_classes), key=lambda obj: obj.__name__)), mass | t | 123 | new_base_classes.sort(key=lambda obj: obj.__name__) |
129 | 124 | return new_base_classes, mass | |||
130 | |||||
131 | __all__ = [ | ||||
132 | "Brick", "Concrete", "Wood", "Steel", "Stone", "Factory" | ||||
133 | ] |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | f | 1 | ||
2 | 2 | ||||
3 | class Material: | 3 | class Material: | ||
4 | density = None | 4 | density = None | ||
5 | 5 | ||||
6 | def __init__(self, mass): | 6 | def __init__(self, mass): | ||
7 | self.mass = mass | 7 | self.mass = mass | ||
8 | self.used = False | 8 | self.used = False | ||
9 | 9 | ||||
10 | @property | 10 | @property | ||
11 | def volume(self): | 11 | def volume(self): | ||
12 | return float(f"{self.mass / self.density: .2f}") # надявам се това форматиране да искате, че не е обяснено | 12 | return float(f"{self.mass / self.density: .2f}") # надявам се това форматиране да искате, че не е обяснено | ||
13 | 13 | ||||
14 | 14 | ||||
15 | 15 | ||||
16 | class Brick(Material): | 16 | class Brick(Material): | ||
17 | density = 2000 | 17 | density = 2000 | ||
18 | 18 | ||||
19 | 19 | ||||
20 | class Concrete(Material): | 20 | class Concrete(Material): | ||
21 | density = 2500 | 21 | density = 2500 | ||
22 | 22 | ||||
23 | 23 | ||||
24 | class Stone(Material): | 24 | class Stone(Material): | ||
25 | density = 1600 | 25 | density = 1600 | ||
26 | 26 | ||||
27 | 27 | ||||
28 | class Wood(Material): | 28 | class Wood(Material): | ||
29 | density = 600 | 29 | density = 600 | ||
30 | 30 | ||||
31 | 31 | ||||
32 | class Steel(Material): | 32 | class Steel(Material): | ||
33 | density = 7700 | 33 | density = 7700 | ||
34 | 34 | ||||
35 | 35 | ||||
36 | BASE_MATERIALS = (Brick, Concrete, Stone, Wood, Steel) | 36 | BASE_MATERIALS = (Brick, Concrete, Stone, Wood, Steel) | ||
37 | 37 | ||||
38 | 38 | ||||
39 | class Factory: | 39 | class Factory: | ||
40 | current_material_classes = list(BASE_MATERIALS).copy() | 40 | current_material_classes = list(BASE_MATERIALS).copy() | ||
41 | factories = [] | 41 | factories = [] | ||
42 | 42 | ||||
43 | def __init__(self): | 43 | def __init__(self): | ||
44 | self.generated_materials = [] | 44 | self.generated_materials = [] | ||
45 | self.__class__.factories.append(self) | 45 | self.__class__.factories.append(self) | ||
46 | 46 | ||||
47 | def __call__(self, *args, **kwargs): | 47 | def __call__(self, *args, **kwargs): | ||
48 | self._validate_call_input(args, kwargs) | 48 | self._validate_call_input(args, kwargs) | ||
49 | if kwargs: | 49 | if kwargs: | ||
50 | return self._get_result_from_kwargs(kwargs) | 50 | return self._get_result_from_kwargs(kwargs) | ||
51 | return self._get_result_from_args(list(args)) | 51 | return self._get_result_from_args(list(args)) | ||
52 | 52 | ||||
53 | def _get_new_instance(self, base_classes, cls_name, mass): | 53 | def _get_new_instance(self, base_classes, cls_name, mass): | ||
54 | new_density = sum([base_class.density for base_class in base_classes]) / len(base_classes) | 54 | new_density = sum([base_class.density for base_class in base_classes]) / len(base_classes) | ||
55 | new_cls = type( | 55 | new_cls = type( | ||
56 | cls_name, | 56 | cls_name, | ||
57 | (Material,), | 57 | (Material,), | ||
58 | { | 58 | { | ||
59 | "density": new_density | 59 | "density": new_density | ||
60 | } | 60 | } | ||
61 | ) | 61 | ) | ||
62 | self.__class__.current_material_classes.append(new_cls) | 62 | self.__class__.current_material_classes.append(new_cls) | ||
63 | instance = new_cls(mass) | 63 | instance = new_cls(mass) | ||
64 | self.generated_materials.append(instance) | 64 | self.generated_materials.append(instance) | ||
65 | return instance | 65 | return instance | ||
66 | 66 | ||||
67 | @property | 67 | @property | ||
68 | def generated_materials_volume(self): | 68 | def generated_materials_volume(self): | ||
69 | return sum([mat.volume for mat in self.generated_materials if not mat.used]) | 69 | return sum([mat.volume for mat in self.generated_materials if not mat.used]) | ||
70 | 70 | ||||
71 | def can_build(self, volume): | 71 | def can_build(self, volume): | ||
72 | return self.generated_materials_volume >= volume | 72 | return self.generated_materials_volume >= volume | ||
73 | 73 | ||||
74 | @classmethod | 74 | @classmethod | ||
75 | def can_build_together(cls, volume): | 75 | def can_build_together(cls, volume): | ||
76 | generated_volume = sum([factory.generated_materials_volume for factory in cls.factories]) | 76 | generated_volume = sum([factory.generated_materials_volume for factory in cls.factories]) | ||
t | 77 | print(generated_volume) | t | ||
78 | return generated_volume >= volume | 77 | return generated_volume >= volume | ||
79 | 78 | ||||
80 | @classmethod | 79 | @classmethod | ||
81 | def get_material_class_by_name(cls, name): | 80 | def get_material_class_by_name(cls, name): | ||
82 | for cur in cls.current_material_classes: | 81 | for cur in cls.current_material_classes: | ||
83 | if cur.__name__ == name: | 82 | if cur.__name__ == name: | ||
84 | return cur | 83 | return cur | ||
85 | raise ValueError() | 84 | raise ValueError() | ||
86 | 85 | ||||
87 | @staticmethod | 86 | @staticmethod | ||
88 | def _validate_call_input(args, kwargs): | 87 | def _validate_call_input(args, kwargs): | ||
89 | if not args and not kwargs: | 88 | if not args and not kwargs: | ||
90 | raise ValueError() | 89 | raise ValueError() | ||
91 | if args and kwargs: | 90 | if args and kwargs: | ||
92 | raise ValueError() | 91 | raise ValueError() | ||
93 | 92 | ||||
94 | def _get_result_from_kwargs(self, kwargs): | 93 | def _get_result_from_kwargs(self, kwargs): | ||
95 | result = [] | 94 | result = [] | ||
96 | for (current_type_name, mass) in kwargs.items(): | 95 | for (current_type_name, mass) in kwargs.items(): | ||
97 | result_class = self.get_material_class_by_name(current_type_name) | 96 | result_class = self.get_material_class_by_name(current_type_name) | ||
98 | instance = result_class(mass) | 97 | instance = result_class(mass) | ||
99 | result.append(instance) | 98 | result.append(instance) | ||
100 | self.generated_materials.append(instance) | 99 | self.generated_materials.append(instance) | ||
101 | return tuple(result) | 100 | return tuple(result) | ||
102 | 101 | ||||
103 | def _get_result_from_args(self, args): | 102 | def _get_result_from_args(self, args): | ||
104 | self._check_for_material_usage(args) | 103 | self._check_for_material_usage(args) | ||
105 | base_classes, new_mass = self._get_new_class_props(args) | 104 | base_classes, new_mass = self._get_new_class_props(args) | ||
106 | cls_name = "_".join([base.__name__ for base in base_classes]) | 105 | cls_name = "_".join([base.__name__ for base in base_classes]) | ||
107 | try: | 106 | try: | ||
108 | result_class = self.get_material_class_by_name(cls_name) | 107 | result_class = self.get_material_class_by_name(cls_name) | ||
109 | instance = result_class(new_mass) | 108 | instance = result_class(new_mass) | ||
110 | self.generated_materials.append(instance) | 109 | self.generated_materials.append(instance) | ||
111 | return instance | 110 | return instance | ||
112 | except ValueError: | 111 | except ValueError: | ||
113 | return self._get_new_instance(base_classes, cls_name, new_mass) | 112 | return self._get_new_instance(base_classes, cls_name, new_mass) | ||
114 | 113 | ||||
115 | @staticmethod | 114 | @staticmethod | ||
116 | def _check_for_material_usage(materials): | 115 | def _check_for_material_usage(materials): | ||
117 | for material_instance in materials: | 116 | for material_instance in materials: | ||
118 | if material_instance.used: | 117 | if material_instance.used: | ||
119 | raise AssertionError() | 118 | raise AssertionError() | ||
120 | material_instance.used = True | 119 | material_instance.used = True | ||
121 | 120 | ||||
122 | def _get_new_class_props(self, args): | 121 | def _get_new_class_props(self, args): | ||
123 | new_base_classes = [] | 122 | new_base_classes = [] | ||
124 | mass = 0 | 123 | mass = 0 | ||
125 | for instance in args: | 124 | for instance in args: | ||
126 | for base_class_name in instance.__class__.__name__.split('_'): | 125 | for base_class_name in instance.__class__.__name__.split('_'): | ||
127 | new_base_classes.append(self.get_material_class_by_name(base_class_name)) | 126 | new_base_classes.append(self.get_material_class_by_name(base_class_name)) | ||
128 | mass += instance.mass | 127 | mass += instance.mass | ||
129 | return list(sorted(list(new_base_classes), key=lambda obj: obj.__name__)), mass | 128 | return list(sorted(list(new_base_classes), key=lambda obj: obj.__name__)), mass | ||
130 | 129 | ||||
131 | 130 | ||||
132 | __all__ = [ | 131 | __all__ = [ | ||
133 | "Brick", "Concrete", "Wood", "Steel", "Stone", "Factory" | 132 | "Brick", "Concrete", "Wood", "Steel", "Stone", "Factory" | ||
134 | ] | 133 | ] |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
n | n | 1 | |||
2 | |||||
1 | class Material: | 3 | class Material: | ||
2 | density = None | 4 | density = None | ||
n | 3 | _base_materials = [] | n | ||
4 | 5 | ||||
5 | def __init__(self, mass): | 6 | def __init__(self, mass): | ||
6 | self.mass = mass | 7 | self.mass = mass | ||
7 | self.used = False | 8 | self.used = False | ||
8 | 9 | ||||
9 | @property | 10 | @property | ||
10 | def volume(self): | 11 | def volume(self): | ||
11 | return float(f"{self.mass / self.density: .2f}") # надявам се това форматиране да искате, че не е обяснено | 12 | return float(f"{self.mass / self.density: .2f}") # надявам се това форматиране да искате, че не е обяснено | ||
12 | 13 | ||||
n | 13 | @classmethod | n | ||
14 | def get_base_materials(cls): | ||||
15 | if cls in BASE_MATERIALS: | ||||
16 | return cls._base_materials + [cls] | ||||
17 | return cls._base_materials | ||||
18 | 14 | ||||
19 | 15 | ||||
20 | class Brick(Material): | 16 | class Brick(Material): | ||
21 | density = 2000 | 17 | density = 2000 | ||
22 | 18 | ||||
23 | 19 | ||||
24 | class Concrete(Material): | 20 | class Concrete(Material): | ||
25 | density = 2500 | 21 | density = 2500 | ||
26 | 22 | ||||
27 | 23 | ||||
28 | class Stone(Material): | 24 | class Stone(Material): | ||
29 | density = 1600 | 25 | density = 1600 | ||
30 | 26 | ||||
31 | 27 | ||||
32 | class Wood(Material): | 28 | class Wood(Material): | ||
33 | density = 600 | 29 | density = 600 | ||
34 | 30 | ||||
35 | 31 | ||||
36 | class Steel(Material): | 32 | class Steel(Material): | ||
37 | density = 7700 | 33 | density = 7700 | ||
38 | 34 | ||||
39 | 35 | ||||
40 | BASE_MATERIALS = (Brick, Concrete, Stone, Wood, Steel) | 36 | BASE_MATERIALS = (Brick, Concrete, Stone, Wood, Steel) | ||
41 | 37 | ||||
42 | 38 | ||||
43 | class Factory: | 39 | class Factory: | ||
44 | current_material_classes = list(BASE_MATERIALS).copy() | 40 | current_material_classes = list(BASE_MATERIALS).copy() | ||
45 | factories = [] | 41 | factories = [] | ||
46 | 42 | ||||
47 | def __init__(self): | 43 | def __init__(self): | ||
48 | self.generated_materials = [] | 44 | self.generated_materials = [] | ||
49 | self.__class__.factories.append(self) | 45 | self.__class__.factories.append(self) | ||
50 | 46 | ||||
51 | def __call__(self, *args, **kwargs): | 47 | def __call__(self, *args, **kwargs): | ||
52 | self._validate_call_input(args, kwargs) | 48 | self._validate_call_input(args, kwargs) | ||
53 | if kwargs: | 49 | if kwargs: | ||
54 | return self._get_result_from_kwargs(kwargs) | 50 | return self._get_result_from_kwargs(kwargs) | ||
55 | return self._get_result_from_args(list(args)) | 51 | return self._get_result_from_args(list(args)) | ||
56 | 52 | ||||
57 | def _get_new_instance(self, base_classes, cls_name, mass): | 53 | def _get_new_instance(self, base_classes, cls_name, mass): | ||
58 | new_density = sum([base_class.density for base_class in base_classes]) / len(base_classes) | 54 | new_density = sum([base_class.density for base_class in base_classes]) / len(base_classes) | ||
59 | new_cls = type( | 55 | new_cls = type( | ||
60 | cls_name, | 56 | cls_name, | ||
61 | (Material,), | 57 | (Material,), | ||
62 | { | 58 | { | ||
n | 63 | "density": new_density, | n | 59 | "density": new_density |
64 | "_base_materials": base_classes | ||||
65 | } | 60 | } | ||
66 | ) | 61 | ) | ||
67 | self.__class__.current_material_classes.append(new_cls) | 62 | self.__class__.current_material_classes.append(new_cls) | ||
68 | instance = new_cls(mass) | 63 | instance = new_cls(mass) | ||
69 | self.generated_materials.append(instance) | 64 | self.generated_materials.append(instance) | ||
70 | return instance | 65 | return instance | ||
71 | 66 | ||||
72 | @property | 67 | @property | ||
73 | def generated_materials_volume(self): | 68 | def generated_materials_volume(self): | ||
74 | return sum([mat.volume for mat in self.generated_materials if not mat.used]) | 69 | return sum([mat.volume for mat in self.generated_materials if not mat.used]) | ||
75 | 70 | ||||
76 | def can_build(self, volume): | 71 | def can_build(self, volume): | ||
77 | return self.generated_materials_volume >= volume | 72 | return self.generated_materials_volume >= volume | ||
78 | 73 | ||||
79 | @classmethod | 74 | @classmethod | ||
80 | def can_build_together(cls, volume): | 75 | def can_build_together(cls, volume): | ||
81 | generated_volume = sum([factory.generated_materials_volume for factory in cls.factories]) | 76 | generated_volume = sum([factory.generated_materials_volume for factory in cls.factories]) | ||
n | n | 77 | print(generated_volume) | ||
82 | return generated_volume > volume | 78 | return generated_volume >= volume | ||
83 | 79 | ||||
84 | @classmethod | 80 | @classmethod | ||
n | 85 | def get_factory_class_by_name(cls, name): | n | 81 | def get_material_class_by_name(cls, name): |
86 | for cur in cls.current_material_classes: | 82 | for cur in cls.current_material_classes: | ||
87 | if cur.__name__ == name: | 83 | if cur.__name__ == name: | ||
88 | return cur | 84 | return cur | ||
89 | raise ValueError() | 85 | raise ValueError() | ||
90 | 86 | ||||
91 | @staticmethod | 87 | @staticmethod | ||
92 | def _validate_call_input(args, kwargs): | 88 | def _validate_call_input(args, kwargs): | ||
93 | if not args and not kwargs: | 89 | if not args and not kwargs: | ||
94 | raise ValueError() | 90 | raise ValueError() | ||
95 | if args and kwargs: | 91 | if args and kwargs: | ||
96 | raise ValueError() | 92 | raise ValueError() | ||
97 | 93 | ||||
98 | def _get_result_from_kwargs(self, kwargs): | 94 | def _get_result_from_kwargs(self, kwargs): | ||
99 | result = [] | 95 | result = [] | ||
100 | for (current_type_name, mass) in kwargs.items(): | 96 | for (current_type_name, mass) in kwargs.items(): | ||
n | 101 | result_class = self.get_factory_class_by_name(current_type_name) | n | 97 | result_class = self.get_material_class_by_name(current_type_name) |
102 | instance = result_class(mass) | 98 | instance = result_class(mass) | ||
103 | result.append(instance) | 99 | result.append(instance) | ||
104 | self.generated_materials.append(instance) | 100 | self.generated_materials.append(instance) | ||
105 | return tuple(result) | 101 | return tuple(result) | ||
106 | 102 | ||||
107 | def _get_result_from_args(self, args): | 103 | def _get_result_from_args(self, args): | ||
108 | self._check_for_material_usage(args) | 104 | self._check_for_material_usage(args) | ||
109 | base_classes, new_mass = self._get_new_class_props(args) | 105 | base_classes, new_mass = self._get_new_class_props(args) | ||
110 | cls_name = "_".join([base.__name__ for base in base_classes]) | 106 | cls_name = "_".join([base.__name__ for base in base_classes]) | ||
111 | try: | 107 | try: | ||
n | 112 | result_class = self.get_factory_class_by_name(cls_name) | n | 108 | result_class = self.get_material_class_by_name(cls_name) |
113 | instance = result_class(new_mass) | 109 | instance = result_class(new_mass) | ||
114 | self.generated_materials.append(instance) | 110 | self.generated_materials.append(instance) | ||
115 | return instance | 111 | return instance | ||
116 | except ValueError: | 112 | except ValueError: | ||
117 | return self._get_new_instance(base_classes, cls_name, new_mass) | 113 | return self._get_new_instance(base_classes, cls_name, new_mass) | ||
118 | 114 | ||||
119 | @staticmethod | 115 | @staticmethod | ||
120 | def _check_for_material_usage(materials): | 116 | def _check_for_material_usage(materials): | ||
121 | for material_instance in materials: | 117 | for material_instance in materials: | ||
122 | if material_instance.used: | 118 | if material_instance.used: | ||
123 | raise AssertionError() | 119 | raise AssertionError() | ||
124 | material_instance.used = True | 120 | material_instance.used = True | ||
125 | 121 | ||||
n | 126 | @staticmethod | n | ||
127 | def _get_new_class_props(args): | 122 | def _get_new_class_props(self, args): | ||
128 | new_base_classes = set() | 123 | new_base_classes = [] | ||
129 | mass = 0 | 124 | mass = 0 | ||
t | 130 | for cls in args: | t | 125 | for instance in args: |
131 | for base_class in cls.get_base_materials(): | 126 | for base_class_name in instance.__class__.__name__.split('_'): | ||
132 | new_base_classes.add(base_class) | 127 | new_base_classes.append(self.get_material_class_by_name(base_class_name)) | ||
133 | mass += cls.mass | 128 | mass += instance.mass | ||
134 | return list(sorted(list(new_base_classes), key=lambda obj: obj.__name__)), mass | 129 | return list(sorted(list(new_base_classes), key=lambda obj: obj.__name__)), mass | ||
135 | 130 | ||||
136 | 131 | ||||
137 | __all__ = [ | 132 | __all__ = [ | ||
138 | "Brick", "Concrete", "Wood", "Steel", "Stone", "Factory" | 133 | "Brick", "Concrete", "Wood", "Steel", "Stone", "Factory" | ||
139 | ] | 134 | ] |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | class Material: | f | 1 | class Material: |
2 | density = None | 2 | density = None | ||
3 | _base_materials = [] | 3 | _base_materials = [] | ||
4 | 4 | ||||
5 | def __init__(self, mass): | 5 | def __init__(self, mass): | ||
6 | self.mass = mass | 6 | self.mass = mass | ||
7 | self.used = False | 7 | self.used = False | ||
8 | 8 | ||||
9 | @property | 9 | @property | ||
10 | def volume(self): | 10 | def volume(self): | ||
11 | return float(f"{self.mass / self.density: .2f}") # надявам се това форматиране да искате, че не е обяснено | 11 | return float(f"{self.mass / self.density: .2f}") # надявам се това форматиране да искате, че не е обяснено | ||
12 | 12 | ||||
13 | @classmethod | 13 | @classmethod | ||
14 | def get_base_materials(cls): | 14 | def get_base_materials(cls): | ||
15 | if cls in BASE_MATERIALS: | 15 | if cls in BASE_MATERIALS: | ||
16 | return cls._base_materials + [cls] | 16 | return cls._base_materials + [cls] | ||
17 | return cls._base_materials | 17 | return cls._base_materials | ||
18 | 18 | ||||
19 | 19 | ||||
20 | class Brick(Material): | 20 | class Brick(Material): | ||
21 | density = 2000 | 21 | density = 2000 | ||
22 | 22 | ||||
23 | 23 | ||||
24 | class Concrete(Material): | 24 | class Concrete(Material): | ||
25 | density = 2500 | 25 | density = 2500 | ||
26 | 26 | ||||
27 | 27 | ||||
28 | class Stone(Material): | 28 | class Stone(Material): | ||
29 | density = 1600 | 29 | density = 1600 | ||
30 | 30 | ||||
31 | 31 | ||||
32 | class Wood(Material): | 32 | class Wood(Material): | ||
33 | density = 600 | 33 | density = 600 | ||
34 | 34 | ||||
35 | 35 | ||||
36 | class Steel(Material): | 36 | class Steel(Material): | ||
37 | density = 7700 | 37 | density = 7700 | ||
38 | 38 | ||||
39 | 39 | ||||
n | 40 | BASE_MATERIALS = [Brick, Concrete, Stone, Wood, Steel] | n | 40 | BASE_MATERIALS = (Brick, Concrete, Stone, Wood, Steel) |
41 | 41 | ||||
42 | 42 | ||||
43 | class Factory: | 43 | class Factory: | ||
t | 44 | current_material_classes = BASE_MATERIALS.copy() | t | 44 | current_material_classes = list(BASE_MATERIALS).copy() |
45 | factories = [] | 45 | factories = [] | ||
46 | 46 | ||||
47 | def __init__(self): | 47 | def __init__(self): | ||
48 | self.generated_materials = [] | 48 | self.generated_materials = [] | ||
49 | self.__class__.factories.append(self) | 49 | self.__class__.factories.append(self) | ||
50 | 50 | ||||
51 | def __call__(self, *args, **kwargs): | 51 | def __call__(self, *args, **kwargs): | ||
52 | self._validate_call_input(args, kwargs) | 52 | self._validate_call_input(args, kwargs) | ||
53 | if kwargs: | 53 | if kwargs: | ||
54 | return self._get_result_from_kwargs(kwargs) | 54 | return self._get_result_from_kwargs(kwargs) | ||
55 | return self._get_result_from_args(list(args)) | 55 | return self._get_result_from_args(list(args)) | ||
56 | 56 | ||||
57 | def _get_new_instance(self, base_classes, cls_name, mass): | 57 | def _get_new_instance(self, base_classes, cls_name, mass): | ||
58 | new_density = sum([base_class.density for base_class in base_classes]) / len(base_classes) | 58 | new_density = sum([base_class.density for base_class in base_classes]) / len(base_classes) | ||
59 | new_cls = type( | 59 | new_cls = type( | ||
60 | cls_name, | 60 | cls_name, | ||
61 | (Material,), | 61 | (Material,), | ||
62 | { | 62 | { | ||
63 | "density": new_density, | 63 | "density": new_density, | ||
64 | "_base_materials": base_classes | 64 | "_base_materials": base_classes | ||
65 | } | 65 | } | ||
66 | ) | 66 | ) | ||
67 | self.__class__.current_material_classes.append(new_cls) | 67 | self.__class__.current_material_classes.append(new_cls) | ||
68 | instance = new_cls(mass) | 68 | instance = new_cls(mass) | ||
69 | self.generated_materials.append(instance) | 69 | self.generated_materials.append(instance) | ||
70 | return instance | 70 | return instance | ||
71 | 71 | ||||
72 | @property | 72 | @property | ||
73 | def generated_materials_volume(self): | 73 | def generated_materials_volume(self): | ||
74 | return sum([mat.volume for mat in self.generated_materials if not mat.used]) | 74 | return sum([mat.volume for mat in self.generated_materials if not mat.used]) | ||
75 | 75 | ||||
76 | def can_build(self, volume): | 76 | def can_build(self, volume): | ||
77 | return self.generated_materials_volume >= volume | 77 | return self.generated_materials_volume >= volume | ||
78 | 78 | ||||
79 | @classmethod | 79 | @classmethod | ||
80 | def can_build_together(cls, volume): | 80 | def can_build_together(cls, volume): | ||
81 | generated_volume = sum([factory.generated_materials_volume for factory in cls.factories]) | 81 | generated_volume = sum([factory.generated_materials_volume for factory in cls.factories]) | ||
82 | return generated_volume > volume | 82 | return generated_volume > volume | ||
83 | 83 | ||||
84 | @classmethod | 84 | @classmethod | ||
85 | def get_factory_class_by_name(cls, name): | 85 | def get_factory_class_by_name(cls, name): | ||
86 | for cur in cls.current_material_classes: | 86 | for cur in cls.current_material_classes: | ||
87 | if cur.__name__ == name: | 87 | if cur.__name__ == name: | ||
88 | return cur | 88 | return cur | ||
89 | raise ValueError() | 89 | raise ValueError() | ||
90 | 90 | ||||
91 | @staticmethod | 91 | @staticmethod | ||
92 | def _validate_call_input(args, kwargs): | 92 | def _validate_call_input(args, kwargs): | ||
93 | if not args and not kwargs: | 93 | if not args and not kwargs: | ||
94 | raise ValueError() | 94 | raise ValueError() | ||
95 | if args and kwargs: | 95 | if args and kwargs: | ||
96 | raise ValueError() | 96 | raise ValueError() | ||
97 | 97 | ||||
98 | def _get_result_from_kwargs(self, kwargs): | 98 | def _get_result_from_kwargs(self, kwargs): | ||
99 | result = [] | 99 | result = [] | ||
100 | for (current_type_name, mass) in kwargs.items(): | 100 | for (current_type_name, mass) in kwargs.items(): | ||
101 | result_class = self.get_factory_class_by_name(current_type_name) | 101 | result_class = self.get_factory_class_by_name(current_type_name) | ||
102 | instance = result_class(mass) | 102 | instance = result_class(mass) | ||
103 | result.append(instance) | 103 | result.append(instance) | ||
104 | self.generated_materials.append(instance) | 104 | self.generated_materials.append(instance) | ||
105 | return tuple(result) | 105 | return tuple(result) | ||
106 | 106 | ||||
107 | def _get_result_from_args(self, args): | 107 | def _get_result_from_args(self, args): | ||
108 | self._check_for_material_usage(args) | 108 | self._check_for_material_usage(args) | ||
109 | base_classes, new_mass = self._get_new_class_props(args) | 109 | base_classes, new_mass = self._get_new_class_props(args) | ||
110 | cls_name = "_".join([base.__name__ for base in base_classes]) | 110 | cls_name = "_".join([base.__name__ for base in base_classes]) | ||
111 | try: | 111 | try: | ||
112 | result_class = self.get_factory_class_by_name(cls_name) | 112 | result_class = self.get_factory_class_by_name(cls_name) | ||
113 | instance = result_class(new_mass) | 113 | instance = result_class(new_mass) | ||
114 | self.generated_materials.append(instance) | 114 | self.generated_materials.append(instance) | ||
115 | return instance | 115 | return instance | ||
116 | except ValueError: | 116 | except ValueError: | ||
117 | return self._get_new_instance(base_classes, cls_name, new_mass) | 117 | return self._get_new_instance(base_classes, cls_name, new_mass) | ||
118 | 118 | ||||
119 | @staticmethod | 119 | @staticmethod | ||
120 | def _check_for_material_usage(materials): | 120 | def _check_for_material_usage(materials): | ||
121 | for material_instance in materials: | 121 | for material_instance in materials: | ||
122 | if material_instance.used: | 122 | if material_instance.used: | ||
123 | raise AssertionError() | 123 | raise AssertionError() | ||
124 | material_instance.used = True | 124 | material_instance.used = True | ||
125 | 125 | ||||
126 | @staticmethod | 126 | @staticmethod | ||
127 | def _get_new_class_props(args): | 127 | def _get_new_class_props(args): | ||
128 | new_base_classes = set() | 128 | new_base_classes = set() | ||
129 | mass = 0 | 129 | mass = 0 | ||
130 | for cls in args: | 130 | for cls in args: | ||
131 | for base_class in cls.get_base_materials(): | 131 | for base_class in cls.get_base_materials(): | ||
132 | new_base_classes.add(base_class) | 132 | new_base_classes.add(base_class) | ||
133 | mass += cls.mass | 133 | mass += cls.mass | ||
134 | return list(sorted(list(new_base_classes), key=lambda obj: obj.__name__)), mass | 134 | return list(sorted(list(new_base_classes), key=lambda obj: obj.__name__)), mass | ||
135 | 135 | ||||
136 | 136 | ||||
137 | __all__ = [ | 137 | __all__ = [ | ||
138 | "Brick", "Concrete", "Wood", "Steel", "Stone", "Factory" | 138 | "Brick", "Concrete", "Wood", "Steel", "Stone", "Factory" | ||
139 | ] | 139 | ] |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|