元类metaclass


元类是什么,用于创建类的类

万物皆对象,类当然也是对象

对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是有另一个类实例化产生的

默认情况下所有类的元类都是type
证明:例如A类 print(type(A))


学习元类的目的:
高度的自定义一个类,例如控制类的名字必须以大驼峰的方式来书写

类也是对象,也有自己的类,

我们的需求是创建类对象做一些限制

想到了初始化方法 我们只要找到类对象的类(元类),覆盖其中 init方法就能实现需求

当然我们不能修改源代码,所以应该继承type来编写自己的元类,同时覆盖init来完成
需求
```python
# 直接调用type类来产生类对象
# 一个类的三个基本组成部分
# 1.类的名字(字符类型) 2.类的父类们 (是一个元组或列表) 3.类的名称空间(字典类型)
cls_obj = type("dog",(),{})
print(cls_obj)
```

只要继承了type 那么这个类就变成了一个元类
```python
#先看type的初始化方法
# 自己 类名 基类们 类的名称空间
#def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
c代码


# 定义了一个元类
class MyType(type):
def __init__(self,clss_name,bases,dict):
super().__init__(clss_name,bases,dict)
print(clss_name,bases,dict)
if not clss_name.istitle():# clss_name.istitle()判断类名是否首字母为大写,其余小写
raise Exception("你丫的 类名不会写...")

# 为pig类指定了元类为MyType
class Pig(metaclass=MyType): #声明Pig的元类为MyType,即执行了MyType的__init__
pass
#等同于MyType("Pig",(),{})

class Duck(metaclass=MyType):
pass
#等同于MyType("Duck",(),{})

MyType("pig",(),{}) #由于首字母没有大写触发异常
```

### 元类与双下__call__

**使用场景:**
当你想要控制对象的创建过程时,就覆盖__call__方法
当你想要控制类的创建过程时,就覆盖__init__方法

案例
```python
#继承type类
class MyMeta(type):

def __init__(self,name,bases,dict):
super().__init__(name,bases,dict)#调用type类的初始化方法
print("init run")

def __call__(self, *args, **kwargs):
print("元类 call run")
# print(self)
# print(args)
# print(kwargs)
return super().__call__(*args,**kwargs) #必须执行type的__call__,并返回


class Dog(metaclass=MyMeta): # === Dog = MyMate("Dog",(),{})

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

def __call__(self, *args, **kwargs):
print("call run")

# d = Dog("大黄")
d = Dog("大黄") #Dog类+():触发Dog类的元类中MyMeta__call__方法
print(d.name)
```
注意:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象

 

设计模式:
用于解决某种固定问题的套路
单例:指的是一个类产生一个对象
为什么要使用单例: 单例是为了节省 资源,当一个类的所有对象属性全部相同时,则没有必要创建多个对象
### 双下__call__的单例模式

```python
# 单例元类
# 想要一个类的实例只有一个,可以让此类指定一个元类,并在元类的__call__方法中,记录此类的obj属性值
# 只要此类做过实例化操作,则此类的obj属性有值
class Single(type):
def __call__(self, *args, **kwargs):
if hasattr(self,"obj"): #判断是否存在已经有的对象
return getattr(self,"obj") # 有就返回

obj = super().__call__(*args,**kwargs) # 没有则创建
print("new 了")
self.obj = obj # 并存入类中
return obj
#Person类只有一个实例
class Person(metaclass=Single):
pass

# 只会创建一个对象
Person()
Person()
```

 

### 双下__new__
当你要创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对这个类进行初始化操作
注意:,如果你覆盖了该方法则必须保证,new方法必须有返回值且必须是,对应的类对象
```python
class Meta(type):

def __new__(cls, *args, **kwargs):
print(cls) # 元类自己
print(args) # 创建类需要的几个参数 类名,基类,名称空间
print(kwargs) #空的
print("new run")
# return super().__new__(cls,*args,**kwargs)
obj = type.__new__(cls,*args,**kwargs)
return obj

def __init__(self,a,b,c):
super().__init__(a,b,c)
print("init run")

class A(metaclass=Meta): #相当于 A = Mate("A",(),{}) 触发__new__
pass
```
总结new方法和init 都可以实现控制类的创建过程,init更简单

### 双下__new__的单例模式

``` python
class A:
__instance = False #私有属性
def __init__(self,name,age):
self.name = name
self.age = age
def __new__(cls, *args, **kwargs):
# __new__为构造方法,__init__初始化之前 先执行的就是构造方法,cls默认参数代表本类,此时还没有self,执行的结果即返回self,

if cls.__instance: #第二次进来才满足此条件
return cls.__instance
cls.__instance = object.__new__(cls) #第一次进来 ,触发object类里的__new__并传入本类,创造出本类的子类

return cls.__instance #返回子类,即self

egon = A('egg',38)
egon.cloth = '小花袄'
nezha = A('nazha',25)
print(nezha)
print(egon)
print(nezha.name)
print(egon.name)
print(nezha.cloth)
```

701a74841d84365558729585b812591c.png  
```
补充:6触发7时,是Room.__init__(obj,) 本质是obj触发了__init__,把obj传给了self,就把name属性封装到obj的属性字典中了
Room()整个过程是在执行__call__,而call如果不返回值,那么r1接收的就是None
类产生实例,并赋予实例属性的原理

所有类的元类为Type,没有自定义时,默认就这个
所有类继承自object,object有个__new__方法,能产生实例,然后把self即Room传给他

自定义元类注意:自定义的类+()不会代表执行,执行的操作要自定义,赋值属性存到属性字典也要自定义,不会自动存的
```

posted @ 2019-08-14 15:07  坚持fighting  阅读(119)  评论(0编辑  收藏  举报