在现代软件开发中,性能和资源管理是至关重要的。尤其是在处理大量数据或资源时,如何高效地加载和使用资源,直接影响到程序的性能和用户体验。今天,我们就来深入探讨一种非常实用的技术——Lazy Loading(懒加载)。
Lazy Loading,即懒加载,是一种延迟加载资源的策略。它的核心思想是:“只有在真正需要使用某个资源时,才去加载它”。与传统的“即时加载”(Eager Loading)不同,懒加载不会在程序启动时一次性加载所有资源,而是按需加载,从而节省内存和初始化时间。
举个简单的例子:假设你有一个包含数千张图片的图库,如果在程序启动时就加载所有图片,可能会导致内存溢出或启动时间过长。而使用懒加载,你可以只在用户需要查看某张图片时才加载它,从而大大减少内存占用和提升性能。
我们长话短说,假如我们在某个文件夹有一批图片需要进行处理,那么我们一般有三种处理的方式:
import os
import time
from PIL import Image
def method_1():
# 使用列表推导式直接读取所有图片并保存到列表中
t_begin = time.perf_counter()
images = [Image.open(os.path.join(folder_path, file)) for file in os.listdir(folder_path) if file.endswith(('.png', '.jpg', '.jpeg'))]
t_loading = time.perf_counter()
print(f"[方法1] [加载耗时] {t_loading - t_begin:.6f}s")
print(f"[方法1] [图片数量] {len(images)}")
print(f"[方法1] [第一张图片] {images[0]}")
# 遍历图片列表并逐一处理
for img in images:
# 将图片转换为灰度图
img_gray = img.convert("L")
t_end= time.perf_counter()
print(f"[方法1] [程序总耗时] {t_end - t_begin:.6f}s")
def method_2():
# 使用列表推导式直接读取所有图片的路径并保存到列表中
t_begin = time.perf_counter()
images_paths = [os.path.join(folder_path, file) for file in os.listdir(folder_path) if file.endswith(('.png', '.jpg', '.jpeg'))]
t_loading = time.perf_counter()
print(f"[方法2] [加载耗时] {t_loading - t_begin:.6f}s")
print(f"[方法2] [图片数量] {len(images_paths)}")
print(f"[方法2] [第一张图片] {images_paths[0]}")
# 遍历图片列表并逐一处理
for path in images_paths:
with Image.open(path) as img: # 使用 with 确保文件正确关闭
# 将图片转换为灰度图
img_gray = img.convert("L")
t_end= time.perf_counter()
print(f"[方法2] [程序总耗时] {t_end - t_begin:.6f}s")
def method_3_lazy_load():
# 定义如何加载图片的lazy Loading函数
def lazy_load_images(folder_path):
for file in os.listdir(folder_path):
if file.endswith(('.png', '.jpg', '.jpeg')):
yield os.path.join(folder_path, file)
# 遍历图片列表并逐一处理
t_begin = time.perf_counter()
for path in lazy_load_images(folder_path):
t_loading = time.perf_counter()
with Image.open(path) as img:
img_gray = img.convert("L")
t_end= time.perf_counter()
print(f"[方法3-Lazy Loading] [程序总耗时] {t_end - t_begin:.6f}s")
if __name__ == '__main__':
# 定义图片文件夹路径
folder_path = "test-Le0v1n/images_10k"
method_1()
print()
method_2()
print()
method_3_lazy_load()
[方法1] [加载耗时] 1.103163s
[方法1] [图片数量] 1000
[方法1] [第一张图片]
[方法1] [程序总耗时] 4.485567s
[方法2] [加载耗时] 0.024818s
[方法2] [图片数量] 1000
[方法2] [第一张图片] test-Le0v1n/images_10k/image_1714117763041427800_722-4x4-5_0.jpg
[方法2] [程序总耗时] 6.909667s
[方法3-Lazy Loading] [程序总耗时] 4.680766s
我们可以看到,在加载1000张图片并处理时第一种方式是最快的,但问题就是对内存的占用太高了,如果电脑内存不够那么可能会无法执行。第二种方式是最慢的。第三种方式也就是我们即将介绍的Lazy Loading是一个tradeoff,平衡了速度和内存占用。
懒加载的实现依赖于“按需加载”的机制。它通常通过以下方式实现:
生成器是 Python 中实现懒加载的常用方式。以下是一个简单的例子:
import os
from PIL import Image
def lazy_load_images(folder_path):
for file in os.listdir(folder_path):
if file.endswith(('.png', '.jpg')):
yield Image.open(os.path.join(folder_path, file))
# 使用生成器逐一处理图片
for img in lazy_load_images(folder_path):
# 处理图片
pass
在这个例子中,lazy_load_images
是一个生成器函数。它定义了如何加载图片,但只有在迭代时才会真正加载图片。这种方式完全符合懒加载的定义。
如果我们需要更复杂的懒加载逻辑,可以使用类来实现。以下是一个示例:
class LazyImageLoader:
def __init__(self, folder_path):
self.folder_path = folder_path
self.image_paths = [os.path.join(folder_path, file) for file in os.listdir(folder_path) if file.endswith(('.png', '.jpg'))]
def __iter__(self):
for path in self.image_paths:
yield Image.open(path)
# 使用类实现懒加载
loader = LazyImageLoader(folder_path)
for img in loader:
# 处理图片
pass
在这个例子中,LazyImageLoader
类封装了图片路径的加载逻辑,并通过生成器逐个加载图片。
Lazy Loading 是一种非常实用的技术,它通过“按需加载”的方式,有效节省了内存和初始化时间。虽然它可能会增加磁盘 I/O 开销,但在处理大规模数据或资源受限的场景中,懒加载的优势非常明显。