#!/usr/bin/env python

class Meta(type):

    def __prepare__(class_name, bases):
        """
        "Prepare" the new class. Here you can update the base classes.

        :param name: Name of new class as a string
        :param bases: Tuple of base classes
        :return: Dictionary that initializes the namespace for the new class (must be a dict)
        """
        print("in metaclass (class={}) __prepare__()".format(class_name), end=' ==> ')
        print("params: name={}, bases={}".format(class_name, bases))
        return {'animal': 'wombat', 'id': 100}

    def __new__(metatype, name, bases, attrs):
        """
        Create the new class. Called after __prepare__(). Note this is only called when classes

        :param metatype: The metaclass itself
        :param name: The name of the class being created
        :param bases: bases of class being created (may be empty)
        :param attrs: Initial attributes of the class being created
        :return:
        """
        print("in metaclass (class={}) __new__()".format(name), end=' ==> ')
        print("params: type={} name={} bases={} attrs={}".format(metatype, name, bases, attrs))
        return super().__new__(metatype, name, bases, attrs)

    def __init__(cls, *args):
        """

        :param cls: The class being created (compare with 'self' in normal class)
        :param args: Any arguments to the class
        """
        print("in metaclass (class={}) __init__()".format(cls.__name__), end=' ==> ')
        print("params: cls={}, args={}".format(cls, args))

        super().__init__(cls)

    def __call__(self, *args, **kwargs):
        """
        Function called when the metaclass is called, as in NewClass = Meta(...)

        :param args:
        :param args:
        :param kwargs:
        :return:
        """
        print("in metaclass (class={})__call__()".format(self.__name__))


class MyBase():
    pass

print('=' * 60)

class A(MyBase, metaclass=Meta):
    id = 5

    def __init__(self):
        print("In class A __init__()")

print('=' * 60)

class B(MyBase, metaclass=Meta):
    animal = 'wombat'

    def __init__(self):
        print("In class B __init__()")


print('-' * 60)
m1 = A()
print('-' * 60)
m2 = B()
print('-' * 60)
m3 = A()
print('-' * 60)
m4 = B()
print('-' * 60)
print("animal: {} id: {}".format(A.animal, B.id))
