Python元类

默认type元类的底层工作原理:

#方式一:用class关键字去创建,用的就是默认的元类
class People:    #People=type(...)
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def eat(self):
        print('%s is eatting'%self.name)

#查看对象obj的类
obj=People('张三',34)
print(type(obj))
#查看People的类
print(type(People))  #<class 'type'> 说明People的类是

---------------------------------------------------------------------------
#分析默认元类的底层实现原理
class_name='People'
class_bases=(object,)   #元祖格式
class_dic={ }
class_body='''  
country='China'
def __init__(self,name,age):
    self.name=name
    self.age=age

def eat(self):
    print('%s is eatting'%self.name)

'''
利用exec通过类体代码得到class_dic,让字典里面有值
exec(class_body,{},class_dic)   #exec模拟类定义阶段造名称空间的过程
准备好创建类的三要素
print(class_name)
print(class_bases)
print(class_dic)

People=type(类名,基类,类的名称空间)
根据一切皆对象的观念,用type(people)发现People实质是type()一个类实例化得到的结果
验证people是调用type元类实例化得到了这个效果(是用的默认的元类type)
实质:class People():class关键字调用的底层原理
People=type(class_name,class_bases,class_dic) ===   等同于class People():
print(People)

 

 

自定义元类:初始模板

#方式二:用自定义元类
#People=type(class_name,class_bases,class_dic) 默认的元类
class Mymeta(type):  #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    # 重点self就是People类,这里其实就是People=Mymeta(...)实例化得到的一个对象
    #触发init方法的时候,会将people当做第一个参数出入,即self 就是 People
    def __init__(self,class_name,class_bases,class_dic):   #自己定义重写init
        print(self)   #现在就是People这个类  <class '__main__.People'>
        print(class_name) #People
        print(class_bases)  #(<class 'object'>,)
        print(class_dic)  #People类体代码执行过程中产生的一堆名字
        super(Mymeta,self).__init__(class_name,class_bases,class_dic)


# 分析用class自定义类的运行原理(而非元类的的运行原理):
# 1、拿到一个字符串格式的类名class_name='People'
# 2、拿到一个类的基类们class_bases=(obejct,一堆类A,B等..) 基类默认继承object类
# 3、执行类体代码,拿到一个类的名称空间class_dic={...},类体执行过程中产生的名字
# 4、调用People=type(class_name,class_bases,class_dic)
# (继承的父类,metaclass=type默认的元类)
class People(object, metaclass=Mymeta):  # People=Mymeta(class_name,class_bases,class_dic)
    country = 'China'
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def eat(self):
        print('%s is eatting'%self.name)

 

应用一:(控制class定义类的过程)

# 自定义类应用一:自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程(限定类首字母大写,必须要有文档注释)

class Mymeta(type):

    def __init__(self,class_name,class_bases,class_dic):   #自己定义重写init
        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(class_name,class_bases,class_dic)
    '''这是People类'''
    country = 'China'

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

    def eat(self):
        print('%s is eatting'%self.name)

 

 

应用二:

__call__ 实现让对象,变成一个可调用的对象,即元类里面如果想实现people()一定是对象people对应的类中有__call__的方法才行,
默认type元类里面有的

#简单说明下我们分析的开始
# 实现目的: obj()
# class Foo:
#     pass
#
# obj=Foo()
# obj()   #报错: 'Foo' object is not callable
#改进:在Foo内定义一个call方法
# class Foo:
#     def __call__(self, *args, **kwargs):
#         print(self)  #<__main__.Foo object at 0x031A1D30>
#         print(args) # ()
#         print(kwargs) #{}
#
# obj=Foo()
# obj()

 

__call__方法在元类例如的应用实例1:控制对象(指的就是People对象)的产生过程

#思考People也是对象,想要控制People对象的调用过程,就是自定义类的实例化过程,
#即通过控制对象People类Mymeta里面的call方法
class Mymeta(type):

    def __call__(self, *args, **kwargs):  #最终接收到的值只要传给People里面init方法里面用的
        # print(self) # self是People
        # print(args) #('egon',)
        # print(kwargs) #{'age': 18}
        # return 123  如果这里不定义,name print(obj)拿到的返回值就是None
        #1、先造出一个People的空对象
        obj=self.__new__(self)     #People要找new这个属性,默认用type里面的__call__

        #2、为该对空对象初始化独有的属性(为对象初始化功能的属性)
        self.__init__(obj,*args,**kwargs)  #没有自定传值一说,现在是控制底层的创建过程,最终是将值传给People的init用

        #3、返回一个初始好的对象
        return obj

class People(object,metaclass=Mymeta):
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    # def __new__(cls, *args, **kwargs):  #为了验证查找new的顺序(先找自己-》再找类Mymeta-》再找type)
    #     print('People自己的new属性')
    def __new__(cls, *args, **kwargs):
        # print(cls)
        # cls.__new__(cls)  #错误的写法,如果自由里面这样写,会变成死循环
        obj=super(People,cls).__new__(cls)  #可以通过super让其去父类里面找
        return obj


#将people看成是一个对象,people()能调用说明People对象类中有__cal
obj=People('egon',age=18)   #‘egon’->args,
print(obj.__dict__)    #调对象拿到的返回值是None,如果不设置返回值,就是None

 

注意几点:

第一点:__call__:只有在调用对象时才会触发即People()

 其实上面加了元类后,与class直接调用元类没有太多变动,

posted @ 2018-07-16 15:50  yangzhizong  阅读(199)  评论(0编辑  收藏  举报