<jupyter_start><jupyter_text>`type` hierarchy<jupyter_code>class Foo:
    pass
x = Foo()
type(x)
type(Foo)
type(type)<jupyter_output><empty_output><jupyter_text>Metaclasses are class `factories`<jupyter_code># Uncomment this
# help(type)
Foo = type('Foo', (), {})
Foo
x = Foo()
type(x)
type(Foo)
isinstance(Foo, type)<jupyter_output><empty_output><jupyter_text>Adding attributes<jupyter_code>Person = type('Person', (), {'name': '', 'age': 0, 'gender': 'NA', 
                             '__str__': lambda x: '{}, age:{}, sex: {}'.format(x.name, x.age, x.gender)})
Person
# Building an object and adding attributes
john = Person()

john.name='John'
john.age = 25
john.gender = 'M'
print(john)
# Can't we initialize directly ?
john = Person(name='John', age=25, sex='M')<jupyter_output><empty_output><jupyter_text>Metaclass to the rescue<jupyter_code>class PersonType(type):
    """ A type for Person classes """
    
    
    def __new__(cls, name, bases, dct):
        x = super().__new__(cls, name, bases, dct)
        # Dynamic assignment of methods
        x.__init__ = cls.__myinit__
        x.__str__ = cls.__mystr__
        x.hello = cls.hello
        return x

    def __myinit__(self, name=None, age=0, gender='NA'):
        self.name = name
        self.age = age
        self.gender = gender
        
    def __mystr__(self):
        return "{}, age: {}, sex: {}".format(self.name, self.age, self.gender)
    
    def hello(self):
        print(self)
        print("Hi - I am ", self.name)
class Person(metaclass=PersonType):
    pass
john = Person('John', 25, 'M')
print(john)
john.hello()<jupyter_output>John, age: 25, sex: M
John, age: 25, sex: M
Hi - I am  John<jupyter_text>A better way - Use `__prepare__` special method on the meta-class<jupyter_code>class PersonPreparedType(type):
    """ A type for Person classes """
    
    @classmethod
    def __prepare__(mcs, name, bases, **kwargs):
        print('Metaclass=>',mcs, name)
        return {'__init__': mcs.__myinit__, '__str__': mcs.__mystr__, 'hello': mcs.hello}

    def __myinit__(self, name=None, age=0, gender='NA'):
        self.name = name
        self.age = age
        self.gender = gender
        
    def __mystr__(self):
        return "{}, age: {}, sex: {}".format(self.name, self.age, self.gender)
    
    def hello(self):
        print('Calling on',self)
        print("Hi - I am ", self.name)
class Person(metaclass=PersonPreparedType):
    pass
john = Person('John', 25, 'M')
print(john)
john.hello()<jupyter_output>John, age: 25, sex: M
Calling on John, age: 25, sex: M
Hi - I am  John
{'fraction_non_alphanumeric': 0.15503875968992248, 'fraction_numerical': 0.007364341085271318, 'mean_word_length': 3.287375415282392, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '189622', 'license': 'MIT', 'n_tokens_mistral': 951, 'n_tokens_neox': 898, 'n_words': 222, 'path': 'kochi-python-oct-2018/.ipynb_checkpoints/Metaclasses-checkpoint.ipynb', 'repo_name': 'pythonhacker/talks', 'size': '2580'}