contextlib.contextmanager:这个函数可以用来定义上下文管理器,它可以把一个普通的函数变成一个上下文管理器。
contextlib.closing:这个函数可以用来包装一个对象,使其可以使用with语句。
contextlib.suppress:这个函数可以用来抑制指定的异常,使其不会被抛出。
contextlib.redirect_stdout:这个函数可以用来重定向标准输出流。
contextlib.ExitStack:这个类可以用来管理多个上下文管理器,它可以让我们在一个with语句中使用多个上下文管理器。
上下文管理器是Python中的一种特殊对象,它可以用来管理上下文,例如打开和关闭文件、访问数据库等。它们通常使用with语句来实现,它可以确保在with语句块执行完毕后,上下文管理器会被正确的关闭。
with open('test.txt', 'w') as f: # 执行完后,文件会关闭close()。
f.write('Hello world')
with open('test.txt','r') as f: # 执行完后,文件会关闭close()。
print(f.readlines())
所谓魔法函数(Magic Methods),是Python的一种高级语法,允许你在类中自定义函数(函数名格式一般为__xx__),并绑定到类的特殊方法中
比如在类People中自定义__str__函数,则在调用People()时,会自动调用__str__函数,并返回相应的结果。在定义类时,经常使用__init__函数(构造函数)和__del__函数(析构函数),其实这也是魔法函数的一种。
Python中以双下划线(__)开始和结束的函数为魔法函数。
调用类实例化的对象的方法时自动调用魔法函数。
在自己定义的类中,可以实现之前的内置函数。(复写的概念)
执行流进入 with 中的代码块时会执行__enter__方法,它会返回在这个上下文中使用的一个对象。执行流离开 with 块时,则调用这个上下文管理器的__exit__方法来清理所使用的资源
class People(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return self.name + ":" + str(self.age)
if __name__=="__main__":
print(People('FrienshipT',18))
执行结果
FrienshipT:18
import contextlib
class Context(contextlib.ContextDecorator):
def __init__(self, mode):
self.mode = mode
print('__init__({})'.format(self.mode))
def __enter__(self):
print('__enter__({})'.format(self.mode))
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('__exit__({})'.format(self.mode))
@Context('装饰器')
def print_func(info):
print(info)
print_func('装饰器运行')
print("\n--------------\n")
with Context('上下文管理器'):
print('上下文管理器方式运行')
运行结果
__init__(装饰器)
__enter__(装饰器)
装饰器运行
__exit__(装饰器)
--------------
__init__(上下文管理器)
__enter__(上下文管理器)
上下文管理器方式运行
__exit__(上下文管理器)
YOLOv5 ./utils/general.py中的上下文管理器
class Profile(contextlib.ContextDecorator):
# YOLOv5 Profile class. Usage: @Profile() decorator or 'with Profile():' context manager
def __init__(self, t=0.0):
self.t = t
self.cuda = torch.cuda.is_available()
def __enter__(self):
self.start = self.time()
return self
def __exit__(self, type, value, traceback):
self.dt = self.time() - self.start # delta-time
self.t += self.dt # accumulate dt
def time(self):
if self.cuda:
torch.cuda.synchronize()
return time.time()
if __name__ == "__main__":
context = Profile()
with context:
print('Profile()上下文管理器')
运行结果
__init__ self.t: 0.0
__enter__ self.start: 1677419715.9893756
Profile()上下文管理器
__exit__ self.time(): 1677419715.9894035
__exit__ self.dt(delta-time): 2.5272369384765625e-05
__exit__ self.t(accumulate-dt): 2.5272369384765625e-05
Profile主要是计算时间的
torch.cuda.synchronize()
等待当前设备上所有流中的所有核心完成。
测试时间的代码
代码1
start = time.time()
result = model(input)
end = time.time()
代码2
torch.cuda.synchronize()
start = time.time()
result = model(input)
torch.cuda.synchronize()
end = time.time()
代码2是正确的。因为在pytorch里面,程序的执行都是异步的。
如果采用代码1,测试的时间会很短,因为执行完end=time.time()程序就退出了,后台的cu也因为python的退出退出了。
如果采用代码2,代码会同步cuda的操作,等待gpu上的操作都完成了再继续成形end = time.time()
代码3
start = time.time()
result = model(input)
print(result)
end = time.time()
如果将代码1改为代码3 ,代码3和代码2的时间是类似的。
因为代码3会等待gpu上的结果执行完传给print函数,所以时间就和代码2同步的操作的时间基本上是一致的了。
将print(result)换成result.cpu()结果是一致的。