Домашни > Another brick in the wall > Решения > Решението на Силвана Николова

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

9 точки общо

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

  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

Въпреки броят забележки - в решението ти си личи разбиране за това как работи езика. Идиомите и дизайна - с времето. :)
История
Това решение има само една версия.