with 上下文协议:
主要调用类的__enter__ , __exit__ 函数。当调用open类时,触发__enter__函数,并将返回值赋值给f(句柄),然后执行with下面的代码块,如果代码块没有错误,则在执行完代码块后自动触发__exit__函数。如果有错误,并不会直接报错,而是先进入__exit__函数退出文件操作后,再报错。如果在__exit__函数中设置返回值为真则会将异常吞下,否则会报错。
class Open:
def __init__(self,name):
self.name = name
# 进入函数,当上下文协议开始时调用,返回值给f(句柄)
def __enter__(self):
print('entering')
return self
# 退出函数,在退出的时候调用
def __exit__(self, exc_type, exc_val, exc_tb):
print('exited')
print(exc_type,exc_val,exc_tb) # exc_type 报错异常的类,exc_val 异常的信息,exc_tb 追踪信息
return True # 不会报错,吞下了错误
with Open('a.txt') as f: # 触发__enter__
print(afsdfdsdv)
# print(fehgadfgv) 会直接退出先不报错,触发__exit__函数
>>> entering
>>> exited
>>> <class 'NameError'> name 'afsdfdsdv' is not defined <traceback object at 0x000001930DC42288>
描述符的小应用:
Python是一个弱类型的编程语言,函数之间传递参数时,并不用明确指出参数类型。下面对函数的参数类型做判断
class Type:
def __init__(self, key):
# 方便在底层字典添加参数
self.key = key
# 定义一个类型字典,用于判断
self.dict = {'name':str, 'age':int}
# instance 是关联类
def __set__(self, instance, value):
print('set method')
# 判断传入的类型是否是对应的类型
if type(value) is not self.dict[self.key]:
print(self.key + 'error')
instance.__dict__[self.key] = value
def __get__(self, instance, owner):
print('get method')
return instance.__dict__[self.key]
def __delete__(self, instance):
print('delete method')
return instance.__dict__.pop(self.key)
class Test:
# 实例数据描述符
name = Type('name')
age = Type('age')
def __init__(self,name,age):
self.name = name
self.age = age
t = Test('Jax', '15')
装饰器:
※本质就是一个函数,为其他函数添加附加功能。不仅能将函数当参数传递,类也可以当成参数进行传递。
原则:1、不修改被修饰函数的代码 2、不修改被修饰函数调用方式
装饰器也可以是类:@符号在做 area=prop(area),所以既可以是函数也可以是类
简单的小例子:闭包
import time
def wrapper(func):
def deco():
t1 = time.time()
func()
t2 = time.time()
print(t2 - t1)
return deco
@wrapper # 先传递函数名
def test():
for i in range(10):
print(i)
test()
>>> 0
1
2
3
4
5
6
7
8
9
0.0009980201721191406
python里一切皆对象,函数也有类的双下划线方法,因为他们都是对象
def wrapper(func):
pass
return func
@wrapper # 不会关下面的东西,都会当成类来处理 test = wrapper(test)
def test():
pass
test.x = 1
print(test.__dict__)
>>> {'x': 1}
更高级的玩法:为类添加功能的装饰器。
def deco(**kwargs):
def wrapper(func):
for i,j in kwargs.items():
setattr(func,i,j)
return func
return wrapper
@deco(x=1,y=2) # 1.先deco(x=1,y=2)返回---->wrapper 2.相当于执行@wrapper再将类传入wrapper中
class Foo:
pass
print(Foo.__dict__)
@deco(name = 'Jax')
class Pig:
pass
print(Pig.__dict__)
>>> {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2}
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Pig' objects>, '__weakref__': <attribute '__weakref__' of 'Pig' objects>, '__doc__': None, 'name': 'Jax'}
装饰器+描述符:
对上面限制的类型限制的更简洁的写法:
通过装饰器,减少代码的冗余
def deco(**kwargs):
def wrapper(obj):
for key, val in kwargs.items():
# 将属性字典的值对应描述符,覆盖原属性值
setattr(obj, key, Typed(key, val))
return obj # 闭包
return wrapper
class Typed:
def __init__(self,key,val):
self.key = key
self.val = val
def __set__(self, instance, value):
if type(value) is not self.val:
print('error')
@deco(name=str, age=int)
class People:
def __init__(self, name, age):
self.name = name
self.age = age
People('Jax',19.9)
还可以实现静态属性的方法:
class Lazy:
def __init__(self,area):
self.area = area
def __get__(self, instance, owner):
# 当类去调用时触发,返回自身
if instance is None:
return self
return self.area(instance)
class Room:
def __init__(self):
pass
# @lazy(x=1) 1.运行lazy(x=1) 拿到返回的结果在 2.@返回值 ----> area = Lazy(area)
@Lazy # 1.area=Lazy(area), 实例化了一个对象,@就是一个语法糖.类自己多了一个属性
def area(self):
print('area method)
r = Room()
r.area # 触发__get__方法
print(Room.area) # 不再是执行area函数而是在执行area属性,已经被代理了
※ 从上到下遇见装饰器时,先运行装饰器。因为这是一个实例化的过程,静态属性主要给实例去用。
实现类方法:
class Statics:
def __init__(self,parms): # 装饰器触发此函数
self.parms = parms
def __get__(self, instance, owner):
return self.parms(instance)
class People:
name = 'Jax'
@Statics
def re(self):
return '66666'
print(People.re)
p = People()
print(p.re)
print(People.__dict__)
>>> 66666
66666
{'__module__': '__main__', 'name': 'Jax', 're': <__main__.Statics object at 0x000001D64C8E30F0>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
静态属性也可以实现类型检测:
静态属性被定义好以后无法被修改,如果再定义一个同名的方法,前面加上 方法名.setter 装饰器 就会在修改属性时触发该方法。
如果再定义一个同名的方法,前面加上 方法名.deleter 装饰器 就会在删除属性时触发该方法。
class Test:
def __init__(self,count, one):
self.count = count
self.one = one
@property # 当有取值操作时触发
def price(self):
price = self.count * self.one
return price
@price.setter # 当有赋值操作时触发
def price(self,val):
self.count = val
@price.deleter # 当有删除操作时触发
def price(self):
del self.count
t = Test(15,20)
print(t.price)
t.count = 30 # 触发setter函数
print(t.price)
另外一种定义静态属性的方法:
class Test:
def get_oppo(self):
print('oppo')
def set_oppo(self,value):
print('nova',value)
def del_oppo(self):
print('deletere')
# 上面函数定义的顺序可以变
aaa = property(get_oppo,set_oppo,del_oppo) # 顺序不能变 函数定义顺序不能变
t = Test()
实现类型检测:
class Test:
def __init__(self,name,age):
self.name = name
self.age = age
@property
def name(self):
print('get')
@name.setter
def name(self,val):
if type(val) is not str:
print('name error')
@property
def age(self):
print('get')
@age.setter
def age(self, val):
if type(val) is not int:
print('age error')
t = Test(19,19.9)
>>> name error age error