元类
Ⅰ 元类
【一】元类简介
# 元类
即产生类的类叫做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才是建造真的楼房
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY