python中@lazy_property

本文介绍Python中如何使用懒加载技术来实现属性的延迟初始化,通过两种方式实现:使用描述符和@property修饰符。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文:https://segmentfault.com/a/1190000005818249

Python 对象的延迟初始化是指,当它第一次被创建时才进行初始化,或者保存第一次创建的结果,然后每次调用的时候直接返回该结果。

延迟初始化主要用于提高性能,避免浪费计算,并减少程序的内存需求。

property

在切入正题之前,我们了解下property的用法,property可以将属性的访问转变成方法的调用。

class Circle(object): 
  def __init__(self, radius): 
    self.radius = radius 
  
  @property
  def area(self): 
    return 3.14 * self.radius ** 2
  
c = Circle(4) 
print c.radius 
print c.area 

可以看到,area虽然是定义成一个方法的形式,但是加上@property后,可以直接执行c.area,当成属性访问。

现在问题来了,每次调用c.area,都会计算一次,太浪费cpu了,怎样才能只计算一次呢?这就是lazy property

lazy property

实现延迟初始化有两种方式,一种是使用python描述符,另一种是使用@property修饰符。

方式1:

class lazy(object): 
  def __init__(self, func): 
    self.func = func 
  
  def __get__(self, instance, cls): 
    val = self.func(instance) 
    setattr(instance, self.func.__name__, val) 
    return val 
  
class Circle(object): 
  def __init__(self, radius): 
    self.radius = radius 
  
  @lazy
  def area(self): 
    print 'evalute'
    return 3.14 * self.radius ** 2
  
c = Circle(4) 
print c.radius 
print c.area 
print c.area 
print c.area 

结果'evalute'只输出了一次。在lazy类中,我们定义了__get__()方法,所以它是一个描述符。当我们第一次执行c.area时,python解释器会先从c.__dict__中进行查找,没有找到,就从Circle.__dict__中进行查找,这时因为area被定义为描述符,所以调用__get__方法。

__get__()方法中,调用实例的area()方法计算出结果,并动态给实例添加一个同名属性area,然后将计算出的值赋予给它,相当于设置c.__dict__['area']=val

当我们再次调用c.area时,直接从c.__dict__中进行查找,这时就会直接返回之前计算好的值了。

不太懂python描述符的话,可以参考Descriptor HowTo Guide

方式2

def lazy_property(func):
    attr_name = "_lazy_" + func.__name__

    @property
    def _lazy_property(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, func(self))
        return getattr(self, attr_name)

    return _lazy_property

class Circle(object): 
  def __init__(self, radius): 
    self.radius = radius 
  
  @lazy_property
  def area(self): 
    print 'evalute'
    return 3.14 * self.radius ** 2

这里与方法1异曲同工,在area()前添加@lazy_property相当于运行以下代码:

lazy_property(area)

lazy_property()方法返回_lazy_property_lazy_property又会调用_lazy_property()方法,剩下的操作与方法1类似。

我们可以检查下是否真的延迟初始化了:

c = Circle(4) 
print "before first visit"
print c.__dict__  
c.area
print "after first visit"
print c.__dict__

输出结果为:

before first visit
{'radius': 4}
evalute
after first visit
{'_lazy_area': 50.24, 'radius': 4}

从中可以看出,只有当我们第一次访问c.area时,才调用area方法,说明确实延迟初始化了。

### Python Lazy Evaluation 和 Lazy Loading 的概念与用法 #### 1. **Lazy Evaluation** 懒惰求值(Lazy Evaluation)是指程序不会立即执行某项操作,而是等到该结果真正被需要时才进行计算。这种方式可以节省资源并提高性能,尤其是在处理大量数据或无限序列时非常有用。 在 Python 中,可以通过生成器(Generators)实现懒惰求值。例如,在引用[^1]中展示了一种惰性函数的写法: ```python def lazy_loading(items): for i in items: # 可以在此处加入复杂的逻辑 yield i ** 2 ``` 这段代码定义了一个名为 `lazy_loading` 的生成器函数,它会对输入列表中的每一项平方后再逐一返回。只有当调用方请求下一个值时,才会触发下一次计算过程[^1]。 #### 2. **Lazy Loading** 延迟加载(Lazy Loading)则是指直到确实需要用到某个对象的时候再对其进行初始化或者读取。这种技术常用于减少内存占用和加快启动速度等方面。 一个典型的例子就是数据库连接池管理——仅当第一次查询发生时建立实际物理链接而不是一开始就全部打开所有的可能使用的连接数。另外像图片预览组件也经常采用类似的策略只渲染当前屏幕可见区域内的图形元素从而提升用户体验流畅度。 下面给出一段基于类属性实现简单版本延迟加载机制的小示范: ```python class Resource: def __init__(self, name): self.name = name print('Resource {} initialized'.format(self.name)) class LazyProperty: def __init__(self, method): self.method = method self.value = None def __get__(self, instance, owner): if self.value is None: self.value = self.method(instance) return self.value class MyClass: @LazyProperty def resource(self): return Resource('my_resource') obj = MyClass() # 资源尚未创建 print(obj.resource) # 打印: Resource my_resource initialized # <__main__.Resource object at ...> ``` 在这个例子中,当我们首次访问 `MyClass` 实例上的 `resource` 属性时,真正的 `Resource` 对象才会被实例化出来[^2]。 #### 3. **结合 itertools 使用** 除了基本的生成器之外,Python 标准库中的 `itertools` 模块提供了多种工具支持更高级别的懒惰运算场景。比如前面提到过的 `islice()` 方法允许我们在不完全遍历整个迭代器的前提下获取其中一部分连续成员[^3]: ```python import itertools def count(n): while True: yield n n += 1 c = count(0) for x in itertools.islice(c, 10, 20): print(x) ``` 以上脚本展示了如何利用 `itertools.islice` 来截取出计数器生成器的部分输出区间而不必担心一次性把所有数值都载入内存之中造成浪费现象的发生。 --- ### 总结 无论是通过普通的生成器还是借助专门设计好的辅助函数/装饰器等形式达成目的,"懒"的思想始终贯穿于现代高效编程实践中发挥着重要作用.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值