目录python
文件IO操做能够对文件对象使用上下文管理,它主要使用with..as..
语法.数据库
with open('123.txt') as f: print(f)
要想本身写的类实现上下文管理,那么须要用到两个方法__exit__和__enter__.安全
方法 | 意义 |
---|---|
__enter__ | 进入与此对象相关的上下文,若是存放该方法,with语法会把该方法的返回值绑定到as子句中指定的变量上 |
__exit__ | 退出与此对象相关的上下文 |
class Text: def __enter__(self): print('enter------') def __exit__(self, exc_type, exc_val, exc_tb): print('exit-------') with Text() as f : print('进来了') print('出来了') # enter------ # 进来了 # exit------- # 出来了
实例化对象的时候,并不会调用__enter__方法,只有进入with语句体中,才会调用__enter__方法,而后执行语句体,最后离开with语句块的时候,再调用__exit__方法.网络
with能够开启一个上下文运行环境,在执行前作一些准备工做,执行后,作一些收尾工做,它并不会开启一个新的做用域.app
class Text: def __enter__(self): print('enter------') def __exit__(self, exc_type, exc_val, exc_tb): print('exit-------') with Text() as f : raise Exception # Traceback (most recent call last): # enter------ # File "E:/Python - base - code/chapter08面向对象/练习3.py", line 169, in <module> # raise Exception # exit------- # Exception
咱们能够看到,with语句体中当异常抛出时,__exit__已经能够执行完毕,因此上下文管理是安全的.dom
class Text: def __enter__(self): print('enter------') def __exit__(self, exc_type, exc_val, exc_tb): print('exit-------') with Text() as f : print(f) # None
这里之因此是None,是由于,__enter__函数的返回值为None,因此若是哪些类的实例化属性或实例自己要在with语句内部使用,能够在__enter__函数中进行返回.函数
class Text: def __init__(self): self.name = 'daxin' def __enter__(self): print('enter------') return self def __exit__(self, exc_type, exc_val, exc_tb): print('exit-------') with Text() as f: print(f.name) # daxin
方法的参数以下:ui
注意:__exit__函数的返回值很重要,当返回值等效为True,表示压制异常(异常不会上报),等效False时,表示不压制异常(此时异常会上报)spa
class A: def __init__(self): pass def __enter__(self): print('Enter ~~~~~') def __exit__(self, exc_type, exc_val, exc_tb): print('exit ~~~~~') print(exc_type) print(exc_val) print(exc_tb) return True with A(): raise Exception('Test') # Enter ~~~~~ # <class 'Exception'> # Test # <traceback object at 0x000001E4D0A5C808>
由于__exit__方法return 了True,因此异常被压制,因此不会异常崩溃。code
下面来计算一个函数的运行时间,主要有两个办法:
装饰器版本:
import time import random import datetime import functools def timer(fn): @functools.wraps(fn) # 拷贝属性信息 def wrapper(*args, **kwargs): start = datetime.datetime.now() res = fn(*args, **kwargs) stop = (datetime.datetime.now() - start).total_seconds() print(stop) return res return wrapper @timer def add(x, y): time.sleep(random.randrange(1, 5)) return x + y add(4,5)
装饰器版本2:类装饰器
import time import random import datetime import functools class Timer: def __init__(self,fn): self.fn = fn functools.wraps(fn)(self) # 拷贝用户函数属性信息 def __call__(self, *args, **kwargs): start = datetime.datetime.now() res = self.fn(*args,**kwargs) stop = (datetime.datetime.now() - start).total_seconds() print('The fn run time is {}'.format(stop)) return res @Timer def add(x, y): '''from add function''' time.sleep(random.randrange(1, 5)) return x + y print(add(4,5)) print(add.__name__)
上下文管理器方法1:
import time import random import datetime def add(x, y): time.sleep(random.randrange(1, 5)) return x + y class Timer: def __init__(self): self.start = None def __enter__(self): self.start = datetime.datetime.now() def __exit__(self, exc_type, exc_val, exc_tb): self.stop = (datetime.datetime.now() - self.start).total_seconds() print(self.stop) with Timer(): add(1, 3)
上下文管理版本2:将要计算的函数看成参数传入上下问管理器中
import time import random import datetime def add(x, y): time.sleep(random.randrange(1, 5)) return x + y class Timer: def __init__(self, fn): self.fn = fn self.start = None def __enter__(self): self.start = datetime.datetime.now() return self.fn def __exit__(self, exc_type, exc_val, exc_tb): self.stop = (datetime.datetime.now() - self.start).total_seconds() print(self.stop) with Timer(add) as f: f(1, 3)
根据上下文管理的特性,总结出三个经常使用的场景:
加强功能
:在代码执行的先后增长代码,以加强其功能。相似装饰器的功能。资源管理
:打来的资源须要关闭,例如文件对象、网络链接、数据库链接等。权限验证
:在执行代码以前,作权限的验证,在__enter__中处理。它是一个装饰器,用于实现上下文管理,它装饰一个函数,由于函数没有像类那样使用__enter__和__exit__来实现,因此使用contextlib.contextmanger可使一个函数变为上下文管理器,可是对被装饰的函数有一个要求,必须包含yeild关键字,也就是说这个函数必须返回一个生成器,且只有yield一个值。
这个装饰器接受一个生成器做为参数
from contextlib import contextmanager @contextmanager def add(x, y): print('hello') yield x + y print('bye bye') print('start') with add(4, 5) as f: print(f) print('end') # start # hello # 9 # bye bye # end
根据打印结果咱们分析:
当咱们传入参数add(1, [5,]) 时,异常直接是函数异常退出了,并无执行yield后面的相似__enter__方法的语句,怎么办呢?可使用try,finally来捕捉
from contextlib import contextmanager @contextmanager def add(x, y): try: print('hello') yield x + y finally: print('bye bye') print('start') with add(1, [5,]) as f: print(f) print('end')
这样就会打印yield后续语句,虽然会异常退出,但因为错误的参数由用户自主传递,那就让用户本身去解决吧。
业务逻辑简单,可使用函数加contextlib.contextmanager装饰器实现,业务逻辑复杂的话,可使用类加__enter__和__exit__来解决。
一个对象可以在运行时,像照镜子同样,显示出其类型信息,这种方法叫作反射。换句话是反射能够在程序运行的同时获取类型定义的信息,好比经过一个对象,找出它的type、class、attribute或者method等。具备反射能力的函数有:type()、isinstance()、callable()、dir()、getattr()等。
内建函数 | 含义 |
---|---|
getattr(object, name[, default]) | 经过name返回object的属性值。当属性不存在,将使用default返回。 若是没有设置default,则抛出AttributeError异常,name必须为字符串。 |
setattr(obj, name, value) | obj的属性存在,则覆盖,不存在,则新增。 |
hasattr(obj, name) | 判断obj是否存在属性,name必须为字符串,返回值为bool类型 |
class Person: def __init__(self,name,age): self.name = name self.age = age def talk(self): print("{} is talking".format(self.name)) daxin = Person('daxin',20) if hasattr(daxin,'name'): # 判断daxin是否含有name属性 print(getattr(daxin,'name')) # 若是有,经过getattr获取name属性 if not hasattr(daxin,'sing'): # 判断daxin没有sing方法 setattr(daxin,'sing',lambda self:print("{} is singing".format(self.name))) # 为实例绑定一个sing方法 daxin.sing() # 实例调用
直接调用时没法执行,提示缺乏self参数,想一下,咱们定义的方法一般是在类中定义的,在类中咱们指定的self参数,在实例调用时会进行传递(由于是实例是绑定在方法上的),而咱们绑定的sing方法是绑定在实例自己上的,因此这种状况下,是没法帮咱们传递self参数的,因此咱们在函数内部也没法调用实例的参数。
这种动态增长属性的方式是运行时改变类或者实例的方式,而装饰器或者Mixin是在定义时就决定了的,所以反射具备更大的灵活性。
命令分发器实例:
class Dispather: def __init__(self): pass def register(self, name, func): setattr(self, name, func) def run(self): while True: cmd = input('>>>: ').strip() if cmd.lower() == 'quit': break else: getattr(self, cmd.lower())() d = Dispather() d.register('ls',lambda :print('hello world')) d.run()
魔术方法 | 含义 |
---|---|
__getattr__(self, name) | 定义当用户试图获取一个不存在的属性时的行为 |
__setattr__(self, name, value) | 定义当一个属性被设置时的行为 |
__delattr__(self, name) | 定义当一个属性被删除时的行为 |
class A: def __init__(self): pass def __getattr__(self, item): print('__getattr__') return 'daxin' daxin = A() print(daxin.name) # __getattr__ # daxin
访问daxin的一个属性name,若是不存在,最后会调用__getattr方法,它的返回值就是结果。若是没有这个方法,就会抛出AttributeError异常,表示找不到属性。
查找属性的顺序为:instance.__dict --> instance.__class.__dict --> ... --> object的dict,找不到,调用实例的__getattr__
class A: def __init__(self): pass def __getattr__(self, item): print('__getattr__') return 'daxin' def __setattr__(self, key, value): self.key = value # self.key依旧调用self.__setattr__方法 # self.__dict__[key] = value daxin = A() daxin.name = 'daxin' # 调用__setattr__方法 print(daxin.name)
上面的代码没法执行,会产生递归是为何呢?
__setattr__()方法,能够拦截对实例属性的增长、修改操做,若是要设置生效,须要本身修改操做实例的__dict__属性。
class Person: def __init__(self,name): self.name = name self.__dict__['a'] = 5 def __getattr__(self, item): print('getattr~~~~~') return getattr(self,item) def __setattr__(self, key, value): print('setattr~~~~~~') self.__dict__[key] = value # setattr(self,key,value) # 不能这样写,这样写等同于调用对象的__setattr__方法,会产生递归 daxin = Person('daxin') print(daxin.name) print(daxin.a)
结果只会输出1次getattr,由于初始化时,已经为字典建立了一个key,a,因此当访问实例属性a时,因为__dict__中存在,因此不会被__getattr__捕获。
setattr本质上也是经过 instance.attribute = value 的方式赋值的。
删除一个属性时,触发__delattr__方法的执行。能够阻止经过实例来删除属性的操做。
class Person: def __init__(self,name): self.name = name self.__dict__['a'] = 5 def __getattr__(self, item): print('getattr~~~~~') return getattr(self,item) def __setattr__(self, key, value): print('setattr~~~~~~') self.__dict__[key] = value def __delattr__(self, item): print('delattr~~~~~~') del self.__dict__[item] # 删除实例的属性, 也能够在这里啥也不作,提示不能删除,便可阻止实例的属性被删除。 daxin = Person('daxin') print(daxin.name) del daxin.a # 触发实例的__delattr__方法的执行。
魔术方法 | 含义 |
---|---|
__getattribute__(self, name) | 定义当该类的属性被访问时的行为 |
实例全部的属性访问,第一个都会调用__getattribute__方法。
class Person: def __init__(self,name,age): self.name = name self.age = age def __getattr__(self, item): return 'getattr' def __getattribute__(self, item): pass # return 'ABC' # raise AttributeError('ABC') daxin = Person('daxin',20) print(daxin.name)
上面的例子获得如下结论:
__getattribute__方法中为了不在该方法中无限递归,它的实现应该永远调用基类的同名方法以访问须要的任何属性(object.__getattribute__(self, name))。
实例属性查找顺序:
instance.__getattribute__() --> instance.__dict__ --> instance.__class__.__dict__ --> object.__dict__ --> instance.__getattr__