"""
    https://stepik.org/lesson/701986/step/11?unit=702087

    Объявите в программе класс Dimensions (габариты) с атрибутами:

MIN_DIMENSION = 10
MAX_DIMENSION = 1000

>>> (Dimensions.MIN_DIMENSION, Dimensions.MAX_DIMENSION)
(10, 1000)

Каждый объект класса Dimensions должен создаваться командой:
d3 = Dimensions(a, b, c)   # a, b, c - габаритные размеры
>>> d3 = Dimensions(10, 10, 10)

и содержать локальные атрибуты:
__a, __b, __c - габаритные размеры (целые или вещественные числа).

Для работы с этими локальными атрибутами в классе Dimensions следует прописать следующие объекты-свойства:
a, b, c - для изменения и считывания соответствующих локальных атрибутов __a, __b, __c.
>>> (d3.a, d3.b, d3.c)
(10, 10, 10)
>>> [*map(lambda x: getattr(Dimensions, x).__class__, "abc")]
[<class 'property'>, <class 'property'>, <class 'property'>]

При изменении значений __a, __b, __c следует проверять, что присваиваемое значение число в диапазоне [MIN_DIMENSION; MAX_DIMENSION]. Если это не так, то новое значение не присваивается (игнорируется).

С помощью магических методов данного занятия запретить создание локальных атрибутов MIN_DIMENSION и MAX_DIMENSION в объектах класса Dimensions. При попытке это сделать генерировать исключение:
raise AttributeError("Менять атрибуты MIN_DIMENSION и MAX_DIMENSION запрещено.")
>>> d3.MIN_DIMENSION = 4
Traceback (most recent call last):
    ...
AttributeError: Менять атрибуты MIN_DIMENSION и MAX_DIMENSION запрещено.
>>> d3.MAX_DIMENSION = 400
Traceback (most recent call last):
    ...
AttributeError: Менять атрибуты MIN_DIMENSION и MAX_DIMENSION запрещено.

Пример использования класса  (эти строчки в программе писать не нужно):
d = Dimensions(10.5, 20.1, 30)
d.a = 8
d.b = 15
a, b, c = d.a, d.b, d.c  # a=10.5, b=15, c=30
d.MAX_DIMENSION = 10  # исключение AttributeError

>>> d = Dimensions(10.5, 20.1, 30)
>>> d.a = 8
>>> d.b = 15
>>> (d.a, d.b, d.c)
(10.5, 15, 30)

P.S. В программе нужно объявить только класс Dimensions. На экран ничего выводить не нужно. 
"""


def make_properties(*names):
    def decorator(cls):
        def prop(private_name: str):
            def getter(self):
                return getattr(self, private_name)

            def setter(self, value):
                return setattr(self, private_name, value)

            return getter, setter

        for name in names:
            setattr(cls, name, property(*prop(f"_{cls.__name__}__{name}")))
        return cls

    return decorator


@make_properties(*"abc")
class Dimensions:
    MIN_DIMENSION = 10
    MAX_DIMENSION = 1000

    def __init__(self, a, b, c):
        self.a, self.b, self.c = a, b, c

    def __setattr__(self, name, value):
        if name in ["MIN_DIMENSION", "MAX_DIMENSION"]:
            raise AttributeError(
                "Менять атрибуты MIN_DIMENSION и MAX_DIMENSION запрещено."
            )
        if not self.MIN_DIMENSION <= value <= self.MAX_DIMENSION:
            return
        super().__setattr__(name, value)


def tests():
    # из https://stepik.org/lesson/701986/step/11?discussion=7391213&unit=702087
    # TEST-TASK___________________________________
    x = Dimensions(10.0, 20, 30)
    # проверка что в классе прописаны объекты свойства a-b-c
    assert type(Dimensions.a) is property, "a - не является объектом свойством property"
    assert type(Dimensions.b) is property, "b - не является объектом свойством property"
    assert type(Dimensions.c) is property, "c - не является объектом свойством property"

    # проверка что в объекте класса существуют 3 приватных локальных атрибута
    assert (
        "_Dimensions__a" in x.__dict__
        and "_Dimensions__b" in x.__dict__
        and "_Dimensions__c" in x.__dict__
    ), "атрибуты не являются приватными"
    # проверка что данные считываются с приватных атрибутов
    assert (
        x.a == 10.0 and x.b == 20 and x.c == 30
    ), "при обращении к приватным атрибутам значения не получены проверьте объекты свойства"
    # проверка что значения являются целым или вещественным числами
    assert (
        type(x.a) in (int, float)
        and type(x.b) in (int, float)
        and type(x.c) in (int, float)
    ), "значения должны быть или целым числом или вещественным"
    # проверка на существование атрибутов минимум-максимум
    assert hasattr(x, "MIN_DIMENSION"), "не найден атрибут MIN_DIMENSION"
    assert hasattr(x, "MAX_DIMENSION"), "не найден атрибут MAX_DIMENSION"

    # проверка что значение в диапазоне
    x.a = 9
    assert (
        x.a == 10.0
    ), "присваиваемое значение должно быть в диапазоне [MIN_DIMENSION; MAX_DIMENSION]"
    x.b = -1
    assert (
        x.b == 20
    ), "присваиваемое значение должно быть в диапазоне [MIN_DIMENSION; MAX_DIMENSION]"
    x.c = 1001
    assert (
        x.c == 30
    ), "присваиваемое значение должно быть в диапазоне [MIN_DIMENSION; MAX_DIMENSION]"

    # проверка
    # С помощью магических методов данного занятия запретить создание локальных атрибутов MIN_DIMENSION и MAX_DIMENSION в объектах класса Dimensions.
    # При попытке это сделать генерировать исключение:
    # raise AttributeError("Менять атрибуты MIN_DIMENSION и MAX_DIMENSION запрещено.")
    try:
        x.MIN_DIMENSION = 0
    except AttributeError:
        assert True
    else:
        assert False, "не сгенерировалось исключение AttributeError"

    try:
        x.MAX_DIMENSION = 0
    except AttributeError:
        assert True
    else:
        assert False, "не сгенерировалось исключение AttributeError"

    assert (
        "MIN_DIMENSION" not in x.__dict__
    ), "запретить создание локальных атрибутов MIN_DIMENSION и MAX_DIMENSION в объектах класса Dimensions"
    assert (
        "MAX_DIMENSION" not in x.__dict__
    ), "запретить создание локальных атрибутов MIN_DIMENSION и MAX_DIMENSION в объектах класса Dimensions"


if __name__ == "__main__":
    import doctest

    doctest.testmod()
    tests()