Домашни > Another brick in the wall > Решения > Решението на Цветелина Гоцова

Резултати
6 точки от тестове
0 точки от учител

6 точки общо

6 успешни теста
4 неуспешни теста
Код
Скрий всички коментари

  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 Concrete(Material):
 14    density = 2500
 15
 16
 17class Brick(Material):
 18    density = 2000
 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
 32class Factory:
 33    all_valid_materials = []
 34    dynamic_classes = {}
 35
 36    def __init__(self):
 37        self.materials = {
 38            "Concrete": Concrete,
 39            "Brick": Brick,
 40            "Stone": Stone,
 41            "Wood": Wood,
 42            "Steel": Steel,
 43        }
 44        self.valid_materials = []
 45        self.invalid_materials = []
 46
 47    def _mark_as_used(self, material):
 48        if material._used:
 49            raise AssertionError("Материалът вече е използван в тази сплав.")
 50        material._used = True
 51
 52    def _create_dynamic_class(self, materials):
 53        base_classes = sorted([mat.__class__.__name__ for mat in materials])
 54        class_name = "_".join(base_classes)
 55
 56        if class_name not in Factory.dynamic_classes:
 57            avg_density = sum(mat.density for mat in materials) / len(materials)
 58            new_class = type(
 59                class_name,
 60                (Material,),
 61                {"density": avg_density}
 62            )
 63            Factory.dynamic_classes[class_name] = new_class
 64
 65        return Factory.dynamic_classes[class_name]
 66
 67    def handle_args(self, args):
 68        for material in args:
 69            self._mark_as_used(material)
 70        dynamic_class = self._create_dynamic_class(args)
 71        new_instance = dynamic_class(sum([material.mass for material in args]))
 72        self.invalid_materials.extend(args)
 73        return new_instance
 74
 75    def handle_kwargs(self, kwargs):
 76        instances = []
 77        for name, mass in kwargs.items():
 78            material_class = self.materials.get(name) or Factory.dynamic_classes.get(name)
 79            if material_class is None:
 80                raise ValueError("Невалидно име на материал")
 81            material_instance = material_class(mass)
 82            instances.append(material_instance)
 83            self.valid_materials.append(material_instance)
 84        return tuple(instances)
 85
 86    def __call__(self, *args, **kwargs):
 87        if args and kwargs:
 88            raise ValueError("Фабриката не може да се извика и с двата типа аргументи.")
 89        if not args and not kwargs:
 90            raise ValueError("Фабриката трябва да се извика или с позиционен, или с именуван параметър.")
 91        if args:
 92            if isinstance(args[0], Material):
 93                return self.handle_args(args)
 94            else:
 95                raise ValueError("Невалидни аргументи.")
 96        if kwargs:
 97            return self.handle_kwargs(kwargs)
 98
 99    def can_build(self, required_volume):
100        total_volume = sum(mat.volume for mat in self.valid_materials if not mat._used)
101        return total_volume >= required_volume
102
103    @classmethod
104    def can_build_together(cls, required_volume):
105        total_volume = sum(mat.volume for factory in Factory.all_valid_materials for mat in factory.valid_materials)
106        return total_volume >= required_volume

.F...FFF..
======================================================================
FAIL: test_can_build (test.TestFactory.test_can_build)
Test can_build methods.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 176, in test_can_build
self.assertTrue(self.factory2.can_build(4.0))
AssertionError: False is not true

======================================================================
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 93, in __call__
return self.handle_args(args)
^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 69, in handle_args
self._mark_as_used(material)
File "/tmp/solution.py", line 49, in _mark_as_used
raise AssertionError("Материалът вече е използван в тази сплав.")
AssertionError: Материалът вече е използван в тази сплав.

======================================================================
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.Concrete'> is not <class 'solution.Concrete'>

----------------------------------------------------------------------
Ran 10 tests in 0.010s

FAILED (failures=4)

Дискусия
Цветелина Гоцова
22.11.2024 16:08

Да, да, аз си питам информативно, мерси!
Георги Кунчев
22.11.2024 15:51

Имаш опцията да използваш неща от `abc` модула. https://docs.python.org/3/library/abc.html Дори да налседиш `abc.ABC`, това не значи, че няма да можеш да го инстанцираш. Целта е да дефинираш абстрактни методи, които наследниците ще са длъжни да имплементират. За твоя случай е достатъчно да направиш обикновен клас и да го наследиш, защото едва ли наследниците ще имплементират нещо особено.
Цветелина Гоцова
22.11.2024 15:00

Всъщност да, добавих такъв *абстрактен* клас впоследствие, явно след като съм качила кода тук. Реално съществуват ли абстрактни класове, както е в C++, трябва ли да се спомене някъде, че не може да се правят инстанции от този клас и ако да, как?
Георги Кунчев
22.11.2024 09:45

"Съгражданинът" не слуша и проверява :smile: Давам идея да направиш клас , който всички материали да наследяват. Така ще си спетиш дефиницията на двата метода, която е една и съща за всички материали. Освен това имай предвид, че не сме баш съграждани. Аз съм от [Селановци](https://bg.wikipedia.org/wiki/%D0%A1%D0%B5%D0%BB%D0%B0%D0%BD%D0%BE%D0%B2%D1%86%D0%B8). Просто учих в ПМГ-то у Враца.
История

n1class Concrete:n1class Material:
2    density = 25002    density = None
33
4    def __init__(self, mass):4    def __init__(self, mass):
5        self.mass = mass5        self.mass = mass
nn6        self._used = False
67
7    @property8    @property
8    def volume(self):9    def volume(self):
9        return self.mass / self.density10        return self.mass / self.density
1011
1112
n12class Brick:n13class Concrete(Material):
14    density = 2500
15 
16 
17class Brick(Material):
13    density = 200018    density = 2000
1419
n15    def __init__(self, mass):n
16        self.mass = mass
1720
n18    @propertyn21class Stone(Material):
19    def volume(self):
20        return self.mass / self.density
21    
22 
23class Stone:
24    density = 160022    density = 1600
2523
n26    def __init__(self, mass):n
27        self.mass = mass
2824
n29    @propertyn25class Wood(Material):
30    def volume(self):
31        return self.mass / self.density
32    
33 
34class Wood:
35    density = 60026    density = 600
3627
n37    def __init__(self, mass):n
38        self.mass = mass
3928
n40    @propertyn29class Steel(Material):
41    def volume(self):
42        return self.mass / self.density
43    
44 
45class Steel:
46    density = 770030    density = 7700
4731
n48    def __init__(self, mass: int):n32class Factory:
49        self.mass = mass33    all_valid_materials = []
34    dynamic_classes = {}
5035
n51    @propertyn36    def __init__(self):
52    def volume(self):37        self.materials = {
53        return self.mass / self.density38            "Concrete": Concrete,
54    39            "Brick": Brick,
40            "Stone": Stone,
41            "Wood": Wood,
42            "Steel": Steel,
43        }
44        self.valid_materials = []
45        self.invalid_materials = []
5546
n56class Factory:n47    def _mark_as_used(self, material):
57    def __init__(self):48        if material._used:
58        pass49            raise AssertionError("Материалът вече е използван в тази сплав.")
59    50        material._used = True
51 
52    def _create_dynamic_class(self, materials):
53        base_classes = sorted([mat.__class__.__name__ for mat in materials])
54        class_name = "_".join(base_classes)
55 
56        if class_name not in Factory.dynamic_classes:
57            avg_density = sum(mat.density for mat in materials) / len(materials)
58            new_class = type(
59                class_name,
60                (Material,),
61                {"density": avg_density}
62            )
63            Factory.dynamic_classes[class_name] = new_class
64 
65        return Factory.dynamic_classes[class_name]
66 
67    def handle_args(self, args):
68        for material in args:
69            self._mark_as_used(material)
70        dynamic_class = self._create_dynamic_class(args)
71        new_instance = dynamic_class(sum([material.mass for material in args]))
72        self.invalid_materials.extend(args)
73        return new_instance
74 
75    def handle_kwargs(self, kwargs):
76        instances = []
77        for name, mass in kwargs.items():
78            material_class = self.materials.get(name) or Factory.dynamic_classes.get(name)
79            if material_class is None:
80                raise ValueError("Невалидно име на материал")
81            material_instance = material_class(mass)
82            instances.append(material_instance)
83            self.valid_materials.append(material_instance)
84        return tuple(instances)
85 
60    def __call__(self, *args, **kwargs):86    def __call__(self, *args, **kwargs):
nn87        if args and kwargs:
88            raise ValueError("Фабриката не може да се извика и с двата типа аргументи.")
61        if not args and not kwargs:89        if not args and not kwargs:
n62            raise ValueError("Фабриката трябва да се извика или с позиционни, или с именувани параметри")n90            raise ValueError("Фабриката трябва да се извика или с позиционен, или с именуван параметър.")
91        if args:
92            if isinstance(args[0], Material):
93                return self.handle_args(args)
94            else:
95                raise ValueError("Невалидни аргументи.")
96        if kwargs:
97            return self.handle_kwargs(kwargs)
6398
t64        if args and kwargs:t99    def can_build(self, required_volume):
65            raise ValueError("Фабриката не може да се извиква и с позиционни, и с именувани параметри едновременно")100        total_volume = sum(mat.volume for mat in self.valid_materials if not mat._used)
101        return total_volume >= required_volume
102 
103    @classmethod
104    def can_build_together(cls, required_volume):
105        total_volume = sum(mat.volume for factory in Factory.all_valid_materials for mat in factory.valid_materials)
106        return total_volume >= required_volume
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1class Concrete:f1class Concrete:
2    density = 25002    density = 2500
33
4    def __init__(self, mass):4    def __init__(self, mass):
5        self.mass = mass5        self.mass = mass
66
7    @property7    @property
8    def volume(self):8    def volume(self):
9        return self.mass / self.density9        return self.mass / self.density
1010
1111
12class Brick:12class Brick:
13    density = 200013    density = 2000
1414
15    def __init__(self, mass):15    def __init__(self, mass):
16        self.mass = mass16        self.mass = mass
1717
18    @property18    @property
19    def volume(self):19    def volume(self):
20        return self.mass / self.density20        return self.mass / self.density
21    21    
2222
23class Stone:23class Stone:
24    density = 160024    density = 1600
2525
26    def __init__(self, mass):26    def __init__(self, mass):
27        self.mass = mass27        self.mass = mass
2828
29    @property29    @property
30    def volume(self):30    def volume(self):
31        return self.mass / self.density31        return self.mass / self.density
32    32    
3333
34class Wood:34class Wood:
35    density = 60035    density = 600
3636
37    def __init__(self, mass):37    def __init__(self, mass):
38        self.mass = mass38        self.mass = mass
3939
40    @property40    @property
41    def volume(self):41    def volume(self):
42        return self.mass / self.density42        return self.mass / self.density
43    43    
4444
45class Steel:45class Steel:
46    density = 770046    density = 7700
4747
48    def __init__(self, mass: int):48    def __init__(self, mass: int):
49        self.mass = mass49        self.mass = mass
5050
51    @property51    @property
52    def volume(self):52    def volume(self):
53        return self.mass / self.density53        return self.mass / self.density
54    54    
5555
t56brick = Brick(2000)t56class Factory:
57print(brick.volume)57    def __init__(self):
58        pass
59    
60    def __call__(self, *args, **kwargs):
61        if not args and not kwargs:
62            raise ValueError("Фабриката трябва да се извика или с позиционни, или с именувани параметри")
63 
64        if args and kwargs:
65            raise ValueError("Фабриката не може да се извиква и с позиционни, и с именувани параметри едновременно")
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op