class Animal:
    def says(self):
        return 'I speak!'
    
class Horse(Animal):
    def says(self):
        return 'Neigh!'
    
class Donkey(Animal):
    def says(self):
        return 'Hee-haw!'
    
class Mule(Donkey, Horse):
    pass

class Hinny(Horse, Donkey):
    pass


def test_animals():
    mule = Mule()
    hinny = Hinny()
    print(mule.says())
    print(hinny.says())

    print(Mule.mro())
    print(Mule.__mro__)

# Миксины
    
class Super():
    def dump(self):
        print('Super!!!')

class PrettyMixin():
    def dump(self):
        import pprint
        pprint.pprint(vars(self))

class Thing(Super, PrettyMixin):
    pass

def test_mixin():
    t = Thing()
    t.name = "Nyarlathotep"
    t.feature = 'ichor'
    t.age = 'eldritch'
    t.dump()

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Инструкция property
    
class Duck():
    def __init__(self, input_name) -> None:
        self.hidden_name = input_name

    def get_name(self):
        print('inside the getter')
        return self.hidden_name
    
    def set_name(self, input_name):
        print('inside the setter')
        self.hidden_name = input_name

    name = property(get_name, set_name)


class ImprovedDuck():
    def __init__(self, input_name) -> None:
        self.__name = input_name

    @property
    def name(self):
        print('inside the getter')
        return self.__name
    
    @name.setter
    def name(self, input_name):
        print('inside the setter')
        self.__name = input_name


def set_property():
    # don = Duck('Donald')
    don = ImprovedDuck('Donald')
    print(don.name)
    don.name = 'Donna'
    print(don.name)
    print(don._ImprovedDuck__name)

# set_property()
    
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    
# Методы классов @classmethod
    
class A():
    count = 0

    def __init__(self):
        A.count += 1

    def exclaim(self):
        print('I`m an A!')

    @classmethod
    def kids(cls):
        print("A has", cls.count, "little objects.")


def test_classmethod():
    easy_a = A()
    breezy_a = A()
    wheezy_a = A()
    A.kids()

# test_classmethod()
    
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    
# Статические методы @staticmethod
    
class CoyoteWeapon():
    @staticmethod
    def commercial():
        print('This CoyoWeapon has been brought to you by Acme')

def test_staticmethod():
    CoyoteWeapon.commercial()

# test_staticmethod()