python学习笔记——自定义元类

自定义元类

什么是元类

在python中万物皆对象,类也是一个对象,自定义的类或是python中内置的类都是由元类(type)实例化来的。因此元类也是一种类。

元类 ——实例化——> ——实例化——> 对象

元类实例化类

使用元类实例化类需要三个参数:类名,基类(即继承于哪些父类),类名称空间(由类子代码执行得到)

type(class_name, class_bases, class_dict)  # type是内置元类,调用type可实例化一个自定义的类

原类实例化流程(与类实例化对象的过程一致):

  1. 调用__new__方法产生一个空对象(类)
  2. 调用__init__方法初始化对象(类)
  3. 返回初始化后的对象(类)

_new_

__new__方法会在类实例化过程中首先被调用,生成并返回一个空对象

class MyType(type):  # 继承type
    def __new__(cls, *args, **kwargs):
        print("MyType.__new__")
        return super().__new__(cls, *args, **kwargs)  # 使用父类即type生成空对象

_init_

__init__用于初始化空对象,在自定义元类中,通常会传入四个参数:self(即实例化的对象本身),类名,基类,类名称空间。后三个参数是调用自定义元类实例化类所需的三个参数,可在此构造函数中对类名,基类,类名称空间做一些限制或后处理。

def __init__(self, class_name, class_bases, class_dict):
    if '_' in class_name:
        raise NameError("类名不能有下划线!")  # 限制类名中不能出现下划线
    if not class_dict.get('__doc__'):
        raise SyntaxError('定义类必须写注释!')  # 限制类中必须有注释文档,如有注释文档,类名称空间中有'__doc__'键

_call_

类可以被调用来实例化一个对象,因此自定义元类必须要有__call__方法,它在对象被调用时触发执行。

# 对象可以被调用执行,产生这个对象的类必须有__call__方法
class Dog(metaclass=MyType):
    def __init__(self, name, color):
        self.name = name
        self.color = color

    def info(self):
        print('name: {}, color: {}'.format(self.name, self.color))

    def __call__(self, *args, **kwargs):
        print("Dog.__call__")
        return "wang"
    
    def __new__(cls, *args, **kwargs):
        obj = super().__new__(cls)  # 使用父类,即object中的__new__方法来生成一个空对象
        return obj
    
dog1 = Dog('ha', 'white')  
print(dog1())  # Dog.__call__, wang, 在调用对象时触发类的__call__方法

同理,Dog这个类可以被调用实例化对象,说明自定义元类中也有__call__方法。

别忘了,类实例化为对象的三个步骤:生成空对象,初始化对象,返回初始化后的对象,即这些过程被封装在元类中的__call__方法中。

def __call__(self, *args, **kwargs):
    dog_obj = self.__new__(self)  # 调用Dog中的__new__方法产生一个空对象
    self.__init__(dog_obj, *args, **kwargs)  # 调用Dog中的__init__方法来初始化对象,因此第一个参数时__new__出来的空对象
    # 修改对象的属性名
    new_dic = {}
    for k in dog_obj.__dict__:chu
        new_dic[f'New_{k}'] = dog_obj.__dict__[k]
    dog_obj.__dict__ = new_dic
    return dog_obj  # 返回对象

dog1 = Dog('ha', 'white')
print(dog1.__dict__)  # {'New_name': 'ha', 'New_color': 'white'}

完整代码

自定义元类流程梳理:

  1. 继承type类
  2. 自定义元类本质上也是一个类,实例化生成一个类,因此会先调用__new__方法产生空对象
  3. 再调用__init__方法初始化空对象
  4. 使用自定义元类实例化出来的类可被调用,因此自定义元类中有__call__方法
  5. 实例化出来的类也可实例化为一个对象,因此__call__方法中包含生成空对象,初始化空对象并返回这三个步骤。
# 产生一个dog对象需要调用Dog,即Dog('ha', 'white'),会触发自定义元类MyType中的__call__方法
class MyType(type):  # 继承type
    def __init__(self, class_name, class_bases, class_dict):
        if '_' in class_name:
            raise NameError("类名不能有下划线!")
        if not class_dict.get('__doc__'):
            raise SyntaxError('定义类必须写注释!')
        # print(self.__bases__)

    def __new__(cls, *args, **kwargs):
        print("MyType.__new__")
        return super().__new__(cls, *args, **kwargs)

    def __call__(self, *args, **kwargs):
        dog_obj = self.__new__(self)  # 调用Dog中的__new__方法产生一个空对象
        self.__init__(dog_obj, *args, **kwargs)  # 调用Dog中的__init__方法来初始化对象,因此第一个参数时__new__出来的空对象
        # 修改对象的属性名
        new_dic = {}
        for k in dog_obj.__dict__:
            new_dic[f'New_{k}'] = dog_obj.__dict__[k]
        dog_obj.__dict__ = new_dic
        return dog_obj


class Dog(metaclass=MyType):
    """
    test
    """

    def __init__(self, name, color):
        self.name = name
        self.color = color

    def info(self):
        print('name: {}, color: {}'.format(self.name, self.color))

    def __call__(self, *args, **kwargs):
        print("Dog.__call__")
        return "wang"

    def __new__(cls, *args, **kwargs):
        obj = super().__new__(cls)  # 使用父类,即object中的__new__方法来生成一个空对象
        return obj


if __name__ == '__main__':
    dog1 = Dog('ha', 'white')  # MyType.__new__
    print(dog1())  # Dog.__call__, wang, 在调用对象时触发类的__call__方法
    print(dog1.__dict__)  # {'New_name': 'ha', 'New_color': 'white'}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值