import math
import random

COLOURS = ['black', 'yellow', 'red', 'magenta', 'cyan', 'purple']

class Square(object):
	# строки 6-37 это лишь описание класса, образец, blueprint, если угодно,
	# но сам он не является объектом (инстансом) квадрата.
	# А вот вызвывая "Square()" уже и создаётся квадрат. Каждый раз — новый,
	# с теми параметрами (стороны и цвета), которые были переданы в аргументами.
    def __init__(self, side, colour="black"):
        self.side = side
        self.colour = colour
    
    @classmethod
    def generate_random(cls, colours):
        # Это классовый метод, в котором первым аргументом передаётся не инстанс
        # класса (то, что в методах 'self' обозначается), а сам класс/тип.
        # Т.е. в данном случае cls == Square, а выражение cls(side, colour)
        # эквивалентно Square(side, colour). Разница появляется при налседовани, где
        # cls уже будет тем классом, без необходимости заново объявлять этот метод.
        # В ромбе-алмазе увидишь, как работает.
        # Ссылаться на глобальные переменные как-то не круто, я подумал, поэтому
        # решил добавить аргумент с массивом цветов, так как это универсальный аргумент
        # среди всех фигур.
        side = random.random() * 100
        colour = colours[random.randint(0, len(colours) - 1)]
        return cls(side, colour)
    
    def area(self):
        # на метод изменил, чтобы с проперти тебя не путать — потом почитаешь про них
        return self.side ** 2
    
    def description(self):
        # Возвращает строку с описанием объекта, при вызове этого метода у 
        # конкретного объекта (инстанса класса Square или любого из унаследовавших)
        # __class__.__name__ — чтобы было написано название именно класса объекта,
        # а не то, что я тут впишу текстом, но при этом и не было необходимости
        # создавать лишние классовые переменные при наследовании. Это на вкус уже,
        # конечно, потому что может нужно в тексте иметь "triangle", а класс назвать
        # Trngl. Нутыпонел, в общем, просто полезную штучку показать решил.
        # В ромбе-алмазе увидишь, как работает.
        return "%s %s with side: %.2f, area: %.2f" % (self.colour.title(), 
            self.__class__.__name__.lower(), self.side, self.area())
            
class Circle(object):
    def __init__(self, radius, colour="black"):
        self.radius = radius
        self.colour = colour
    
    @classmethod
    def generate_random(cls, colours):
        radius = random.random() * 100
        colour = colours[random.randint(0, len(colours) - 1)]
        return cls(radius, colour)
    
    def area(self):
        return self.radius ** 2 * math.pi

    def description(self):
        return "%s %s with radius: %.2f, area: %.2f" % (self.colour.title(), 
            self.__class__.__name__.lower(), self.radius, self.area())

class Diamond(Square):
    # ромб (у которого все стороны равны, как у квадрата), но имеющий углы по 
    # 60 и 120 градусов.
    def area(self):
        return self.side ** 2 * math.sin(math.pi * (60/180))


# Если сначала просто вручную создадим парочку.
s1 = Square(5)              # квадрат со стороной 5 и чёрным (дефолтным) цветом.
s2 = Square(10, "yellow")   # жёлтый квадрат со стороной 10.
# Никаки строк текста никуда пока не писалось, но за s1 и s2 стоят самые настоящие
# квадратики, с которым можно что угодно делать. Был бы у них метод draw(), сейчас
# бы я мог их нарисовать. Или не сейчас.
# А вот теперь я хочу посмотреть описание второго квадрата:
print(s2.description())

# Теперь к генератору перейдём
classes = [Square, Circle, Diamond]
# Это просто список всех классов (_типов_ фигур). Я их туда запихнул для лупа
# дальнейшего лупа по этом списку и генерации их всех за один проход. Можно, конечно,
# и как ты - отдельно каждого класса создавать, а потом перемешать, но представь,
# что у тебя 100 таких классов - задолбаешься на каждый из них пускай даже по 5, но
# одинаковых строк тратить. А так запихнул в список и прекрасно.


figs = []       # вот это как вот раз список _фигур_ (таких объектов, как s1 и s2)
numfigs = 5

# Дальше создадим десяток их (numfigs = 5, но по две за одну итерацию создаётся)
for i in range(numfigs):
    fig = classes[random.randint(0, len(classes) - 1)]
    # fig - просто рандомный класс из списка выше. 
    # В данные момент, например, может быть, что fig == Circle.
    
    side_or_rad = random.random() * 100
    colour = COLOURS[random.randint(0, len(COLOURS) - 1)]
    # Так как у нас все фигуры имеют одинаковый "вид" создания: длина/радиус и цвет,
    # можно не делать отдельно функции для каждого, а просто сгенерить случайные 
    # side_or_rad и colour, которые можно передавать любому из классов.
    # В реальности же, добавив, например, треугольник, больше так не сделать, так как
    # он одним параметром не задаётся (разве что равнобедренный, но это отедльный
    # случай). Для этого я и сделал классовый метод generate_random(), который 
    # уникален для каждой отдельной фигуры, и создаётся именно в классе,
    # а не где-то отдельно.
    figure = fig(side_or_rad, colour)
    figs.append(figure)
    
    # Добавим ещё одну, воспользовавшись этим самым методом.
    fig = classes[random.randint(0, len(classes) - 1)]
    figs.append(fig.generate_random(COLOURS))


# А теперь выведем _описание_ фигуры. По две строки на каждую, где первая - это
# дефолтное питоновское описание любого объекта - класс и область в памяти, 
# а вторая - как раз наш метод, возвращающий текст с разрмерами и т.д.
# (На самом деле первая строка это результат метода __repr__(), который в предыдущей версии
# кода можешь увидеть.
for fig in figs:
    print()
    print(fig)
    print(fig.description())