python基础14-__enter__/__exit__/描述符管理实例的实参类型控制/类的装饰器/自定制property/自定义元类
-
上下文管理协议
- 操作文件有三步骤
- 打开文件,赋值给一个文件句柄
- 文件操作
- 关闭文件
- 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方法