元类简介及总结

Ⅰ 元类

【一】元类简介

# 元类
    即产生类的类叫做type  type就是所有类默认的元类
print(type(123))  # <class 'int'>
print(type([12, 23, 43]))  # <class 'list'>
print(type({'name': 'silence', 'age': 16}))  # <class 'dict'>
# type查看的其实是当前对象所属的类名称


class MyClass(object):
    pass

obj = MyClass()
print(type(obj))   # <class '__main__.MyClass'>
print(type(MyClass))  # <class 'type'> 意思是MyClass这个类是由type这个类产生的
'''type就是所有类默认的元类'''

【二】产生类的两种编写形式(本质是一种)

1.class关键字
    class 类名:
        pass

2. type元类
 类名 = type('类名',(父类1,父类2),名称空间字典)
# 方式1
class C1(object):
    pass

print(C1)  # <class '__main__.C1'>


# 方式2
res = type('C1', (), {})
print(res)  # <class '__main__.C1'>

【三】学习元类的目的

  • 元类能够掌控类的创建,也就意味着我们可以高度定制类的行为
    • eg:掌握了游戏成神发展的步骤, 就可以在过程中做任何的额外操作

【四】元类的基本使用

'''元类是不能通过继承的方式直接指定的'''
    需要通过关键字参数的形式修改
        class C1(metaclass=MyTypeClass):
            pass
        
        
class Myclass(metaclass=MyType):
    ...

    
class student(metaclass=MyType):
    ...
# TypeError: student首字母必须大写!

# 控制当前类名必须首字母大写!首字母不大写就报错!
class MyTypeClass(type):
    def __init__(cls, cls_name, cls_bases, cls_dict):
        print(cls, cls_name, cls_bases, cls_dict)
        # <class '__main__.C1'> C1 () {'__module__': '__main__', '__qualname__': 'C1'}
        if not cls_name.istitle():
            raise Exception("类名的首字母必须大写")
        super().__init__(cls_name, cls_bases, cls_dict)



class C1(metaclass=MyTypeClass):
    school = '同济大学'
# <class '__main__.C1'> C1 () {'__module__': '__main__', '__qualname__': 'C1', 'school': '同济大学'}

class a(metaclass=MyTypeClass): # 报错 Exception: 类名的首字母必须大写
    pass

【5】元类进阶操作__call__

1.回想__call__方法
    对象加括号会自动执行产生该对象的类里面的__call__,并且该方法返回什么对象加括号就会得到什么
    推导:类加括号就会执行元类的里面的__call__,该方法返回什么其实类加括号就会得到什么
class MyTypeClass(type):
    def __call__(self, *args, **kwargs):
        return self



class MyClass(metaclass=MyTypeClass):
    pass

obj = MyClass()
print(obj)   # None  因为元类里面 __call__什么都没有
# 元类是pass是返回的None 什么也没有  现在return 741  print(obj) 接收返回的就是741
#  return self  那么接收返回的就是<class '__main__.MyClass'>
'''执行先后顺序是,先元类里面的__call__方法,然后是类里面的__init__方法'''
class MyTypeClass(type):
    def __call__(self, *args, **kwargs):
        print('__call__run')
        super().__call__(*args, **kwargs)


class MyClass(metaclass=MyTypeClass):
    def __init__(self, name):
        print('__init__run')
        self.name = name

# obj = MyClass() # __call__run
# 对象产生要先经过__call__,才到__init__
# 然后发现__init__不执行,被__call__拦截了
# 加上super().__call__(*args, **kwargs)才会解扣,再执行__init__
# 运行后报错 显示缺少一个name参数
obj = MyClass('silence')
# __call__run
# __init__run
class MyTypeClass(type):
    def __call__(self, *args, **kwargs):
        # print('__call__run')
        # print(args, kwargs)   # ('silence',) {}
        if args:
            raise Exception('必须全部采用关键字参数')
        super().__call__(*args, **kwargs)


class MyClass(metaclass=MyTypeClass):
    def __init__(self, name):
        print('__init__run')
        self.name = name


'''强制规定:类在实例化产生对象的时候,对象的独有数据必须采用关键字参数'''
# obj1 = MyClass('silence')   # 报错 Exception: 必须全部采用关键字参数
obj2 = MyClass(name='silence')   # __init__run

【6】总结

  • 如果你想高度定制类的产生过程
    • 那么编写元类里面的__init__方法
  • 如果你想高度定制对象的产生过程
    • 那么编写元类里面的__call__方法

__new__方法

__new__用于产生空对象(类)    骨架
__init__用于实例化对象(类)   血肉

【一】类中的__new__

class MyClass(object):
    def __init__(self, name, age):
        print(f"给当前 MyClass 类的对象初始化属性的时候会触发 __init__")
        self.name = name
        self.age = age

    def __call__(self, *args, **kwargs):
        print(f"当前 MyClass 类的对象被调用的时候会触发 __call__")
        return f'当前 MyClass 类 的 __call__ 的返回值 :>>>> {self.name}'

    # 【三】花骨架 有了骨架才能上色
    def __new__(cls, *args, **kwargs):
        print(f"给当前 MyClass 类的对象创建骨架的时候会触发 __new__")

        # 【1】当前类本身
        print(f" MyClass 类 的 __call__ 的 cls :>>>> {cls}")
        # MyClass 类 的 __call__ 的 cls :>>>> <class '__main__.MyClass'>

        # 【2】当前类初始化传的位置参数
        print(f" MyClass 类 的 __call__ 的 args :>>>> {args}")
        # MyClass 类 的 __call__ 的 args :>>>> ('dream',)

        # 【3】当前类初始化传的关键字参数
        print(f" MyClass 类 的 __call__ 的 kwargs :>>>> {kwargs}")
        # MyClass 类 的 __call__ 的 kwargs :>>>> {'age': 18}

        # 【四】调用父类 的 object 的 __new__ 帮我搭建好骨架
        obj = object.__new__(cls)
        # 【1】查看当前返回值发现是一个对象类型
        print(f'obj :>>>> {obj}')
        # obj :>>>> <__main__.MyClass object at 0x000001984B032340>
        # 【2】发现当前对象的民称空间是空的
        print(f'obj.__dict__ :>>>> {obj.__dict__}')
        # obj.__dict__ :>>>> {}
        # 【五】调用自己的 __init__ 方法 初始化属性
        obj.__init__(*args, **kwargs)
        # 【】给自己的名称空间初始化属性
        print(f'obj.__dict__ :>>>> {obj.__dict__}')
        # obj.__dict__ :>>>> {'name': 'dream', 'age': 18}
        return obj


# MyClass 相当于给你一张纸
# 【一】类() 调用 ---> 一定会触发 __init__ 初始化对象的属性 #  __init__  给你人体骨架上个色
# 【二】在调用  __init__  之前要调用 __new__  # __new__ 相当于将你人体的骨架搭建好
m = MyClass('silence', age=18)
# 【六】完成对象属性的初始化
print(m.name)

【二】元类中的__new__

class MyType(type):
    def __init__(cls, class_name, class_bases, class_name_space):
        print(f"给当前 MyType 类的对象初始化属性的时候会触发 __init__")
        super().__init__(class_name, class_bases, class_name_space)

    def __call__(self, *args, **kwargs):
        # 得到一个空的对象
        obj = super().__call__(*args, **kwargs)
        return obj

    # 【三】花骨架 有了骨架才能上色
    def __new__(cls, *args, **kwargs):
        print(f"给当前 MyType 类的对象创建骨架的时候会触发 __new__")

        # 【1】当前类本身
        print(f" MyType 类 的 __call__ 的 cls :>>>> {cls}")
        #  MyType 类 的 __call__ 的 cls :>>>> <class '__main__.MyType'>

        # 【2】当前类初始化传的位置参数
        print(f" MyType 类 的 __call__ 的 args :>>>> {args}")
        #  MyType 类 的 __call__ 的 args :>>>> ('MyClass', (), {'__module__': '__main__', '__qualname__': 'MyClass', '__init__': <function MyClass.__init__ at 0x0000016DE31ACAF0>, '__call__': <function MyClass.__call__ at 0x0000016DE31ACB80>})

        # 【3】当前类初始化传的关键字参数
        print(f" MyType 类 的 __call__ 的 kwargs :>>>> {kwargs}")
        # MyType 类 的 __call__ 的 kwargs :>>>> {}

        # 【四】让你的父类帮你大骨架
        obj = type.__new__(cls, *args, **kwargs)
        print(f'obj :>>>> {obj}')
        # obj :>>>> <class '__main__.MyClass'>

        print(f'obj.__dict__ :>>>> {obj.__dict__}')
        # obj.__dict__ :>>>> {'__module__': '__main__', '__init__': <function MyClass.__init__ at 0x0000023A62C2CB80>, '__call__': <function MyClass.__call__ at 0x0000023A62C2CC10>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}

        return obj


class MyClass(metaclass=MyType):
    def __init__(self, name, age):
        print(f"给当前 MyClass 类的对象初始化属性的时候会触发 __init__")
        self.name = name
        self.age = age

    def __call__(self, *args, **kwargs):
        print(f"当前 MyClass 类的对象被调用的时候会触发 __call__")
        return f'当前 MyClass 类 的 __call__ 的返回值 :>>>> {self.name}'

# MyClass 相当于给你一张纸
# 【一】类() 调用 ---> 一定会触发 __init__ 初始化对象的属性 #  __init__  给你人体骨架上个色
# 【二】在调用  __init__  之前要调用 __new__  # __new__ 相当于将你人体的骨架搭建好
m = MyClass('silence', age=18)
# 【六】完成对象属性的初始化
print(m.name)

Ⅲ 总结

__new__产生一个空对象

整体可以理解为

  • new是买的空地皮,call是准备开发,init才是建造真的楼房
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值