反射:

    1、内置函数 hasattr,getattr,setattr,delattr

    2、动态导入模块

    3、 __setattr__,__delattr__,__getattr__

    4、二次加工 二种方式

        包装: 继承+派生

        授权: 类似组生的方式+__getattr__方式

    5、 instance、issubclass、 getattribute

    6、 __getitem__ 、 __setitem__ 、 __delitem__       # 跟*attr属性类似的功能

    7、 __str__ 、 __repr__ 、 __format__

    8、 __slots__ 

    9、 __doc__ 、 __module__ 、 __class__ 

    10、 __del__ 、 __call__

    11、 __iter__ 、 __iter__ 类迭代器

    12、 描述符 __get__ 、 __set__ 、 __delete__ 

    13、 类的装饰器

    14、 模拟 property运行状态

    15、 元类


# 理论知识来源:http://www.cnblogs.com/linhaifeng/articles/6204014.html#_label2


1、反射:

    python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

    四个可以实现自省的函数

    下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

    

    # ------------- 演示类 -------------

class te1:
def __init__(self,name,addr):
    self.name = name
    self.addr = addr    

t1=te1('xiong','bj')
print(t1.__dict__)      # {'name': 'xiong', 'addr': 'bj'}

        #-----------------------------------


hasattr:    #bool值  false,true
# 使用格式: hasattr(实例,'str')  相等于直接调用了t1.name存在就true否则就false
print(hasattr(t1,'name'))   # True
print(hasattr(t1,'22'))     # False

getattr:    # 没有值就会报错
# 使用格式 getattr(class,'str',返回内容)    # 直接获取结果 相等于print(t1.name)
print(getattr(t1,'name'))      # xiong
print(getattr(t1,'nam11e'))     # 当值不存在时,AttributeError: 'te1' object has no attribute 'nam11e'
print(getattr(t1,'nam11e','不存在'))   # 如果有返回值,那么它就直接返回   "不存在"

setattr     # 相等于实例.key=值
# 使用格式 setattr(class,'str','值')
setattr(t1,'xiong','123')       # 相等于 t1.xiong='123'
print(t1.__dict__)              # {'name': 'xiong', 'addr': 'bj', 'xiong': '123'}

# delattr   # 相等于  del 实例.key
# 使用格式 delattr(class,'key')  
delattr(t1,'xiong')       # 相等于 del t1.xiong
print(t1.__dict__)      # 打印结果: {'name': 'xiong', 'addr': 'bj'}

    # 使用反射的好处:

        # 可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能


2、动态导入模块

    # ------------- 一般正常导入模块

        # m1是目录 f1是一个.py文件,里头定义了一个 test函数

from m1 import f1
f1.test()   # 直接返回结果 test

# --------- 使用字符串导入模块
m=__import__('m1.f1')
print(m)        #   # 导入之后不管多少层目录,调用它只能从第一层.t.x.x使用

# 如 m.f1.test()   打印结果: test

# 调用方式二
    m1=importlib.import_module('m1.f1')
    print(m1)       # 打印结果:

# 使用importlib函数的话那么导入完之后它就会在模块那层, 直接使用函数就能返回
# m1.test()    # 打印结果:test


3、类内置方法

class te1:
    def __init__(self,name,addr):
        self.name = name
        self.addr = addr    

    def __getattr__(self, item):
        return '__getattr__运行'

    def __setattr__(self, key, value):
        print('__setattr__运行')
        # self.key = value      # RecursionError: maximum recursion depth exceeded
        self.__dict__[key]=value  # 应当使用它

    def __delattr__(self, item):
        print('__delattr__执行')
        self.__dict__.pop(item)    # 应当使用它直接在类中的字典中操作删除健值对

t1=te1('ge','j')
print(t1.namef)         # 没有这个属性,为false时才会返回__getattr__结果

t1=te1('ade','j')        # 执行self.key = value时会报 RecursionError: maximum recursion depth exceeded错误
t1.x=1
print(t1.__dict__)      # {'name': 'ge', 'addr': 'j', 'x': 1} 会增加一个健值对

del t1.addr             # 执行 __delattr__类内置方法 使用字典pop删除的方式
print(t1.__dict__)      # __delattr__执行 {'name': 'ge', 'x': 1}


4、二次加工 二种方式

    包装: 继承+派生

    授权: 类似组生的方式+__getattr__方式


  包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识

class List(list):
    def append(self, object):
        if type(object) is str:
            super().append(object)
        else:
            print('只能追加 字符串 (str) 类型数据')

    def pop(self, index: int = -1):         # 删除选项有一个默认值 -1
        super().pop(index)

    def helf(self):         # 派生一个方法,取出list中间的索引
    index=int(len(self)/2)
    return self[index]
    
    
li1=list('hello,world')     # 通过标准数据类型打印出来的
print(li1,type(li1))        # ['h', 'e', 'l', 'l', 'o', ',', 'w', 'o', 'r', 'l', 'd'] 

li2=List('hello,world')     # 通过二次加工标准类型打印
print(li2,type(li2))        # ['h', 'e', 'l', 'l', 'o', ',', 'w', 'o', 'r', 'l', 'd'] 

print(li2.helf())           # ,

li2.append('werewr')
print(li2)                  # ['h', 'e', 'l', 'l', 'o', ',', 'w', 'o', 'r', 'l', 'd', 'werewr']

li2.append(11111)       
print(li2)                  # 只能追加 字符串 (str) 类型数据

li2.pop()                   # 不写任何值,默认删除最后的
print(li2)                  # ['h', 'e', 'l', 'l', 'o', ',', 'w', 'o', 'r', 'l', 'd']


# 授权

    授权其实也是一种包装

class FileHander:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.filename = open(filename,mode=mode,encoding=encoding)
        self.mode = mode
        self.encoding = encoding

    def write(self,item):
        print('------> %s'%item)
        self.filename.write(item)

    def __getattr__(self, item):
        # print('没有:【 %s 】方法' %item)
        return getattr(self.filename,item)

f1=FileHander('test.txt','r+')
print(f1.__dict__)                  # {'filename': <_io.TextIOWrapper name='test.txt' mode='r+' encoding='utf-8'>, 'mode': 'r+', 'encoding': 'utf-8'}

print(f1.read)                      # 

f1.write('11111111121\n')           # ------> 11111111121
f1.seek(0)
print('----------->',f1.read())     #  -----------> 11111111121


5、isinstance、issubclass、 getattribute

isinstance # 判断这个实例是否由这个类生成
class Te:
    pass

class TC:
    pass

t1=Te()
t4=TC()

print(isinstance(t1,Te))        # 有就是 True
print(isinstance(t4,Te))        # 如果不是它生成的就是 False

issubclass  # 判断这个类的父类是否正确
class Te:
    pass

class TC(Te):
    pass
class TB:
    pass

print(issubclass(TC,Te))        # 如果Te是Tc的父类就返回 True
print(issubclass(TB,Te))        # 不是就返回 False

getattribute     
# 正常触发条件
    class Get:
        def __init__(self,name):
            self.name = name

        def __getattr__(self, item):    # 不存在就会触发
            print('__getattr__属性 %s'%item)
            
    g1=Get('xi')
    g1.name2        # 当这个属性不存在时会触发getattr

# 不管有没有都会触发__getattribute__
class Get:
    def __init__(self,name):
        self.name = name

    def __getattribute__(self, item):
        print('__getattribute__属性')

g1=Get('xi')        # 不管属性是不是存在,__getattribute__都会触发它
g1.name             # __getattribute__属性
g1.name2            # __getattribute__属性

# 两者同时存在,创建实例跟属性不存在时会返回__getattribute__,属性存在返回__getattr__

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

def __getattr__(self, item):
    print('__getattr__属性 %s'%item)

def __getattribute__(self, item):
    print('__getattribute__属性')
    raise AttributeError('返回错识')

g1=Get('xi')        # 创建实例时正确也会返回 __getattribute__属性
g1.name             # 存在就直接返回__getattr__属性 name


6、 __getitem__ 、 __setitem__ 、 __delitem__

    # 通过 实例.xx 对属性的操作触发的是__*attr__内置函数

    # 通过 []触发的是__*item__内置函数

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

        def __getitem__(self, item):
            print('====> getitem属性 %s'%item)
            return self.__dict__[item]

        def __setitem__(self, key, value):
            print('====> setitem属性')
            self.__dict__[key]=value

        def __delitem__(self, key):
            print('====> delitem属性')
            self.__dict__.pop(key)

    t1=Te1('x')             # 只有带[]的才能调用 *item方法,带.的会触会*attr方法
    print(t1.name)           # x
    print(t1['name'])       # ====> getitem属性 name   有return会返回x
    t1['age']=111           # ====> setitem属性
    print(t1.__dict__)       #{'name': 'x', 'age': 111}
    del t1['age']           # ====> delitem属性
    print(t1.__dict__)       #{'name': 'x'}


7、 __str__ 、 __repr__ 、 __format__

str函数或者print函数--->obj.__str__()

repr在解释器中触发、repr或者交互式解释器--->obj.__repr__()

如果__str__没有被定义,那么就会使用__repr__来代替输出

注意:这俩方法的返回值必须是字符串,否则抛出异常

    # 自定制格式字符串
    class St:
        def __init__(self,inp):
            self.inp = inp

        # def __str__(self):
        #     return '输入的字符串 %s' %self.inp

        def __repr__(self):
            return 'repr输入的字符串 %s '%self.inp
    s1=St('xxxxxx')

    # 当__str__没有定义,那么会使用__repr__来代替输出  注释str时输出 : repr输入的字符串 xxxxxx
    print(s1)   # 其实它相当于执行了 print(str(s1)) 跟 print(s1.__str__())
    # __format__ 格式化

    format_dic = {              # 将需要格式化的字符串定义在此
        'ymd': '{0.year}{0.mon}{0.day}',
        'y-m-d': '{0.year}-{0.mon}-{0.day}',
        'y:m:d': '{0.year}:{0.mon}:{0.day}',
    }

    class Data:
        def __init__(self,year,mon,day):
            self.year = year
            self.mon = mon
            self.day = day

        def __format__(self, format_spec):
            # print(format_spec)      # TypeError: __format__ must return a str, not NoneType 必须要有一个返回值
            if not format_spec or format_spec not in format_dic:
                return format_dic['ymd'].format(self)
            fmr=format_dic[format_spec]
            return fmr.format(self)
    d1=Data(2017,12,28)
    print(format(d1))           #20171228
    print(format(d1,'y:m:d')) # 2017:12:28

8、 __slots__        

    # 使用好处: 节省内存地址,字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__

    # __slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)

    # 使用坏处: 使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

    #.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不在支持一些普通类特性了,比如多继承。大多数情况下,你应该关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。           更多的是用来作为一个内存优化工具。

    class Sl:
        __slots__ = 'name'

    s1=Sl()
    s1.name='xiong'
    print(s1.name)
    # 使用slots就无法再定义字典了 查看直接使用__slots__
    # print(s1.__dict__)  # AttributeError: 'Sl' object has no attribute '__dict__'
    print(s1.__slots__)


9、 __doc__ 、 __module__ 、 __class__ 

    __doc__ 是不能被继承的, 原因: 因为每个类都会自动先添加一个__doc__属性会覆盖继承的属性

    __module__ # 查看类是由哪个模块产生的

    __class__  # 查看类

    # 说明 m1/xx.py
    class Xx:
        def __init__(self):
            self.inp = 'ok'

    # 跟m1同级目录
    from m1.xx import  Xx

    X1=Xx()             # 实例化
    print(X1.inp)       
    print(X1.__module__)    # 查看这个实例是由哪个模块产生         # m1.xx
    print(X1.__class__)     # 查看这个实例是由哪个模块下的类产生   # 


10、 __del__ 、 __call__

    # __del__  析构方法,当对象在内存中被释放时,自动触发执行

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

    def __del__(self):
        print('__删除一个算一个__')

    X1=Xg('xxx')
    print(X1.name)  # xxx    直接打印会发现del竟然也自动执行了, 是因为当整个文件执行完之后, 内存回收会再次执行析构函数
                    # __删除一个算一个__

    # __del__# 只在实例被删除的时候才会触发

    del X1   # __删除一个算一个__

    __call__  # 触发执行
    class Foo:
    def __call__(self, *args, **kwargs):
        print('call方法')

    f1=Foo()    
    f1()        # 一个对象加一个小括号,可以去执行,执行是调用下面类的__call__方法    打印结果: call方法
    Foo()       # 执行类方法()就等相于也执行了 父类的__call__方法


11、 __iter__ 、 __iter__ 类迭代器

    __iter__    # 将对象变成可迭代对象, 在类中必须有一个__iter__方法 

    __next__    # 有iter方法必须得要有一个next方法返回值 


    # 说明: 迭代器一定得要有 iter跟next iter将对象变成可迭代对象,next返回可迭代对象值

    class Foo:
        def __init__(self,inp):
            self.inp = inp
    f=Foo(10)               # 类中没有iter方法,说明不是一个迭代器
    print(f.__iter__())     # AttributeError: 'Foo' object has no attribute '__iter__'
    class Foo:
        def __init__(self,inp):
            self.inp = inp
        def __iter__(self):
            return self

    f=Foo(10)
    print(f.__iter__())     # 将类变成可迭代对象,会直接return一个对象地址 <__main__.Foo object at 0x00000000024C1F60>
    print(f.__next__())     # AttributeError: 'Foo' object has no attribute '__next__'

    class Foo:
        def __init__(self,inp):
            self.inp = inp
        def __iter__(self):
            return self
        def __next__(self):
            if self.inp == 12:          # 当self.inp值等于12时,会直接返回迭代器的stopiteration
                raise StopIteration('迭代器停止....')
            self.inp+=1
            return self.inp

    f=Foo(10)
    print(f.__iter__())     # <__main__.Foo object at 0x000000000248D940>
    print(f.__next__())     # 11   next值每次加1,这个值在内存中只能往前, 往后除非代码控制
    print(f.__next__())     
    print(f.__next__())     # raise StopIteration('迭代器停止....')  \n StopIteration: 迭代器停止....

    # 斐波那契数列
    class Fb:
        def __init__(self,inp):
            self.inp = inp
            self._na = 1
            self._nb = 1

        def __iter__(self):
            return self

        def __next__(self):
            if self._na > self.inp:
                raise StopIteration('终止咯')
            self._na,self._nb = self._nb,self._na+self._nb
            return self._na

    def inp():
        inp=int(input('请输入最大的数值: '))
        F1=Fb(inp)
        for i in F1:
            print(i,end=' ')
    inp()           # 打印结果: 1 2 3 5 8 13 21 34 55 89 144


12、 描述符 __get__ 、 __set__ 、 __delete__ 

    # 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议

    __get__():调用一个属性时,触发

    __set__():为一个属性赋值时,触发

    __delete__():采用del删除属性时,触发


    # 描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

     class Foo:
        def __get__(self, instance, owner):
            print('get方法')
        def __set__(self, instance, value):
            print('set方法')
        def __delete__(self, instance):
            print('delete方法')

    class Bar:  # 描述符的作用是用来代理另外一个类的属性的
        f=Foo()     # 类属性就是描述符的对象

    b1=Bar()
    b1.f        # get方法
    b1.f=1      # set方法
    del b1.f    # delete方法

    注意事项:

    一 描述符本身应该定义成新式类,被代理的类也应该是新式类

    二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中

    三 要严格遵循该优先级,优先级由高到底分别是

       1.类属性                           # 代理的类,如Bar下的f   

       2.数据描述符                       # __get__ 、 __set__ 、 __delete__

       3.实例属性                         # b1=Bar()

       4.非数据描述符                     # 没有__set__描述符的情况就是非数据描述符

       5.找不到的属性触发__getattr__()

    # 类属性 > 数据描述符
        class Foo:
            def __get__(self, instance, owner):
                print('get方法')
            def __set__(self, instance, value):
                print('set方法')
            def __delete__(self, instance):
                print('delete方法')

        class Bar:
            f=Foo()     # 类属性就是描述符的对象

        Bar.f         # get方法 调用类属性,本质就是要调用__get__描述符,触发__get__方法
        # Bar.f=1     # 打印为空, 首先类会先检查自己的属性字典,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__()
        del Bar.f     # 同上,它拥有更高的优先级,直接覆盖了描述符,也就不会触发描述符了

    # 数据描述符 > 实例属性
        b1=Bar()    # 创建一个实例对象
        b1.f        # get方法 b1的属性字典没有f,因为f是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关
        b1.f=1      # set方法
        del b1.f    # delete方法

    # 实例属性 > 非数据描述符  
        class Foo:
            def __get__(self, instance, owner):
                print('get方法')

        class Bar:
            f=Foo()

        b1=Bar()
        b1.f        # 描述符大于实例属性
        b1.f=1
        print(b1.f) # 实例属性大于描述符属性,毕竟都没有这个属性值
        del b1.f    # 同上
########### 使用 普通方式 ###########
        class Descriptor:
            def __get__(self, instance, owner):
                print('get方法')
                print('instance的值【%s】' %instance)       # instance的值【<__main__.Foo object at 0x000000000275A6D8>】
                print('owner的值是【%s】' %owner)            # owner的值是【】
            def __set__(self, instance, value):
                print('set方法')
                print('instance的值【%s】' % instance)      # instance的值【<__main__.Foo object at 0x000000000275A6D8>】
                print('value的值是【%s】' % value)           # value的值是【xxx】

        class Foo:
            name = Descriptor()     # 定义类属性
            def __init__(self,name,age,interest):
                self.name = name
                self.age = age
                self.interest = interest

        F1=Foo('xxx',123,'money')        # 定义一个实例属性
        print(F1)
        F1.name
        print(F1.__dict__)      # {'age': 123, 'interest': 'money'}  发现这里没有name的值了 因为name就是描述符的对象
########### 使用 定义格式 ###########
    class Descriptor:
        def __init__(self,key):
            self.key = key
        def __get__(self, instance, owner):
            print('get方法')
            return instance.__dict__[self.key]
        def __set__(self, instance, value):
            print('set方法')
            if isinstance(value,str):
                instance.__dict__[self.key]=value
            else:
                raise TypeError('定义的值有问题,请重新输入')    # 当输入的错不是字符串类型的时候直接吐出错误,退出程序
    class Foo:
        name = Descriptor('name')     # 定义类属性
        def __init__(self,name,age,interest):
            self.name = name
            self.age = age
            self.interest = interest

    F1=Foo('xxx',123,'money')        # 定义一个实例属性
    print(F1)
    print(F1.name)
    print(F1.__dict__)
# 如果是非字符类型的时候它会直接吐出错误,直接退出程序
    F1=Foo(111,123,'money')   
    raise TypeError('定义的值有问题,请重新输入')
    TypeError: 定义的值有问题,请重新输入
########### 判断类型 ###########
class Descriptor:
        def __init__(self,key,exce_type):
            self.key = key
            self.exce_type = exce_type
        def __get__(self, instance, owner):
            print('get方法')
            return instance.__dict__[self.key]
        def __set__(self, instance, value):
            print('set方法')
            if isinstance(value,self.exce_type):
                instance.__dict__[self.key]=value
            else:
                # 当输入的错不是字符串类型的时候直接吐出错误,退出程序
                raise TypeError('%s定义的值有问题,请重新输入%s'%(self.key,self.exce_type))
    class Foo:
        name = Descriptor('name',str)     # 定义类属性
        age = Descriptor('age',int)     # 定义类属性

        def __init__(self,name,age,interest):
            self.name = name
            self.age = age
            self.interest = interest
# 打印结果
F1=Foo('xx',11,'money')        # 定义一个实例属性
print(F1.__dict__)      #{'name': '111', 'age': 11, 'interest': 'money'}

F1=Foo(11,11,'money')        # 定义一个实例属性
print(F1.__dict__)      #TypeError: name定义的值有问题,请重新输入

F1=Foo('ag','11','money')        # 定义一个实例属性
print(F1.__dict__)      #TypeError: age定义的值有问题,请重新输入


12、 __enter__ 、 __exit__ 上下文操作

# 上下文操作
    class File:
        def __init__(self,name):
            self.name = name

        def __enter__(self):
            print('执行enter方法')

        def __exit__(self, exc_type, exc_val, exc_tb):
            print('执行exit方法')
    # with File('a.txt')调用的是enter方法 赋值给x  打印结果: 'enter方法'
    with File('a.txt') as x:
        print('------------>')      # 执行完这个操作之后
        # 触发的是exit方法   打印结果: 执行exit方法

    exc_type: 异常类性
    exc_val :异常值
    exc_tb  :异常追踪信息

        with obj as  f:
            '代码块'
    
    1.with obj ----》触发obj.__enter__(),拿到返回值

    2.as f----->f=返回值、

    3.with obj as f  等同于     f=obj.__enter__()
    class Foo:
        def __init__(self,name):
            self.name=name
        def __enter__(self):
            print('enter方法')
            return self
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('exit方法')

    with Foo('a.txt') as f:
        print('1233')
        print(f)            # <__main__.Foo object at 0x00000000024CA3C8>
        print(f.name)       # 返回值a.txt
    4.执行代码块
    一:没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为None
    二:有异常的情况下,从异常出现的位置直接触发__exit__
        a:如果__exit__的返回值为True,代表吞掉了异常
                def __exit__(self, exc_type, exc_val, exc_tb):
                    print('exit方法')
                    # 当为False的时候
                    print(exc_tb)         # Traceback (most recent call last):
                                          # NameError: name 'xxx' is not defined
                    return True
            with Foo('a.txt') as f:
                print('1233')
                print(xxx)            # 当为true的时候 tracback直接打印内存地址 

        b:如果__exit__的返回值不为True,代表吐出了异常
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('exit方法')     
            # 当为False的时候  
            print(exc_tb)         # Traceback (most recent call last):
                                  # NameError: name 'xxx' is not defined

        c:__exit__的的运行完毕就代表了整个with语句的执行完毕


13、类的装饰器

    def Li(*args,**kwargs):
    def wapper(func):
        print('------',kwargs)      # ------ {'a': 1, 'b': 2}
        for key,val in kwargs.items():
            setattr(func,key,val)
        return func
    return wapper

    @Li(a=1,b=2)
    class Foo:      # 一切皆对象,对象文件都可以作为装饰器的参数传递
        pass        

    print(Foo.__dict__)     # 'a': 1, 'b': 2
##### 结合描述符与装饰器的写法 #####
# 描述符
    class Descriptor():
        '''
          1、先定义 数据描述符 get set delete
          2、再定义 数据描述符中的返回值参数,需要接收一个值用来做为key
          3、判断实例中参数的类型比如 name为str类型,传入错误就直接退出程序
        '''
        def __init__(self,key,exce_type):
            self.key = key
            self.exce_type = exce_type
        def __get__(self, instance, owner):
            return instance.__dict__[self.key]
        def __set__(self, instance, value):
            if isinstance(value,self.exce_type):
                instance.__dict__[self.key]=value
            else:
                raise TypeError('%s 输入的类型应为 %s' %(self.key,self.exce_type))
        def __delete__(self, instance):
            del instance.__dict__[self.key]
    # 装饰器
    def Decorate(**kwargs):
        '''
         简化Base_type类传于参数 比如     name = Descriptor('name',str)
         1、首先先搭好高阶函数架子,两个return 最里的返回类的地址,最外的接收它并传递到类中
        '''
        def warpper(func):
            for key,val in kwargs.items():      # 将字典传入拆开
                setattr(func,key,Descriptor(key,val))   # 反射
            return func
        return warpper
    # 基础参数
    @Decorate(name=str,age=int)
    class Base_type:
        def __init__(self,name,age):
            self.name = name
            self.age = age

    # B1=Base_type('ag','11')     # TypeError: age 输入的类型应为 
    B2=Base_type('ag',11)
    print(B2.__dict__)            #{'name': 'ag', 'age': 11}


14、 模拟 property运行状态

  # 说明 property 对象静态属性
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-

    class Lproperty:
        def __init__(self,func):
            self.func = func
        def __get__(self, instance, owner):
            if instance is None:
                return self
            return self.func(instance)

    class cat:
        def __init__(self,name,length,wide,hight):
            self.name = name
            self.length = length
            self.wide = wide
            self.hight = hight

        # @property       # 相当于执行了 area=property(area)
        @Lproperty
         # 1、相当于实例化了 area=Lproperty(area)一个对象
         # 2、改变了area的运行状态,类属性调用area参数的时候
         # 3、发现全局类中area指向的地址是Lproperty、然后又执行了一次 非数据描述符
         # 4、最后执行area方法返回对象结束到get方式,并执行 相等于 C1.area.func(C1)
         # 5、直接使用类调用area方法 instance参数会直接返回None传递到area方法中,报错 AttributeError: 'NoneType' object has no attribute 'length'
         # 6、 get描述符中的instance判断为None,直接返回return self       #<__main__.Lproperty object at 0x000000000249A6D8>
        def area(self):
            return self.length * self.wide

    C1=cat('ba',500,400,210)
    print(C1.area)


15、元类

    # 普通元类
    class Foo:
        pass
    # 类的元类就是类 type
    print(type(Foo))        #

    def __init__(self,name,age):
        self.name = name
        self.age = age
    # print(type(Fo))         # 
    # print(Fo.x)             # 2
    # 使用元类创建一个初始化函数
    def __init__(self,name,age):
        self.name = name
        self.age = age
    # 使用type元类定义类
    Fo=type('Fo',(object,),{'x':2,'__init__':__init__})
    F1=Fo('xiong',100)
    print(F1.__dict__)          #{'name': 'xiong', 'age': 100}
# 元类的用法
    # 定义一个元类,父类为元类
    class Mytype(type):
    #     def __init__(self):     # TypeError: __init__() takes 1 positional argument but 4 were given
    #         print('元类')

        def __init__(self,funcname,obj,dict):
            print('元类')
            # print('-a----> ',funcname)   #-a---->  Foo 返回值为类名
            # print('-b----> ',obj)   # -b---->  () 父类是object 为空的默认值就是object
            # print('-c----> ',dict)   # -c---->  {'__module__': '__main__', '__qualname__': 'Foo', '__init__': }
        def __call__(self, *args, **kwargs):
            obj=object.__new__(self)  # 这里返回的就是直接实例名obj = Foo  F1=Foo
            self.__init__(obj, *args, **kwargs)   #  *args, **kwargs 怎么传过来的怎么导入
            return obj            # 返回Foo实例值

    # 元类是mytype
    class Foo(metaclass=Mytype):
        def __init__(self,name):
            self.name = name

    # F1=Foo('xi')        # TypeError: 'Mytype' object is not callable 原因:元类中没有call方法调用
    F1=Foo('yi')         # <__main__.Mytype object at 0x000000000291DC18>  它执行到catt方法时就直接返回值
    print(F1.__dict__)