1from itertools import chain
2
3MATERIALS = {'Concrete': 2500, 'Brick': 2000, 'Stone': 1600, 'Wood': 600, 'Steel': 7700}
4INVALID_MATERIALS = 'invalid_materials'
5CREATED_MATERIALS = 'created_materials'
6
7class Material:
8 def __init__(self, mass, density):
9 self.__mass = mass
10 self.__density = density
11 self.__volume = self.__mass / self.__density
12
13 @property
14 def volume(self):
15 return self.__volume
16
17 def __str__(self):
18 return self.__class__.__name__
19
20class Concrete(Material):
21 def __init__(self, mass):
22 super().__init__(mass, MATERIALS['Concrete'])
23
24class Brick(Material):
25 def __init__(self, mass):
26 super().__init__(mass, MATERIALS['Brick'])
27
28class Stone(Material):
29 def __init__(self, mass):
30 super().__init__(mass, MATERIALS['Stone'])
31
32class Wood(Material):
33 def __init__(self, mass):
34 super().__init__(mass, MATERIALS['Wood'])
35
36class Steel(Material):
37 def __init__(self, mass):
38 super().__init__(mass, MATERIALS['Steel'])
39
40class Factory:
41 __class_materials = {
42 INVALID_MATERIALS: set(),
43 CREATED_MATERIALS: set()
44 }
45
46 def __init__(self):
47 self.__instance_materials = {
48 INVALID_MATERIALS: set(),
49 CREATED_MATERIALS: set()
50 }
51
52 @classmethod
53 def __check_class_materials(cls, args):
54 for arg in args:
55 if arg in cls.__class_materials[INVALID_MATERIALS]:
56 raise AssertionError(f'Already used material: {arg}')
57 cls.__class_materials[INVALID_MATERIALS].update(args)
58
59
60 def __check_instance_materials(self, args):
61 for arg in args:
62 if arg in self.__instance_materials[INVALID_MATERIALS]:
63 raise AssertionError(f'Already used material: {arg}')
64 self.__instance_materials[INVALID_MATERIALS].update(args)
65
66 def can_build(self, volume):
67 for material in self.__instance_materials[CREATED_MATERIALS]:
68 if material not in self.__instance_materials[INVALID_MATERIALS]:
69 volume -= material.volume
70 return volume <= 0
71
72 @classmethod
73 def can_build_together(cls, volume):
74 for material in cls.__class_materials[CREATED_MATERIALS]:
75 if material not in cls.__class_materials[INVALID_MATERIALS]:
76 volume -= material.volume
77 return volume <= 0
78
79 def __call__(self, *args, **kwargs):
80 if not args and not kwargs:
81 raise ValueError('Function should receive some arguments')
82 if args and kwargs:
83 raise ValueError('Function should receive either named args or positional args')
84 if args:
85 self.__check_instance_materials(args)
86 self.__class__.__check_class_materials(args)
87
88 parts = list(chain.from_iterable(chain.from_iterable(
89 [part.split('_') for part in str(arg).split(' ')]
90 for arg in args
91 )))
92
93 class_name = '_'.join(sorted(parts))
94 mass = sum(arg._Material__mass for arg in args)
95
96 if class_name in MATERIALS.keys():
97 density = MATERIALS[class_name]
98
99 created_material = globals()[class_name](mass)
100 self.__instance_materials[CREATED_MATERIALS].add(created_material)
101 self.__class__.__class_materials[CREATED_MATERIALS].add(created_material)
102 return created_material
103 else:
104 density = sum(MATERIALS[part] for part in parts) // len(parts)
105 MATERIALS[class_name] = density
106 class_name = type(
107 class_name,
108 (Material,),
109 {'__init__': lambda self, mass: super(class_name, self).__init__(mass, MATERIALS[class_name.__name__])}
110 )
111 globals()[class_name.__name__] = class_name
112
113 created_material = class_name(mass)
114 self.__instance_materials[CREATED_MATERIALS].add(created_material)
115 self.__class__.__class_materials[CREATED_MATERIALS].add(created_material)
116 return created_material
117 if kwargs:
118 result_materials = []
119 for material, mass in kwargs.items():
120 if material not in MATERIALS.keys():
121 raise ValueError(f'Invalid material: {material}')
122 result_materials.append(globals()[material](mass))
123 self.__instance_materials[CREATED_MATERIALS].update(result_materials)
124 self.__class__.__class_materials[CREATED_MATERIALS].update(result_materials)
125 return tuple(result_materials)
......F...
======================================================================
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 125, in test_positional_arguments_multiple_argument_with_dynamics
self.assertAlmostEqual(brick_concrete_stone.volume, 5.2131, places=4)
AssertionError: 5.213969503197245 != 5.2131 within 4 places (0.0008695031972454359 difference)
----------------------------------------------------------------------
Ran 10 tests in 0.038s
FAILED (failures=1)
Виктор Бечев
23.11.2024 18:05Въпреки броят забележки - в решението ти си личи разбиране за това как работи езика. Идиомите и дизайна - с времето. :)
|
23.11.2024 17:39
23.11.2024 17:40
23.11.2024 17:41
23.11.2024 17:55
23.11.2024 17:43
23.11.2024 21:58
23.11.2024 17:59
23.11.2024 17:58
23.11.2024 18:01
23.11.2024 18:02
23.11.2024 18:03