zoukankan      html  css  js  c++  java
  • Python基础(十一)-面向对象四

    一、上下文管理协议

    1.1、什么叫上下文管理协议?

    with open('a.txt') as f:
      '代码块'

    即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

    with语句小结

    with obj as  f:
        '代码块'
    	
    1)with obj ==>触发obj.__enter__(),拿到返回值
    2)as f ==> f=返回值
    3)with obj as f ==> f=obj.__enter__()
    4) 执行代码块
        一:没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为None
        二:有异常的情况下,从异常出现的位置直接触发__exit__
    	a:如果__exit__的返回值为True,代表吞掉了异常
    	b:如果__exit__的返回值不为True,代表吐出了异常
    	c:__exit__的的运行完毕就代表了整个with语句的执行完毕

    1.2、方法使用

    class Open:
        def __init__(self,name):
            self.name=name
    
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
            return self
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
    
    
    with Open('a.txt') as f:  #触发__enter__方法
        print('=====>执行代码块')
        print(f,f.name)  #<__main__.Open object at 0x0000022813E55C88> a.txt
    

    __exit__()中的三个参数分别代表异常类型异常值追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

    class Open:
        def __init__(self,name):
            self.name=name
    
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
            print(exc_type)
            print(exc_val)
            print(exc_tb)
    
    
    with Open('a.txt') as f:
        print('=====>执行代码块')
        raise AttributeError('异常产生')
    print('0'*100) #------------------------------->不会执行

    如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

    class Open:
        def __init__(self,name):
            self.name=name
    
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
            print(exc_type)    #<class 'AttributeError'>
            print(exc_val)     #产生异常
            print(exc_tb)      #<traceback object at 0x000001E54419AF08>
            return True
    
    
    with Open('a.txt') as f:
        print('=====>执行代码块')
        raise AttributeError('产生异常')
    print('0'*100) #------------------------------->会执行

    模拟open:

    class Open:
        def __init__(self,filepath,mode='r',encoding='utf-8'):
            self.filepath=filepath
            self.mode=mode
            self.encoding=encoding
    
        def __enter__(self):
            print('enter')
            self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)  #拿到文件句柄
            return self.f
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            # print('exit')
            self.f.close()
            return True
        def __getattr__(self, item):
            return getattr(self.f,item)
    
    with Open('a.txt','w') as f:  #f==open(self.filepath,mode=self.mode,encoding=self.encoding)
        print(f)                  #<_io.TextIOWrapper name='a.txt' mode='w' encoding='utf-8'>
        f.write('aaaaaa')
        f.wasdf #抛出异常,交给__exit__处理

    1.3、上下文管理用途

    1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

    2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

    二、描述符

    2.1、什么是描述符?

    描述符本质就是一个新式类,在这个新式类中,至少实现__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
    __get__():调用一个属性时,触发
    __set__():为一个属性赋值时,触发
    __delete__():采用del删除属性时,触发

    class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
        def __get__(self, instance, owner):
            pass
        def __set__(self, instance, value):
            pass
        def __delete__(self, instance):
            pass
    

    2.2、描述符是干什么的?

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

    #描述符Str
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
        def __set__(self, instance, value):
            print('Str设置...')
        def __delete__(self, instance):
            print('Str删除...')
    
    #描述符Int
    class Int:
        def __get__(self, instance, owner):
            print('Int调用')
        def __set__(self, instance, value):
            print('Int设置...')
        def __delete__(self, instance):
            print('Int删除...')
    
    class People:
        name=Str()
        age=Int()
        def __init__(self,name,age): #name被Str类代理,age被Int类代理,
            self.name=name
            self.age=age
    
    
    p1=People('alex',18)  #触发str类的__set__,触发int类的__set__
    print("========")
    
    #描述符Str的使用
    print(p1.name)  #Str调用 None
    p1.name='egon'
    del p1.name
    
    #描述符Int的使用
    p1.age   #Int调用
    p1.age=18  #Int设置...
    del p1.age  #Int删除...
    
    print(p1.__dict__)  #{}
    print(People.__dict__)  #{'__init__': <function People.__init__ at 0x000001714E4CE620>, 'age': <__main__.Int object at 0x000001714E4D46D8>, '__module__': '__main__', 'name': <__main__.Str object at 0x000001714E4D46A0>, '__dict__': <attribute '__dict__' of 'People' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'People' objects>}
    
    
    #补充
    print(type(p1) == People) #True,type(obj)其实是查看obj是由哪个类实例化来的
    print(type(p1).__dict__ == People.__dict__) #True

    2.3、描述符的种类

    1)数据描述符:至少实现了__get__()和__set__()

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

    2)非数据描述符:没有实现__set__()

    class Foo:
        def __get__(self, instance, owner):
            print('get')

    2.4、描述符注意事项

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

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

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

    1. 类属性
    2. 数据描述符
    3. 实例属性
    4. 非数据描述符
    5. 找不到的属性(触发__getattr__)

    2.5、描述符优先级

    1)类属性>数据描述符

    #数据描述符Str
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
        def __set__(self, instance, value):
            print('Str设置...')
        def __delete__(self, instance):
            print('Str删除...')
    
    class People:
        name=Str()
        def __init__(self,name,age): #name被Str类代理
            self.name=name
            self.age=age
    
    #在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典
    People.name  #Str调用 ==>调用类属性name,本质就是在调用描述符Str,触发了__get__()
    People.name="AAA"  #没有触发__set__
    print(People.name)  #AAA
    del People.name   #没有触发__delete__
    
    '''
    原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级
    People.name #调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__()
    
    People.name='egon' #直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__()
    del People.name #同上
    '''

    2)数据描述符>实例属性

    #数据描述符Str
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
        def __set__(self, instance, value):
            print('Str设置...')
        def __delete__(self, instance):
            print('Str删除...')
    
    class People:
        name=Str()
        def __init__(self,name,age): #name被Str类代理
            self.name=name
            self.age=age
    
    p1=People('egon',18)  #触发__set__
    
    #如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
    p1.name="AAA"  #触发__set__
    p1.name        #Str调用
    print(p1.__dict__)  #{'age': 18} ==>并没有name属性 ==>name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
    print(People.__dict__)  #'name': <__main__.Str object at 0x00000202C1DF5D30>
    del p1.name  #Str删除...

    3)实例属性>非数据描述符

    #非数据描述符
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
    
    class People:
        name=Str()
        def __init__(self,name,age): #name被Str类代理
            self.name=name
            self.age=age
    
    p1=People('egon',18) 
    print(p1.name)  #egon   #没有触发__get__方法
    print(p1.__dict__)  #{'age': 18, 'name': 'egon'}
    

    4)非数据描述符>找不到

    class Foo:
        def func(self):
            print('我胡汉三又回来了')
    
        def __getattr__(self, item):
            print('找不到了当然是来找我啦',item)
    f1=Foo()
    
    f1.xxxxxxxxxxx  #找不到了当然是来找我啦 xxxxxxxxxxx
    

    2.6、描述符的使用

    2.6.1、参数类型限制

    python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能

    class Str:
        def __init__(self,name):
            self.name=name
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name=Str('name')
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    p1=People('egon',18,3231.3)  #set---> <__main__.People object at 0x00000274AA9B4668> egon
    
    #调用
    print(p1.__dict__)  #{'salary': 3231.3, 'age': 18, 'name': 'egon'}
    p1.name   #get---> <__main__.People object at 0x000001BCB50B4668> <class '__main__.People'>
    print(p1.name)  #egon
    
    #赋值
    print(p1.__dict__)  #{'age': 18, 'name': 'egon', 'salary': 3231.3}
    p1.name='egonlin'   #触发set
    print(p1.__dict__)  #{'age': 18, 'name': 'egonlin', 'salary': 3231.3}
    
    #删除
    print(p1.__dict__)  #{'salary': 3231.3, 'name': 'egonlin', 'age': 18}
    del p1.name         #触发delete
    print(p1.__dict__)  #{'salary': 3231.3, 'age': 18}

    实例二:

    class Str:
        def __init__(self,name):
            self.name=name
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name=Str('name')
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    # 疑问:如果我用类名去操作属性呢  ==>没有初始化实例
    People.name #报错,错误的根源在于类去操作属性时,会把None传给instance
    
    #修订__get__方法
    class Str:
        def __init__(self,name):
            self.name=name
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name=Str('name')
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    print(People.name)
    
    #输出
    # get---> None <class '__main__.People'>
    # <__main__.Str object at 0x0000020ABCE05D68>

    实例三:

    class Typed:
        def __init__(self,name,expected_type):
            self.name=name
            self.expected_type=expected_type
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            if not isinstance(value,self.expected_type):
                raise TypeError('Expected %s' %str(self.expected_type))
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name=Typed('name',str)
        age=Typed('name',int)
        salary=Typed('name',float)
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    # p1=People(123,18,3333.3)  #TypeError: Expected <class 'str'>
    # p1=People('egon','18',3333.3)  #TypeError: Expected <class 'int'>
    p1=People('egon',18,3333)  #TypeError: Expected <class 'float'>
    

    2.7、类的装饰器

    1)类的装饰器--无参

    def decorate(cls):
        print("类的装饰器")
        return cls
    
    @decorate   #People=decorate(People)
    class People:
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    p1=People("AAA",18,333.33)

    2)类的装饰器--有参

    def typeassert(**kwargs):
        def decorate(cls):
            print('类的装饰器开始运行啦------>',kwargs)
            return cls
        return decorate
    @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
    class People:
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    p1=People('egon',18,3333.3)
    
    #类的装饰器开始运行啦------> {'salary': <class 'float'>, 'age': <class 'int'>, 'name': <class 'str'>}

    3)与描述符结合

    class Typed:
        def __init__(self,name,expected_type):
            self.name=name
            self.expected_type=expected_type
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            if not isinstance(value,self.expected_type):
                raise TypeError('Expected %s' %str(self.expected_type))
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    def typeassert(**kwargs):
        def decorate(cls):
            print('类的装饰器开始运行啦------>',kwargs)
            for name,expected_type in kwargs.items():
                setattr(cls,name,Typed(name,expected_type))
            return cls
        return decorate
    
    @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
    class People:
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    print(People.__dict__)
    #{'age': <__main__.Typed object at 0x000001C81FC155F8>, '__doc__': None, 'name': <__main__.Typed object at 0x000001C81FC156A0>, '__weakref__': <attribute '__weakref__' of 'People' objects>, 'salary': <__main__.Typed object at 0x000001C81FC15668>, '__dict__': <attribute '__dict__' of 'People' objects>, '__init__': <function People.__init__ at 0x000001C81FC0E620>, '__module__': '__main__'}
    p1=People('egon',18,3333.3)
    # p1=People('egon','18',3333.3)  #TypeError: Expected <class 'int'>

    2.8、描述符总结

    描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性

    描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.

    2.9、自定制@property

    利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)

    1)@property回顾

    class Room:
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
        @property
        def area(self):
            return self.width * self.length
    
    r1=Room('AA',1,1)
    print(r1.area)   #1
    

    2)自定制property

    class Lazyproperty:
        def __init__(self,func):
            self.func=func
        def __get__(self, instance, owner):
            print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
            if instance is None:  #如果是类调用,会返回none  ==>Room.area
                return self
            return self.func(instance)
    
    class Room:
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
        @Lazyproperty     #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
        def area(self):
            return self.width * self.length
    
    print(Room.__dict__)   #==>相当于在类中添加了一个属性 ==>'area': <__main__.Lazyproperty object at 0x000001ED1F016C50>}
    r1=Room('alex',1,1)
    print(r1.__dict__)  #{'length': 1, 'width': 1, 'name': 'alex'}
    print(r1.area)   #先在实例属性中查找,没找到到类中查找,属性被描述符代理,触发__get__方法
    print(Room.area)  #<__main__.Lazyproperty object at 0x000001D492CF5358>

    3)实现延迟计算功能

    class Lazyproperty:
        def __init__(self,func):
            self.func=func
        def __get__(self, instance, owner):
            print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
            if instance is None:
                return self
            else:
                print('--->')
                # print(self)  #<__main__.Lazyproperty object at 0x0000024F8E435550>
                # print(self.func)  #<function Room.area at 0x000001E9D740E488>
                value=self.func(instance)  #self.func ===>传进来的area(),需要 将实例传进去 ==>值为1
    
                # print(self.func.__name__)  #area
                setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中
                return value
    
    class Room:
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
        @Lazyproperty #area=Lazyproperty(area) 相当于'定义了一个类属性,即描述符'
        def area(self):
            return self.width * self.length
    
    r1=Room('alex',1,1)
    # print(r1.__dict__)  #{'width': 1, 'name': 'alex', 'length': 1}
    print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后触发了area的__get__方法
    # print(r1.__dict__)  #{'name': 'alex', 'area': 1, 'width': 1, 'length': 1}
    # print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算,直接在实例属性字典中查找

    4)小小改动

    class Lazyproperty:
        def __init__(self,func):
            self.func=func
        def __get__(self, instance, owner):
            print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
            if instance is None:
                return self
            else:
                value=self.func(instance)
                instance.__dict__[self.func.__name__]=value
                return value
            # return self.func(instance)
        def __set__(self, instance, value):   #实现了set方法,此时是数据描述符,优先级高于实例属性
            print('hahahahahah')
    
    class Room:
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
        @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
        def area(self):
            return self.width * self.length
    
    print(Room.__dict__)  #==>'area': <__main__.Lazyproperty object at 0x000001D868D35550>}
    r1=Room('alex',1,1)
    print(r1.area)
    print(r1.area)
    print(r1.area)
    print(r1.area) #缓存功能失效,每次都去找描述符了,因为描述符实现了set方法,它由非数据描述符变成了数据描述符,数据描述符比实例属性有更高的优先级,因而所有的属性操作都去找描述符了
    
    #输出
    这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
    1
    这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
    1
    这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
    1
    这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
    1

    2.10、自制@classmethod

    class ClassMethod:
        def __init__(self,func):
            self.func=func
    
        def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
            def feedback(*args,**kwargs):
                print('在这里可以加功能啊...')
                return self.func(owner,*args,**kwargs)
            return feedback
    
    class People:
        name='AAA'
        @ClassMethod           # say_hi=ClassMethod(say_hi)
        def say_hi(cls,msg):
            print('%s %s' %(cls.name,msg))
    
    print(People.__dict__)  #==>'say_hi': <__main__.ClassMethod object at 0x000001EF2EA16B00>
    People.say_hi('hello')
    
    p1=People()
    p1.say_hi('hello')

    2.11、自制@staticmethod

    class StaticMethod:
        def __init__(self,func):
            self.func=func
    
        def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
            def feedback(*args,**kwargs):
                print('在这里可以加功能啊...')
                return self.func(*args,**kwargs)
            return feedback
    
    class People:
        @StaticMethod         #say_hi=StaticMethod(say_hi)
        def say_hi(x,y,z):
            print('------>',x,y,z)
    
    print(People.__dict__)  #==>'say_hi': <__main__.StaticMethod object at 0x000001C3D63C5518>
    People.say_hi(1,2,3)
    
    p1=People()
    p1.say_hi(4,5,6)

    三、property使用

    3.1、property本质

    一个静态属性property本质就是实现了get,set,delete三种方法

    class Foo:
        @property
        def AAA(self):
            print('get的时候运行我啊')
    
        @AAA.setter
        def AAA(self,value):
            print('set的时候运行我啊')
    
        @AAA.deleter
        def AAA(self):
            print('delete的时候运行我啊')
    
    #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
    f1=Foo()
    f1.AAA   #get的时候运行我啊
    f1.AAA='aaa'  #set的时候运行我啊
    del f1.AAA    #delete的时候运行我啊
    
    #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    
    #第二种写法
    class Foo:
        def get_AAA(self):
            print('get的时候运行我啊')
    
        def set_AAA(self,value):
            print('set的时候运行我啊')
    
        def delete_AAA(self):
            print('delete的时候运行我啊')
        AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应
    
    f1=Foo()
    f1.AAA
    f1.AAA='aaa'
    del f1.AAA

    3.2、property使用

    使用示例一:

    class Goods:
    
        def __init__(self):
            # 原价
            self.original_price = 100
            # 折扣
            self.discount = 0.8
    
        @property
        def price(self):
            # 实际价格 = 原价 * 折扣
            new_price = self.original_price * self.discount
            return new_price
    
        @price.setter
        def price(self, value):
            self.original_price = value
    
        @price.deleter
        def price(self):
            del self.original_price
    
    
    obj = Goods()
    obj.price         # 获取商品价格
    obj.price = 200   # 修改商品原价
    print(obj.price)
    del obj.price     # 删除商品原价

    四、元类

    4.1、元类的引出

    参考文档:https://www.cnblogs.com/linhaifeng/articles/8029564.html

    python中一切都是对象,类本身也是一个对象,当使用class时,解释器会在加载class时就会创建一个对象(这里的对象值的是类而不是实例)

    class Foo:
        pass
    
    f1=Foo()
    
    #使用type()查看类型
    print(type(f1))  #<class '__main__.Foo'>
    print(type(Foo)) #<class 'type'>
    print(type(object))  #<class 'type'>

    4.2、什么是元类

    元类是类的类,是类的模板

    元类是控制如何创建类的,正如类是创建对象的模板一样

    元类的实例是类,正如类的实例是对象(f1对象是Foo类的一个实例,Foo类是type类的一个 实例)

    type是python中一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类的实例

    一个类如果没有声明自己的元类,默认他的类就是type,除了使用元类,用户也可以通过继承type来自定义元类

    4.3、类的创建方式

    方式一:

    class Foo:
        def func(self):
            print("func")
    print(Foo)   #<class '__main__.Foo'>
    print(Foo.__dict__)
    #{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, 'func': <function Foo.func at 0x0000024EDB86E158>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
    

    方式二:

    def func(self):
        print("func")
    x=1
    Foo=type("Foo",(object,),{"func":func,"x":1})
    print(Foo)  #<class '__main__.Foo'>
    print(Foo.__dict__)
    #{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, 'x': 1, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'func': <function func at 0x0000019343BA7F28>}
    

    4.4、自定制元类

    分析版:

    class MyType(type):
        def __init__(self,a,b,c):
            print('元类的构造函数执行')
            # print(a)  #Foo
            # print(b)  #()
            # print(c)  #{'__module__': '__main__', '__init__': <function Foo.__init__ at 0x000001BE2AB9E400>, '__qualname__': 'Foo'}
        def __call__(self, *args, **kwargs):
            print('=-======>')
            print(self)   #<class '__main__.Foo'>
            print(args,kwargs)  #('alex',) {}
            obj=object.__new__(self) #object.__new__(Foo)-->f1  #创建Foo实例化出来的实例f1
            # print(obj)  #<__main__.Foo object at 0x0000020492F45550>
            self.__init__(obj,*args,**kwargs)  #Foo.__init__(f1,*arg,**kwargs),传入参数
            return obj  #返回实例化的对象给f1
    class Foo(metaclass=MyType): #Foo=MyType(Foo,'Foo',(),{})---》__init__
        def __init__(self,name):
            self.name=name #f1.name=name
    print(Foo)   #<class '__main__.Foo'>
    f1=Foo('alex')  #实例() ==>调用父类的__call__方法
    print(f1)       #<__main__.Foo object at 0x00000133525E5550>
    print(f1.__dict__)  #{'name': 'alex'}

    精简版:

    #通过断点分析
    
    class MyType(type):
        def __init__(self,a,b,c):
            print('元类的构造函数执行')
        def __call__(self, *args, **kwargs):
            obj=object.__new__(self)
            self.__init__(obj,*args,**kwargs)
            return obj
    class Foo(metaclass=MyType):
        def __init__(self,name):
            self.name=name
    f1=Foo('alex')
    
    
  • 相关阅读:
    设计模式-简单工厂模式、工厂模式、抽象工厂模式-(创建型模式)
    设计模式-类间关系
    设计模式-六大基本原则
    设计模式-总起
    [转载]常见的移动端H5页面开发遇到的坑和解决办法
    [转载]Angular4 组件通讯方法大全
    【angular5项目积累总结】文件下载
    【转载】Vue 2.x 实战之后台管理系统开发(二)
    [转载]Vue 2.x 实战之后台管理系统开发(一)
    WordPress翻译中 __()、_e()、_x、_ex 和 _n 的用法及区别
  • 原文地址:https://www.cnblogs.com/hujinzhong/p/11499088.html
Copyright © 2011-2022 走看看