元类

'''
1、什么是元类
    在python中一切皆对象,那么我们用class关键字定义的类本身也是一个对象
    负责产生该对象的类称之为元类,即元类可以简称为类的类
    class Foo: # Foo=元类()        #一切皆对象,类加括号产生对象
        pass
2、为何要用元类
    元类是负责产生类的,所以我们学习元类或者自定义元类的目的
    是为了控制类的产生过程,还可以控制对象的产生过程
3、如何用元类
'''
#1、储备知识:内置函数exec的用法
cmd="""
x=1
def func(self):
    pass
"""
class_dic={}
exec(cmd,{},class_dic)     #exec会将cmd字符串中的代码拿出来执行一次,将产生的名字丢掉事先定义好的class_dic空字典中
print(class_dic)            #{'x': 1, 'func': <function func at 0x00000267165F92F0>}

#2、创建类的方法有两种
# 大前提:如果说类也是对象的话,那么用class关键字去创建类的过程也是一个实例化的过程
# 该实例化的目的是为了得到一个类,调用的是元类
#2.1 方式一:用的默认的元类type
class People: #People=type(...)--------默认的元类type实例化出一个对象Pelple,实例化的结果也是一个对象
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def eat(self):
        print('%s is eating' %self.name)
peo=People('EGON',18)
print(peo)               #------------<__main__.People object at 0x000001F635282E10>*********调用类实例化出对象
print(type(People))      #------------<class 'type'>*****************************************调用元类实例化出类
 
"""重点"""
#2.1.1 创建类的3个要素:类名,基类,类的名称空间
class_name='People'         #类名,是一个字符串,---------由上面的class定义类我们知道,创建类的三要素:类名,基类,类的名称空间
class_bases=(object,)       #基类,----------------------我们通过__bases__,知道基类是一个元组的形式
class_dic={}                #类的名称空间,---------------通过__dict__,知道类的名称空间的是一个字典
class_body="""             
country='China'
def __init__(self,name,age):
    self.name=name
    self.age=age
def eat(self):
    print('%s is eating' %self.name)
"""                          #--------将类体代码放到一个字符串中
exec(class_body,{},class_dic)#执行字符传中的代码,将产生的名字方到class_dic的名称空间中,即之前定义类将产生的名字放到类的名称空间中
# 准备好创建类的三要素
# print(class_name)        #-------People
# print(class_bases)       #-------(<class 'object'>,)
# print(class_dic)         #-------{'country': 'China', '__init__': <function __init__ at 0x00000222D55F92F0>, 'eat': <function eat at 0x00000222DC618BF8>}
# People=type(类名,基类,类的名称空间)                #调用元类就可以产生一个类这个对象
People1=type(class_name,class_bases,class_dic)      #将事先定义好的类的三要素放到当做参数传给元类,调用元类即产生对象
print(People1)             #--------<class '__main__.People'>自定义类产生的结果
obj1=People1('egon',18)
print(People)              #--------<class '__main__.People'>,class定义类产生的结果
obj=People('egon',18)
obj1.eat()
obj.eat()

"""----------------------------------------重点----------------------------------------"""
#2.2 方式二:用的自定义的元类
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    def __init__(self,class_name,class_bases,class_dic):
        print(self) #现在是People
        print(class_name)
        print(class_bases)
        print(class_dic)
        super(Mymeta,self).__init__(class_name,class_bases,class_dic) #重用父类的功能
# 分析用class自定义类的运行原理(而非元类的的运行原理):
#1、拿到一个字符串格式的类名class_name='People'
#2、拿到一个类的基类们class_bases=(obejct,)
#3、执行类体代码,拿到一个类的名称空间class_dic={...}------------------前三步就是造类的三要素
#4、调用People=type(class_name,class_bases,class_dic)----------------调用元类(类)产生类(对象)------------调用类产生对象
class People(object,metaclass=Mymeta): #People=Mymeta(类名,基类们,类的名称空间)------metaclass=Mymeta是自定义的元类名
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def eat(self):
        print('%s is eating' %self.name)
"""----------------------------------------重点----------------------------------------"""
 
 
"""应用:自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程------(对象的产生过程就是调用类的过程)"""
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类---------------必须要继承type类
    def __init__(self,class_name,class_bases,class_dic):                                      #在自定义类之上添加逻辑判断
        if class_dic.get('__doc__') is None or len(class_dic.get('__doc__').strip()) == 0:    #必须有文档注释,且不为空
            raise TypeError('类中必须有文档注释,并且文档注释不能为空')
        if not class_name.istitle():                                                          #类的首字母必须大写
            raise TypeError('类名首字母必须大写')
        super(Mymeta,self).__init__(class_name,class_bases,class_dic)                         #重用父类的功能
class People(object,metaclass=Mymeta): #People=Mymeta('People',(object,),{....})
    """这是People类"""
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def eat(self):
        print('%s is eating' %self.name)
 
#3 储备知识:__call__
class Foo:
    def __call__(self, *args, **kwargs):
        print(self)      #<__main__.Foo object at 0x000002193E892E10>
        print(args)       #(1, 2, 3)----------------*args接收位置参数,存成元组的形式
        print(kwargs)     #{'x': 1, 'y': 2}---------**kwargs接收关键字参数,存成字典的形式

obj=Foo()          #调用类不会自动触发,会在调用对象时自动触发,通过self也可以看出,是调用对象时自动触发
# # 要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法__call__方法
# # 该方法会在调用对象时自动触发
# obj(1,2,3,x=1,y=2) #调用对象时自动触发__call__方法,并将对象自动传入
 
"""-----------------------------------------------重点---------------------------------------------"""
# 4、自定义元类来控制类的调用的过程,即类的实例化过程
class Mymeta(type):
    def __call__(self, *args, **kwargs):          #会在调用对象时自动触发,此时的对象时一个类,即People
        # print(self) # self是People
        # print(args)
        # print(kwargs)
        # return 123
        """调用类产生一个对象,发生两件事"""       #和class定义类,调用类一样发生两件事
        # 1、先造出一个People的空对象
        obj=self.__new__(self)                   #造出了一个自定义类People的空对象
        # 2、为该对空对象初始化独有的属性
        # print(args,kwargs)
        self.__init__(obj,*args,**kwargs)        #对空对象进行初始化,空对象传入,以及参数原封不动的传入
        # 3、返回一个初始好的对象
        return obj                               #将造出的对象返回,
'''**********************************看成一个对象************************************************'''
class People(object,metaclass=Mymeta):           #自定义类People,元类是Mymeta,元类必须继承type类,否则就不是元类
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def eat(self):
        print('%s is eating' %self.name)
    def __new__(cls, *args, **kwargs):           #对象自己中有__new__属性,先从对象自己的名称空间中找,自己没有在到自己的类中找
        print(cls)
        # cls.__new__(cls) # 错误                 #自己有调用了自己的__new__,这样就出现无线递归,所以会报错
        obj=super(People,cls).__new__(cls)       #自己中有,我们任然让其取继承父类中的__new__属性,来产生一个空对象,然后将对象初始化,拿到一个返回值
        return obj
'''**********************************看成一个对象************************************************'''
"""-----------------------------------------------重点---------------------------------------------"""
 
# 分析:调用Pepole的目的
#1、先造出一个People的空对象
#2、为该对空对象初始化独有的属性
# obj1=People('egon1',age=18)
# obj2=People('egon2',age=18)
# print(obj1)
# print(obj2)
# obj=People('egon',age=18)
# print(obj.__dict__)
# print(obj.name)
# obj.eat()
posted @ 2019-07-31 21:34  FFLYY  阅读(132)  评论(0)    收藏  举报