使用 Python 中的元类重新定义 __init__ 3

Redefining __init__ with metaclass in Python 3

我正在学习元编程 python 3. 我的教授给了我们这个练习:

编写 metaclass 法术,在巫师中转换学生实例。

我这样做了:

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__)

向导 class __init__ 被正确调用而不是学生 class __init__,但是创建的对象有一个空的 __dict__。我哪里错了?

您的 init() 替换函数会创建一个 local wizard() 实例,并且不会 return 任何东西:

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

那是一个单独的实例,self 没有被触及。

不要使用__new__;产生一个新的 class。你只是重命名了 student class 并给它一个无效的 __init__ 方法。

覆盖 __call__ 方法以挂钩创建 实例 。您可以在那里替换或忽略第一个参数,即 student class 对象并使用 wizard class 代替它:

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通常只接受一个参数,所以如果指定none,上面会添加一个magic参数。

演示:

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