python基础14-__enter__/__exit__/描述符管理实例的实参类型控制/类的装饰器/自定制property/自定义元类

  • 上下文管理协议

  • 操作文件有三步骤
  1. 打开文件,赋值给一个文件句柄
  2. 文件操作
  3. 关闭文件
  • with open('a.txt') as f:#open()也是工厂函数,用这种方式不用关闭函数
  • 应用场景,文件,网络连接,也需要打开关闭操作,和锁的编程环境
  • 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')
            print(exc_type)#异常类
            print(exc_val)#异常值
            print(exc_tb)#异常的追踪信息
            # return True#把异常吞了
            # 可以在exit方法中添加close()方法
    
    with Foo('a.txt') as f:#这句会触发enter方法,enter方法的return值赋值给f
        # 等同于f=obj.enter()
        print(f)
        #print(sdafasdfh)#一遇到错误就执行exit方法
        print(f.name)
    print('000000')
  • 描述符应用

  • 描述符本身是新式类,被代理的类也应该是新式类
  • 必须把描述符定义成类的类属性,不能定义到构造函数init中
  • Python是弱类型语言,即不用定义变量的类型即可使用,没有类型检测的功能
  • # 实现针对传入值的类型检查
    class Typed:
        def __init__(self, key, expected_type):
            self.key = key
            self.expected_type = expected_type
    
        def __get__(self, instance, owner):  # instance是实例本身,owner是实例的拥有者,即类名
            print('这是get方法')
            # print('instance参数【%s】' % instance)
            # print('owner参数【%s】' % owner)
            return instance.__dict__[self.key]  # 返回实例字典中的值
    
        def __set__(self, instance, value):  # value是key的属性值
            print('set方法')
            # print('instance参数【%s】' % instance)
            # print('value参数【%s】' % value)
            if not isinstance(value, self.expected_type):  # 如果name不是str类型,则不让设置key
                # print('你传入的类型不是字符串,错误')
                # return
                raise TypeError('%s 传入的类型不是%s' % (self.key, self.expected_type))
            instance.__dict__[self.key] = value
    
        def __delete__(self, instance):
            print('delete方法')
            # print('instance参数【%s】' % instance)
            instance.__dict__.pop(self.key)
    
    
    class People:
        name = Typed('name', str)  # 实例的数据属性是字典形式存储的,是字符串格式,将该name数据属性代理给描述符
        age = Typed('age', int)
    
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    
    # p1 = People('alex', 13, 13.3)
    # print(p1.__dict__)
    # p1 = People(122, 13, 13.3)
    p1 = People('alex', '13', 13.3)
    print(p1.__dict__)
    
    # print(p1.name)
    
    # p1.name
    # p1.name = 'egon'
    # print(p1.__dict__)

     

  • 类的装饰器
  • 装饰器也可以装饰类

  • def deco(obj):
        print('-------', obj)
        obj.x = 1
        obj.y = 2
        obj.z = 3
        return obj
    
    # @deco    #等价于test=deco(test),将test值传给deco
    # def test():
    #     print('test函数运行')
    #
    # test()
    
    @deco  # Foo=deco(Foo),类的装饰器,将类传给装饰器的函数,返回值作为新类。以后再运行Foo,就是被deco处理过的Foo
    class Foo:
        pass
    
    print(Foo.__dict__)
    # 目的:用同一个装饰器,为不同类增加不同的属性
    def Typed(**kwargs):
        def deco(obj):
            # print('-->', kwargs)
            # print('类名-->', obj)
            for key, val in kwargs.items():
                setattr(obj, key, val)
            return obj
    
        # print('==>', kwargs)
        return deco
    
    
    @Typed(x=1, y=2, z=3)  # 步骤一:Typed()本身会执行一遍,返回deco。步骤二,@deco
    class Foo:
        pass
    
    print(Foo.__dict__)
    
    
    @Typed(name='egon')
    class Bar:
        pass
    
    print(Bar.name)

     

  • 装饰器的应用
  • # 用装饰器来实现类属性被描述符代理,以实现针对传入值的类型检查
    class Typed:
        def __init__(self, key, expected_type):
            self.key = key
            self.expected_type = expected_type
    
        def __get__(self, instance, owner):  # instance是实例本身,owner是实例的拥有者,即类名
            print('这是get方法')
            return instance.__dict__[self.key]  # 返回实例字典中的值
    
        def __set__(self, instance, value):
            print('set方法')
            if not isinstance(value, self.expected_type):
                raise TypeError('%s 传入的类型不是%s' % (self.key, self.expected_type))
            instance.__dict__[self.key] = value
    
        def __delete__(self, instance):
            print('delete方法')
            # print('instance参数【%s】' % instance)
            instance.__dict__.pop(self.key)
    
    
    def deco(**kwargs):
        def wrapper(obj):
            for key, val in kwargs.items():
                # print('==>', key, val)
                setattr(obj, key, Typed(key, val))  # Typed(key,val),给类增加一个类属性,该类属性被描述符代理
                                                    # 所以key的value不是简单的value,而不是描述符
            return obj
    
        return wrapper
    
    
    @deco(name=str, age=int, salary=float)
    class People:
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    
    # p1 = People('alex', 13, 13.3)
    p1 = People('122', 13, 13.3)
    print(p1.__dict__) 
  • 自定制property
  • @property,所有的@都是执行,将函数传入property执行,返回值作为新函数
  • #自定制property
    class Lazyproperty: def __init__(self, func): print('====>', func) self.func = func def __get__(self, instance, owner): res = self.func(instance)#此步运行area方法,area方法就是property类实例化的self实例 return res class Room: def __init__(self, name, width, length): self.name = name self.width = width self.length = length @Lazyproperty # 实现了给类增加描述符的功能 def area(self): return self.width * self.length r1 = Room('厕所', 1, 1) print(r1.area) 
  • #同上页的描述符的第二种方式
    class Foo: @property#前提,必须先定义此,方可定义后边 def AAA(self): print('get的时候运行我') @AAA.setter#给AAA赋值时,触发此函数 def AAA(self,val): print('set的时候运行我',val) @AAA.deleter#删除AAA时执行此操作 def AAA(self): print('del的时候运行我') f1=Foo() f1.AAA f1.AAA='aaa'
  • 元类

  • 产生类的类,就是type
  • 元类的实例是类,正如类的实例是对象
  • #另外一种产生实例的方法
    def __init__(self,name,age):#定义类中的init方法 self.name=name self.age=age def test(self): pass FFo=type('FFo',(object,),{'x':1,'__init__':__init__,'test':test})#定义类 print(FFo) print(FFo.__dict__) f1=FFo('alex',18)#实例化 print(f1.name)
  • 自定义元类,控制类的生成过程

  • #自定义元类
    class MyType(type): def __init__(self, a, b, c): print('元类的构造函数执行') print(self)#self是产生的类Foo print(a) print(b) print(c) def __call__(self, *args, **kwargs): print('=====') obj = object.__new__(self) # object.__new__(Foo),即Foo产生一个新对象 self.__init__(obj, *args, **kwargs) # Foo.__init__() #此处的self.__init__方法,就是在执行Foo中的init方法 class Foo(metaclass=MyType): # MyType(self,'Foo',(object),{}),使用元类创建一个类Foo # MyType(4个参数)--》__init__ def __init__(self, name): self.name = name f1 = Foo('name') # 执行Foo方法就是在调用父类的call方法
上一篇:QThread basic


下一篇:使用python对指定手机号获取各网站登录的验证码。