'''
Таким образом, разница между __str__ и __repr__ заключается в целях
использования. __str__ предназначен для представления объекта в читаемой
форме, а __repr__ предназначен для представления объекта в точном и воспроизводимом виде.
'''

class Lion:
    def __init__(self, name):
        self.name = name

    '''Как видят объект разработчики'''
    def __repr__(self) -> str:
        return f'The name Lion - f{self.name}'

    '''Как видят объект пользователи. Функции print, str'''
    def __str__(self) -> str:
        return f'Lion - f{self.name}'

class Person:

    def __init__(self, name, surname, gender='male'):
        self.name = name
        self.surname = surname
        if gender not in ['male', 'female']:
            print("Не знаю, что вы имели в виду? Пусть это будет мальчик!")
            self.gender = 'male'
        else:
            self.gender = gender

    def __str__(self):
        if self.gender == 'male':
            return f"Гражданин {self.surname} {self.name}"
        else:
            return

    # Метод должен возвращать неотрицательное значение
    def __len__(self):
         return len(self.name + self.surname)


class MyList:

    def __init__(self, elements):
        self.elements = elements

    def __len__(self):
        return len([elem for elem in self.elements if elem % 2 == 0])


class Hero:

    def __init__(self) -> None:
        pass

    def __len__(self):
        return(len(self.__dict__))
    
    def __str__(self):
        sorted_params = sorted(self.__dict__.items(), key=lambda item: item[0])
        params = ''
        for item in sorted_params:
            params += f'{item[0]}: {item[1]}\n'
        params = params[:len(params)-1]
        return params

class MyAtr:
    
    def __init__(self, name, surname) -> None:
        self.name = name
        self.surname = surname

    def __str__(self) -> str:
        for key, value in self.__dict__.items():
            print(key, value)

        return "hello"

        
'''Математические методы'''
class BankAccount:
    def __init__(self, name, balance):
        print('new_object init')
        self.name = name
        self.balance = balance

    def __repr__(self):
        return f"Клиент {self.name} с балансом {self.balance}"

    def __add__(self, other):
        print('__add__ call')
        if isinstance(other, BankAccount):
            return self.balance + other.balance
        if isinstance(other, (int, float)):
            return BankAccount(self.name, self.balance + other)
        raise NotImplemented

    def __radd__(self, other):
        print('__radd__ call')
        return self + other
    
    def __mul__(self, other):
        print('__add__ call')
        if isinstance(other, BankAccount):
            return self.balance * other.balance
        if isinstance(other, (int, float)):
            return self.balance * other
        if isinstance(other, str):
            return self.name + other
        raise NotImplemented

    

def main():

    hero = Hero()
    assert len(hero) == 0
    hero.health = 50
    hero.damage = 10
    assert len(hero) == 2
    assert str(hero) == '''damage: 10
    health: 50'''
    hero.weapon = ['sword', ' bow']
    hero.skill = 'Некромант'
    assert str(hero) == '''damage: 10
    health: 50
    skill: Некромант
    weapon: ['sword', ' bow']'''
    print(hero)

    villain = Hero()
    assert str(villain) == ''
    assert len(villain) == 0
    villain.level = 15
    villain.skill = 'Боец'
    villain.armor = 25
    assert len(villain) == 3
    assert str(villain) == '''armor: 25
    level: 15
    skill: Боец'''
    print(villain)


    # at = MyAtr('Ivan', 'Ivanov')
    # print(at)

    # hero = Hero()
    # print(len(hero))

    '''
    lion = Lion('Simba')
    print(lion) # __str__
    print(lion) # __str__
    '''

    # my_list = MyList([1, 2, 3, 4, 5, 6, 7])
    # print(len(my_list))
    

if __name__ == '__main__':
    main()
