1class Material:
2 density = 0
3 def __init__(self, weight):
4 self.weight = weight
5
6 @property
7 def volume(self):
8 return self.weight / self.density
9
10 def _get_class_name(self):
11 return self.__class__.__name__
12
13
14class Concrete(Material):
15 density = 2500
16
17
18class Brick(Material):
19 density = 2000
20
21
22class Stone(Material):
23 density = 1600
24
25
26class Wood(Material):
27 density = 600
28
29
30class Steel(Material):
31 density = 7700
32
33
34class Factory:
35 created_material_classes = {
36 "Brick": Brick,
37 "Concrete": Concrete,
38 "Steel": Steel,
39 "Stone": Stone,
40 "Wood": Wood,
41 }
42
43 used_instances = set()
44 all_instances = set()
45
46 def __init__(self):
47 self.current_instances = set()
48
49 def _get_base_densities(self, args, processed_classes):
50 """Get the densities of the base materials used to create the given instances."""
51 densities = []
52
53 for arg in args:
54 if isinstance(arg, Material):
55 class_name = arg._get_class_name()
56 base_classes = class_name.split("_")
57
58 for base in base_classes:
59 if base not in processed_classes:
60 if base in self.created_material_classes:
61 densities.append(self.created_material_classes[base].density)
62 processed_classes.add(base)
63 else:
64 raise ValueError(f"Unknown material base: {base}")
65 return densities
66
67 def __call__(self, *args, **kwargs):
68 if not args and not kwargs:
69 raise ValueError("Cannot call with no arguments.")
70 if args and kwargs:
71 raise ValueError("Cannot mix positional and keyword arguments.")
72
73 if kwargs:
74 instances = ()
75 for key, value in kwargs.items():
76 if key in self.created_material_classes:
77 material_class = self.created_material_classes[key]
78 instance = material_class(value)
79 instances += (instance,)
80
81 self.current_instances.add(instance)
82 self.all_instances.add(instance)
83 else:
84 raise ValueError(f"Invalid name of keyword argument: {key}")
85 return instances
86
87 if args:
88 for arg in args:
89 if arg in self.used_instances:
90 raise AssertionError(f"Instance {arg} has already been used.")
91 if not any(isinstance(arg, cls) for cls in self.created_material_classes.values()):
92 raise ValueError(f"Invalid material instance: {arg}")
93
94 class_name = "_".join(
95 sorted(
96 set(
97 base_name
98 for arg in args
99 for base_name in arg._get_class_name().split("_")
100 )))
101
102 if class_name in self.created_material_classes:
103 dynamic_class = self.created_material_classes[class_name]
104 else:
105 processed_classes = set()
106 base_densities = self._get_base_densities(args, processed_classes)
107 average_density = sum(base_densities) / len(base_densities)
108
109 dynamic_class = type(
110 class_name,
111 (Material,),
112 {
113 "density": average_density,
114 },
115 )
116
117 self.created_material_classes[class_name] = dynamic_class
118
119 self.used_instances.update(args)
120 total_weight = sum(arg.weight for arg in args)
121 instance = dynamic_class(total_weight)
122
123 self.current_instances.add(instance)
124 self.all_instances.add(instance)
125
126 return instance
127
128 def can_build(self, required_volume):
129 total_volume = sum(
130 instance.volume
131 for instance in self.current_instances
132 if instance not in self.used_instances
133 )
134 return total_volume >= required_volume
135
136 @classmethod
137 def can_build_together(cls, required_volume):
138 total_volume = sum(
139 instance.volume
140 for instance in cls.all_instances
141 if instance not in cls.used_instances
142 )
143 return total_volume >= required_volume
..........
----------------------------------------------------------------------
Ran 10 tests in 0.014s
OK
f | 1 | class Material: | f | 1 | class Material: |
2 | density = 0 | 2 | density = 0 | ||
3 | def __init__(self, weight): | 3 | def __init__(self, weight): | ||
4 | self.weight = weight | 4 | self.weight = weight | ||
5 | 5 | ||||
6 | @property | 6 | @property | ||
7 | def volume(self): | 7 | def volume(self): | ||
8 | return self.weight / self.density | 8 | return self.weight / self.density | ||
9 | 9 | ||||
10 | def _get_class_name(self): | 10 | def _get_class_name(self): | ||
11 | return self.__class__.__name__ | 11 | return self.__class__.__name__ | ||
12 | 12 | ||||
13 | 13 | ||||
14 | class Concrete(Material): | 14 | class Concrete(Material): | ||
15 | density = 2500 | 15 | density = 2500 | ||
16 | 16 | ||||
17 | 17 | ||||
18 | class Brick(Material): | 18 | class Brick(Material): | ||
19 | density = 2000 | 19 | density = 2000 | ||
20 | 20 | ||||
21 | 21 | ||||
22 | class Stone(Material): | 22 | class Stone(Material): | ||
23 | density = 1600 | 23 | density = 1600 | ||
24 | 24 | ||||
25 | 25 | ||||
26 | class Wood(Material): | 26 | class Wood(Material): | ||
27 | density = 600 | 27 | density = 600 | ||
28 | 28 | ||||
29 | 29 | ||||
30 | class Steel(Material): | 30 | class Steel(Material): | ||
31 | density = 7700 | 31 | density = 7700 | ||
32 | 32 | ||||
33 | 33 | ||||
34 | class Factory: | 34 | class Factory: | ||
35 | created_material_classes = { | 35 | created_material_classes = { | ||
36 | "Brick": Brick, | 36 | "Brick": Brick, | ||
37 | "Concrete": Concrete, | 37 | "Concrete": Concrete, | ||
38 | "Steel": Steel, | 38 | "Steel": Steel, | ||
39 | "Stone": Stone, | 39 | "Stone": Stone, | ||
40 | "Wood": Wood, | 40 | "Wood": Wood, | ||
41 | } | 41 | } | ||
42 | 42 | ||||
43 | used_instances = set() | 43 | used_instances = set() | ||
44 | all_instances = set() | 44 | all_instances = set() | ||
t | 45 | #created_classes = {} | t | ||
46 | 45 | ||||
47 | def __init__(self): | 46 | def __init__(self): | ||
48 | self.current_instances = set() | 47 | self.current_instances = set() | ||
49 | 48 | ||||
50 | def _get_base_densities(self, args, processed_classes): | 49 | def _get_base_densities(self, args, processed_classes): | ||
51 | """Get the densities of the base materials used to create the given instances.""" | 50 | """Get the densities of the base materials used to create the given instances.""" | ||
52 | densities = [] | 51 | densities = [] | ||
53 | 52 | ||||
54 | for arg in args: | 53 | for arg in args: | ||
55 | if isinstance(arg, Material): | 54 | if isinstance(arg, Material): | ||
56 | class_name = arg._get_class_name() | 55 | class_name = arg._get_class_name() | ||
57 | base_classes = class_name.split("_") | 56 | base_classes = class_name.split("_") | ||
58 | 57 | ||||
59 | for base in base_classes: | 58 | for base in base_classes: | ||
60 | if base not in processed_classes: | 59 | if base not in processed_classes: | ||
61 | if base in self.created_material_classes: | 60 | if base in self.created_material_classes: | ||
62 | densities.append(self.created_material_classes[base].density) | 61 | densities.append(self.created_material_classes[base].density) | ||
63 | processed_classes.add(base) | 62 | processed_classes.add(base) | ||
64 | else: | 63 | else: | ||
65 | raise ValueError(f"Unknown material base: {base}") | 64 | raise ValueError(f"Unknown material base: {base}") | ||
66 | return densities | 65 | return densities | ||
67 | 66 | ||||
68 | def __call__(self, *args, **kwargs): | 67 | def __call__(self, *args, **kwargs): | ||
69 | if not args and not kwargs: | 68 | if not args and not kwargs: | ||
70 | raise ValueError("Cannot call with no arguments.") | 69 | raise ValueError("Cannot call with no arguments.") | ||
71 | if args and kwargs: | 70 | if args and kwargs: | ||
72 | raise ValueError("Cannot mix positional and keyword arguments.") | 71 | raise ValueError("Cannot mix positional and keyword arguments.") | ||
73 | 72 | ||||
74 | if kwargs: | 73 | if kwargs: | ||
75 | instances = () | 74 | instances = () | ||
76 | for key, value in kwargs.items(): | 75 | for key, value in kwargs.items(): | ||
77 | if key in self.created_material_classes: | 76 | if key in self.created_material_classes: | ||
78 | material_class = self.created_material_classes[key] | 77 | material_class = self.created_material_classes[key] | ||
79 | instance = material_class(value) | 78 | instance = material_class(value) | ||
80 | instances += (instance,) | 79 | instances += (instance,) | ||
81 | 80 | ||||
82 | self.current_instances.add(instance) | 81 | self.current_instances.add(instance) | ||
83 | self.all_instances.add(instance) | 82 | self.all_instances.add(instance) | ||
84 | else: | 83 | else: | ||
85 | raise ValueError(f"Invalid name of keyword argument: {key}") | 84 | raise ValueError(f"Invalid name of keyword argument: {key}") | ||
86 | return instances | 85 | return instances | ||
87 | 86 | ||||
88 | if args: | 87 | if args: | ||
89 | for arg in args: | 88 | for arg in args: | ||
90 | if arg in self.used_instances: | 89 | if arg in self.used_instances: | ||
91 | raise AssertionError(f"Instance {arg} has already been used.") | 90 | raise AssertionError(f"Instance {arg} has already been used.") | ||
92 | if not any(isinstance(arg, cls) for cls in self.created_material_classes.values()): | 91 | if not any(isinstance(arg, cls) for cls in self.created_material_classes.values()): | ||
93 | raise ValueError(f"Invalid material instance: {arg}") | 92 | raise ValueError(f"Invalid material instance: {arg}") | ||
94 | 93 | ||||
95 | class_name = "_".join( | 94 | class_name = "_".join( | ||
96 | sorted( | 95 | sorted( | ||
97 | set( | 96 | set( | ||
98 | base_name | 97 | base_name | ||
99 | for arg in args | 98 | for arg in args | ||
100 | for base_name in arg._get_class_name().split("_") | 99 | for base_name in arg._get_class_name().split("_") | ||
101 | ))) | 100 | ))) | ||
102 | 101 | ||||
103 | if class_name in self.created_material_classes: | 102 | if class_name in self.created_material_classes: | ||
104 | dynamic_class = self.created_material_classes[class_name] | 103 | dynamic_class = self.created_material_classes[class_name] | ||
105 | else: | 104 | else: | ||
106 | processed_classes = set() | 105 | processed_classes = set() | ||
107 | base_densities = self._get_base_densities(args, processed_classes) | 106 | base_densities = self._get_base_densities(args, processed_classes) | ||
108 | average_density = sum(base_densities) / len(base_densities) | 107 | average_density = sum(base_densities) / len(base_densities) | ||
109 | 108 | ||||
110 | dynamic_class = type( | 109 | dynamic_class = type( | ||
111 | class_name, | 110 | class_name, | ||
112 | (Material,), | 111 | (Material,), | ||
113 | { | 112 | { | ||
114 | "density": average_density, | 113 | "density": average_density, | ||
115 | }, | 114 | }, | ||
116 | ) | 115 | ) | ||
117 | 116 | ||||
118 | self.created_material_classes[class_name] = dynamic_class | 117 | self.created_material_classes[class_name] = dynamic_class | ||
119 | 118 | ||||
120 | self.used_instances.update(args) | 119 | self.used_instances.update(args) | ||
121 | total_weight = sum(arg.weight for arg in args) | 120 | total_weight = sum(arg.weight for arg in args) | ||
122 | instance = dynamic_class(total_weight) | 121 | instance = dynamic_class(total_weight) | ||
123 | 122 | ||||
124 | self.current_instances.add(instance) | 123 | self.current_instances.add(instance) | ||
125 | self.all_instances.add(instance) | 124 | self.all_instances.add(instance) | ||
126 | 125 | ||||
127 | return instance | 126 | return instance | ||
128 | 127 | ||||
129 | def can_build(self, required_volume): | 128 | def can_build(self, required_volume): | ||
130 | total_volume = sum( | 129 | total_volume = sum( | ||
131 | instance.volume | 130 | instance.volume | ||
132 | for instance in self.current_instances | 131 | for instance in self.current_instances | ||
133 | if instance not in self.used_instances | 132 | if instance not in self.used_instances | ||
134 | ) | 133 | ) | ||
135 | return total_volume >= required_volume | 134 | return total_volume >= required_volume | ||
136 | 135 | ||||
137 | @classmethod | 136 | @classmethod | ||
138 | def can_build_together(cls, required_volume): | 137 | def can_build_together(cls, required_volume): | ||
139 | total_volume = sum( | 138 | total_volume = sum( | ||
140 | instance.volume | 139 | instance.volume | ||
141 | for instance in cls.all_instances | 140 | for instance in cls.all_instances | ||
142 | if instance not in cls.used_instances | 141 | if instance not in cls.used_instances | ||
143 | ) | 142 | ) | ||
144 | return total_volume >= required_volume | 143 | return total_volume >= required_volume |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
n | 1 | class Concrete: | n | 1 | class Material: |
2 | density = 2500 | 2 | density = 0 | ||
3 | def __init__(self, weight): | ||||
4 | self.weight = weight | ||||
5 | |||||
6 | @property | ||||
7 | def volume(self): | ||||
8 | return self.weight / self.density | ||||
9 | |||||
10 | |||||
11 | class Brick: | ||||
12 | density = 2000 | ||||
13 | def __init__(self, weight): | 3 | def __init__(self, weight): | ||
14 | self.weight = weight | 4 | self.weight = weight | ||
15 | 5 | ||||
16 | @property | 6 | @property | ||
17 | def volume(self): | 7 | def volume(self): | ||
18 | return self.weight / self.density | 8 | return self.weight / self.density | ||
19 | 9 | ||||
n | 20 | n | 10 | def _get_class_name(self): | |
21 | class Stone: | 11 | return self.__class__.__name__ | ||
22 | density = 1600 | ||||
23 | def __init__(self, weight): | ||||
24 | self.weight = weight | ||||
25 | |||||
26 | @property | ||||
27 | def volume(self): | ||||
28 | return self.weight / self.density | ||||
29 | 12 | ||||
30 | 13 | ||||
n | 31 | class Wood: | n | 14 | class Concrete(Material): |
32 | density = 600 | 15 | density = 2500 | ||
33 | def __init__(self, weight): | ||||
34 | self.weight = weight | ||||
35 | 16 | ||||
n | 36 | @property | n | 17 | |
37 | def volume(self): | 18 | class Brick(Material): | ||
38 | return self.weight / self.density | 19 | density = 2000 | ||
39 | 20 | ||||
40 | 21 | ||||
n | 41 | class Steel: | n | 22 | class Stone(Material): |
23 | density = 1600 | ||||
24 | |||||
25 | |||||
26 | class Wood(Material): | ||||
27 | density = 600 | ||||
28 | |||||
29 | |||||
30 | class Steel(Material): | ||||
42 | density = 7700 | 31 | density = 7700 | ||
n | 43 | def __init__(self, weight): | n | ||
44 | self.weight = weight | ||||
45 | |||||
46 | @property | ||||
47 | def volume(self): | ||||
48 | return self.weight / self.density | ||||
49 | 32 | ||||
50 | 33 | ||||
51 | class Factory: | 34 | class Factory: | ||
n | 52 | base_material_classes = { | n | 35 | created_material_classes = { |
53 | "Brick": Brick, | 36 | "Brick": Brick, | ||
54 | "Concrete": Concrete, | 37 | "Concrete": Concrete, | ||
55 | "Steel": Steel, | 38 | "Steel": Steel, | ||
56 | "Stone": Stone, | 39 | "Stone": Stone, | ||
57 | "Wood": Wood, | 40 | "Wood": Wood, | ||
58 | } | 41 | } | ||
59 | 42 | ||||
60 | used_instances = set() | 43 | used_instances = set() | ||
61 | all_instances = set() | 44 | all_instances = set() | ||
n | 62 | created_classes = {} | n | 45 | #created_classes = {} |
63 | 46 | ||||
64 | def __init__(self): | 47 | def __init__(self): | ||
65 | self.current_instances = set() | 48 | self.current_instances = set() | ||
66 | 49 | ||||
n | 67 | def _get_base_class_name(self, instance): | n | ||
68 | """Get the base class name of a material instance.""" | ||||
69 | return instance.__class__.__name__.split("_") | ||||
70 | |||||
71 | def _get_base_densities(self, args, processed_classes): | 50 | def _get_base_densities(self, args, processed_classes): | ||
72 | """Get the densities of the base materials used to create the given instances.""" | 51 | """Get the densities of the base materials used to create the given instances.""" | ||
73 | densities = [] | 52 | densities = [] | ||
n | n | 53 | |||
74 | for arg in args: | 54 | for arg in args: | ||
n | 75 | if isinstance(arg, tuple(Factory.base_material_classes.values())): | n | 55 | if isinstance(arg, Material): |
76 | base_name = arg.__class__.__name__ | ||||
77 | if base_name not in processed_classes: | ||||
78 | densities.append(arg.density) | ||||
79 | processed_classes.add(base_name) | ||||
80 | elif isinstance(arg.__class__, type): | ||||
81 | class_name = arg.__class__.__name__ | 56 | class_name = arg._get_class_name() | ||
82 | base_classes = class_name.split("_") | 57 | base_classes = class_name.split("_") | ||
n | n | 58 | |||
83 | for base in base_classes: | 59 | for base in base_classes: | ||
84 | if base not in processed_classes: | 60 | if base not in processed_classes: | ||
n | n | 61 | if base in self.created_material_classes: | ||
85 | densities.append(Factory.base_material_classes[base].density) | 62 | densities.append(self.created_material_classes[base].density) | ||
86 | processed_classes.add(base) | 63 | processed_classes.add(base) | ||
64 | else: | ||||
65 | raise ValueError(f"Unknown material base: {base}") | ||||
87 | return densities | 66 | return densities | ||
88 | 67 | ||||
89 | def __call__(self, *args, **kwargs): | 68 | def __call__(self, *args, **kwargs): | ||
90 | if not args and not kwargs: | 69 | if not args and not kwargs: | ||
91 | raise ValueError("Cannot call with no arguments.") | 70 | raise ValueError("Cannot call with no arguments.") | ||
92 | if args and kwargs: | 71 | if args and kwargs: | ||
93 | raise ValueError("Cannot mix positional and keyword arguments.") | 72 | raise ValueError("Cannot mix positional and keyword arguments.") | ||
94 | 73 | ||||
95 | if kwargs: | 74 | if kwargs: | ||
96 | instances = () | 75 | instances = () | ||
97 | for key, value in kwargs.items(): | 76 | for key, value in kwargs.items(): | ||
n | 98 | if key in Factory.base_material_classes: | n | 77 | if key in self.created_material_classes: |
99 | material_class = Factory.base_material_classes[key] | 78 | material_class = self.created_material_classes[key] | ||
100 | instance = material_class(value) | 79 | instance = material_class(value) | ||
101 | instances += (instance,) | 80 | instances += (instance,) | ||
102 | 81 | ||||
103 | self.current_instances.add(instance) | 82 | self.current_instances.add(instance) | ||
n | 104 | Factory.all_instances.add(instance) | n | ||
105 | elif key in self.created_classes: | ||||
106 | material_class = Factory.created_classes[key] | ||||
107 | instance = material_class(value) | ||||
108 | instances += (instance,) | ||||
109 | |||||
110 | self.current_instances.add(instance) | 83 | self.all_instances.add(instance) | ||
111 | Factory.all_instances.add(instance) | ||||
112 | else: | 84 | else: | ||
113 | raise ValueError(f"Invalid name of keyword argument: {key}") | 85 | raise ValueError(f"Invalid name of keyword argument: {key}") | ||
114 | return instances | 86 | return instances | ||
115 | 87 | ||||
116 | if args: | 88 | if args: | ||
117 | for arg in args: | 89 | for arg in args: | ||
n | 118 | if arg in Factory.used_instances: | n | 90 | if arg in self.used_instances: |
119 | raise AssertionError(f"Instance {arg} has already been used.") | 91 | raise AssertionError(f"Instance {arg} has already been used.") | ||
n | 120 | if not any(isinstance(arg, cls) for cls in Factory.base_material_classes.values()) and \ | n | ||
121 | not any(isinstance(arg, cls) for cls in Factory.created_classes.values()): | 92 | if not any(isinstance(arg, cls) for cls in self.created_material_classes.values()): | ||
122 | raise ValueError(f"Invalid material instance: {arg}") | 93 | raise ValueError(f"Invalid material instance: {arg}") | ||
123 | 94 | ||||
n | 124 | class_name = "_".join(sorted(set(name for arg in args for name in self._get_base_class_name(arg)))) | n | 95 | class_name = "_".join( |
96 | sorted( | ||||
97 | set( | ||||
98 | base_name | ||||
99 | for arg in args | ||||
100 | for base_name in arg._get_class_name().split("_") | ||||
101 | ))) | ||||
102 | |||||
125 | if class_name in Factory.created_classes: | 103 | if class_name in self.created_material_classes: | ||
126 | dynamic_class = Factory.created_classes[class_name] | 104 | dynamic_class = self.created_material_classes[class_name] | ||
127 | else: | 105 | else: | ||
128 | processed_classes = set() | 106 | processed_classes = set() | ||
129 | base_densities = self._get_base_densities(args, processed_classes) | 107 | base_densities = self._get_base_densities(args, processed_classes) | ||
130 | average_density = sum(base_densities) / len(base_densities) | 108 | average_density = sum(base_densities) / len(base_densities) | ||
131 | 109 | ||||
132 | dynamic_class = type( | 110 | dynamic_class = type( | ||
133 | class_name, | 111 | class_name, | ||
n | 134 | (object,), | n | 112 | (Material,), |
135 | { | 113 | { | ||
n | 136 | "__init__": lambda self, weight: setattr(self, "weight", weight), | n | ||
137 | "density": average_density, | 114 | "density": average_density, | ||
n | 138 | "volume": property(lambda self: self.weight / self.density), | n | ||
139 | }, | 115 | }, | ||
140 | ) | 116 | ) | ||
141 | 117 | ||||
n | 142 | Factory.created_classes[class_name] = dynamic_class | n | 118 | self.created_material_classes[class_name] = dynamic_class |
143 | 119 | ||||
n | 144 | Factory.used_instances.update(args) | n | 120 | self.used_instances.update(args) |
145 | total_weight = sum(arg.weight for arg in args) | 121 | total_weight = sum(arg.weight for arg in args) | ||
146 | instance = dynamic_class(total_weight) | 122 | instance = dynamic_class(total_weight) | ||
147 | 123 | ||||
148 | self.current_instances.add(instance) | 124 | self.current_instances.add(instance) | ||
n | 149 | Factory.all_instances.add(instance) | n | 125 | self.all_instances.add(instance) |
150 | 126 | ||||
151 | return instance | 127 | return instance | ||
152 | 128 | ||||
153 | def can_build(self, required_volume): | 129 | def can_build(self, required_volume): | ||
154 | total_volume = sum( | 130 | total_volume = sum( | ||
155 | instance.volume | 131 | instance.volume | ||
156 | for instance in self.current_instances | 132 | for instance in self.current_instances | ||
t | 157 | if instance not in Factory.used_instances | t | 133 | if instance not in self.used_instances |
158 | ) | 134 | ) | ||
159 | return total_volume >= required_volume | 135 | return total_volume >= required_volume | ||
160 | 136 | ||||
161 | @classmethod | 137 | @classmethod | ||
162 | def can_build_together(cls, required_volume): | 138 | def can_build_together(cls, required_volume): | ||
163 | total_volume = sum( | 139 | total_volume = sum( | ||
164 | instance.volume | 140 | instance.volume | ||
165 | for instance in cls.all_instances | 141 | for instance in cls.all_instances | ||
166 | if instance not in cls.used_instances | 142 | if instance not in cls.used_instances | ||
167 | ) | 143 | ) | ||
168 | return total_volume >= required_volume | 144 | return total_volume >= required_volume |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|