Overriding __init__ with metaclass in Python 3

I am learning metaprogramming in python 3. My professor gave us this exercise:

Record a metaclass spell that transforms Student instances into a Wizard.

I have done this:

class Spell(type):
    def __new__(cls, classname, supers, cls_dict):
        cls_dict['__init__'] = Spell.toWizard()
        cls_dict['__qualname__'] = 'wizard'
        return type.__new__(cls, 'wizard', supers, cls_dict)

    @staticmethod
    def toWizard():
        def init(*args):
            wizard(args, 'fire_spell')
        return init    

class student(metaclass=Spell):
    def __init__(self, name):
        self.name = name

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

class wizard(person):
    def __init__(self, name, magic):
        self.magic = magic
        super().__init__(name)



if __name__ == '__main__':
    s = student('name1')
    print(s.__dict__)
    s = student('name2')
    print(s.__dict__)

The wizard class is __init__correctly called instead of the class __init__, however, the created objects are empty __dict__. Where am I mistaken?

+4
source share
1 answer

The replace function init()creates a local instance wizard()and returns nothing:

def init(*args):
    wizard(args, 'fire_spell')

To not touch a separate instance self.

Do not use __new__; which creates a new class. You just renamed the class studentand gave it an inefficient method __init__.

__call__, . , student, wizard :

class Spell(type):
    def __call__(cls, *args, **kwargs):
        # ignore the cls argument, return a wizard instead
        if 'magic' not in kwargs and len(args) < 2:
            # provide a default magic value
            kwargs['magic'] = 'fire_spell'
        return wizard(*args, **kwargs)

student , magic, .

:

>>> student('name1')
<__main__.wizard object at 0x10f1e8198>
>>> vars(_)
{'magic': 'fire_spell', 'name': 'name1'}
+6

Source: https://habr.com/ru/post/1693778/


All Articles