元类
(一)什么是元类
- 创建出类的类就是元类
"""元类"""
# 什么是元类
# 元类:就是产出类的类-----创建出类的类----type
# 在python中一切皆对象
class Person(object):
def __init__(self,name):
self.name=name
# 实例化类得到对象
p=Person(name='syh')
# 查看实例化类得到的对象的数据类型
print(type(p))# <class '__main__.Person'>
# 查看类的数据类型
print(type(Person))# <class 'type'>
# 查看产生字典的数据类型
print(type(dict))# <class 'type'>
# 查看数据类型后,都存在type这个类,所有type这个类就称之为元类
(二)产生类的两种方式
- 创建类的两种方式
- class关键字创建类
- 通过type创建类
(1)class关键字创建类
#class关键字创建类
class Person(object):
person='人类'
def __init__(self,name):
self.name=name
# 查看创建当前类的类
print(type(Person))# <class 'type'>
# 查看当前类的名称空间
print(Person.__dict__)# {'__module__': '__main__', 'person': '人类', '__init__': <function Person.__init__ at 0x00000210D65A2440>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
(2)通过type创建类
- type创建类
- type('类名',(父类1,父类2,....),{属性名:属性值})
# 通过type创建类
# type('类名',(父类1,父类2,....),{数据属性名:数据属性值})
Person=type('Person',(object,),{})
# 查看创建的类的类
print(type(Person))# <class 'type'>
# 查看类的名称空间
print(Person.__dict__)# {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
"""可以在字典中传入 属性:值"""
# 字典中不仅可以添加数据属性,还可以添加函数属性
def read():
...
Person=type('Person',(object,),{'name':'syh','run':read})
# 查看创建的类的类
print(type(Person))# <class 'type'>
# 查看类的名称空间
print(Person.__dict__)# {'name': 'syh', 'run': <function read at 0x0000025D14C12320>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
# 细心地你可能会发现,当我们使用关键字创建类内的方法的时候,查看他的内存空间是带着类名字的
# 而我们当前创建的这个类,初始化进去的只是一个普普通通的函数,也就是非绑定方法
(三)为什么要使用元类
- 元类可以控制类的创建,也就是说 我们可以高度定制类的具体行为
- 简单来说:我们控制了生产的过程,可以在过程中添加内容
(四)元类的基本使用
(1)需求
- 要求所有类的名字必须是大写
(2)思考
- 我们应该在哪里定制这些代码
- 对象的产生过程,是通过类内部的__init__方法实现的
- 那么,在元类的内部也有一个__init__的方法
(3)基本使用
class MyType(type):
def __init__(cls,class_name,class_bases,class_dict):
print(class_name,class_bases,class_dict)
super().__init__(class_name,class_bases,class_dict)
# 元类的使用区别于我们继承中使用父类的那种形式
# 元类的使用采用metaclass关键字声明
# 创建一个类
class MyClass(object,metaclass=MyType):
...
# 初始化类
MyClass()
# MyClass
# (<class 'object'>,)
# {'__module__': '__main__', '__qualname__': 'MyClass'}
(4)进阶使用
- 限制类的名字必须是首字母大写
class MyType(type):
def __init__(cls,class_name,class_bases,class_dict):
print(class_name,class_bases,class_dict)
if not class_name.istitle():
raise Exception(f"类名:{class_name}必须首字母大写")
super().__init__(class_name,class_bases,class_dict)
# 元类的使用区别于我们继承中使用父类的那种形式
# 元类的使用采用metaclass关键字声明
# 创建一个类
# class MyClass(object,metaclass=MyType):
# ...
# # 初始化类
# MyClass()
# # 创建一个类
# class Myclass(object,metaclass=MyType):
# ...
# # 初始化类
# Myclass()# Myclass (<class 'object'>,) {'__module__': '__main__', '__qualname__': 'Myclass'}
# 创建一个类
class myclass(object,metaclass=MyType):
...
# 初初始化类
myclass()# Exception: 类名:myclass必须首字母大写
- 限制类只能按关键字传入如参数
class MyMeta(type):
def __init__(cls, class_name, class_bases, class_dict):
print(f'这是 MyMeta 中的init方法被触发了')
if not class_name.istitle():
raise f'{class_name}必须开头字母大写'
# print(class_name) # MyClass
# print(class_bases) # (<class 'object'>,)
# print(class_dict) # {'__module__': '__main__', '__qualname__': 'MyClass', '__init__': <function MyClass.__init__ at 0x000002A251540360>}
super().__init__(class_name, class_bases, class_dict)
def __call__(self, *args, **kwargs):
print(f'这是 MyMeta 中的 __call__ 方法被触发了')
print(f'这是 MyMeta 中的 {args}')
if args:
raise Exception(f'参数只能按照关键字传参')
print(f'这是 MyMeta 中的 {kwargs}')
obj = super().__call__(*args, **kwargs)
print(f'这是 MyMeta 中的 {obj}')
return obj
# 创建一个新的类:继承元类区别于继承父类
# class MyClass(类名)
class Myclass(object, metaclass=MyMeta):
def __init__(self, name):
print(f'这是 MyClass 中的init方法被触发了')
self.name = name
def __call__(self, *args, **kwargs):
print(f'这是 MyClass 中的 __call__ 方法被触发了')
print(args)
print(kwargs)
# 对象() ---> 触发了实例化得到自己的这个类里面的 __call__ 方法
p = Myclass(name='dream')
p1 = Myclass('hope')
print(p)
print(p1)
# 需求:定制初始化类的时候传参数必须是按照关键字传参,不能按位置传参
# 【1】写一个类 继承元类 type
# 【2】重写 type 的 __call__
# 【3】拦截到 __call__ 带的位置参数和关键字参数
# 【4】限制你的不定长未知参数为空即可
# 【5】继承给 想使用的类
(五)元类的进阶使用
(1)引入
- 类内部定义一个__init__方法,对象加括号会自动执行产生该对象的类里面的__call__方法,并返回对应的返回值
- 类加括号调用,也会触发元类中的__call__方法,获得返回值,这个返回值就是实例化类得到的对象
class MyType(type):
def __init__(cls,class_name,class_bases,class_dict):
print(f"MyType中的init方法")
super().__init__(class_name,class_bases,class_dict)
def __call__(self, *args, **kwargs):
print(f"MyType中的call方法")
# 创建对象并初始化参数
obj=super().__call__(*args,**kwargs)
return obj
class MyClass(metaclass=MyType):
def __init__(self, name):
print(f' MyClass 中的 init 方法 我被触发了')
self.name = name
def __call__(self, *args, **kwargs):
print(f' MyClass 中的 call 方法 我被触发了')
return 'call 方法返回的值'
obj=MyClass(name='syh')
# MyType中的init方法
# MyType中的call方法
# MyClass 中的 init 方法 我被触发了
print(obj)# <__main__.MyClass object at 0x0000019EE59E7D60>
obj()
# MyType中的init方法
# MyType中的call方法
# MyClass 中的 init 方法 我被触发了
# MyClass 中的 call 方法 我被触发了
print(obj())# call 方法返回的值
(2)定制对象的产生过程
class MyType(type):
def __init__(cls, class_name, class_bases, class_dict):
print(f'MyType 中的 init 方法 我被触发了')
super().__init__(class_name, class_bases, class_dict)
def __call__(self, *args, **kwargs):
print(f'MyType 中的 call 方法 我被触发了')
print(f'args: {args}, kwargs: {kwargs}')
# 定制对象的产生条件:只能通过关键字传参创建对象,不允许通过位置传参
if args:
raise TypeError(f'{self.__name__} 产生对象只能通过关键字传参创建')
# 创建对象并初始化参数
obj = super().__call__(*args, **kwargs)
# print(obj)
# 将产生的对象返回出去
return obj
class MyClass(metaclass=MyType):
def __init__(self, name):
print(f' MyClass 中的 init 方法 我被触发了')
self.name = name
def __call__(self, *args, **kwargs):
print(f' MyClass 中的 call 方法 我被触发了')
return 'call 方法返回的值'
# 正确的
obj = MyClass(name='张三')
print(obj)
print(obj())
# MyType 中的 init 方法 我被触发了
# MyType 中的 call 方法 我被触发了
# args: (), kwargs: {'name': '张三'}
# MyClass 中的 init 方法 我被触发了
# <__main__.MyClass object at 0x1287b3dc0>
# MyClass 中的 call 方法 我被触发了
# call 方法返回的值
# 错误的,会被拦截并报错
obj = MyClass('张三')
print(obj)
print(obj())
# raise TypeError(f'{self.__name__} 产生对象只能通过关键字传参创建')
# TypeError: MyClass 产生对象只能通过关键字传参创建
总结:
# 总结 :
# 【】如果你想定制类的产生过程 ---> 编写元类中的 __init__ 方法 (必须类名首字母大写)
# 【】如果你想定制对象的产生过程 ---> 编写元类中的 __call__ 方法 (类初始化必须按关键字传参数)
* __new__方法
# __new__ 方法 :如果在元类里面就是产生空对象
# __init__ 方法 :往里面添加血和肉
class MyMeta(type):
# 填充血肉
def __init__(cls, class_name, class_bases, class_dict):
print(f'MyType 中的 init 方法 我被触发了')
super().__init__(class_name, class_bases, class_dict)
# 生成骨架
def __new__(cls, *args, **kwargs):
print(f'MyType 中的 new 方法 我被触发了')
print(args)
print(kwargs)
obj = type.__new__(cls, *args, **kwargs)
return obj
def __call__(self, *args, **kwargs):
print(f'MyType 中的 call 方法 我被触发了')
print(f'args: {args}, kwargs: {kwargs}')
# 创建对象并初始化参数
obj = super().__call__(*args, **kwargs)
# print(obj)
# 将产生的对象返回出去
return obj
class MyClass(object, metaclass=MyMeta):
def __init__(self, name):
print(f' MyClass 中的 init 方法 我被触发了')
self.name = name
def __call__(self, *args, **kwargs):
print(f' MyClass 中的 call 方法 我被触发了')
return 'call 方法返回的值'
def __new__(cls, *args, **kwargs):
print(f' MyClass 中的 new 方法 我被触发了')
print(args, kwargs)
# return super().__new__(cls)
obj = object.__new__(cls)
# obj.__init__(*args, **kwargs)
return obj
p = MyClass(name='dream')
p()
# MyType 中的 new 方法 我被触发了
# ('MyClass', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'MyClass', '__init__': <function MyClass.__init__ at 0x000001A7BED656C0>, '__call__': <function MyClass.__call__ at 0x000001A7BED65D80>, '__new__': <function MyClass.__new__ at 0x000001A7BED65EA0>})
# {}
# MyType 中的 init 方法 我被触发了
# MyType 中的 call 方法 我被触发了
# args: (), kwargs: {'name': 'dream'}
# MyClass 中的 new 方法 我被触发了
# () {'name': 'dream'}
# MyClass 中的 init 方法 我被触发了
# MyClass 中的 call 方法 我被触发了
标签:__,name,元类,init,call,print,class
From: https://www.cnblogs.com/suyihang/p/17961577