Python中lazy property的两种方法小结

 更新时间:2025年01月17日 10:15:45   作者:Python热爱者  
本文介绍了Python中实现延迟属性lazyproperty的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

我们都知道,在Python的类中,__dict__保存了一个对象所有的属性,如下面的例子,我们建立了一个Circle的对象,对象的字典中保存着半径radius 这个k-v值:

class Circle(object):
    def __init__(self,radius):
        self.radius = radius
    
c = Circle(2.5)
print(c.__dict__)
 
#{'radius': 2.5}

对于类中的方法,我们有时候希望它可以像属性一样被调用,这时候我们通常给类的方法添加@property修饰符:

class Circle(object):
    def __init__(self,radius):
        self.radius = radius
    @property
    def area(self):
        print("calculate area")
        return 3.14 * self.radius * 2
circle = Circle(4)
print(circle.area)
 
#calculate area
#25.12

但是这么做,虽然area可以当作一个属性访问,但是并不是真正的变成了一个属性,同时,我们每次调用circle.area,都会从头到尾执行一遍函数,我们来看下面的例子:

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

输出为:

{'radius': 4}
calculate area
25.12
calculate area
25.12
{'radius': 4}

可以发现:

  • 每次执行circle.area,整个函数流程都会被执行一次
  • 同时area也没有真正变成对象的属性,dict中并没有area

深入

那么我们有没有办法把一个类中的函数真正变成对象的属性,同时只有在第一次调用时进行一次计算,而之后每次调用不会重复计算呢?这就是Python中的lazy property。

本文介绍两种方法。一种是使用python描述符,另一种是使用python修饰符。

1、使用python描述符

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("calculate area")
    return 3.14 * self.radius ** 2
 
c = Circle(4) 
print('before calculate area')
print(c.__dict__)
print(c.area)
print(c.area)
print('after calculate area')
print(c.__dict__)
c.radius = 5
print(c.__dict__)
print(c.area)

输出为:

before calculate area
{'radius': 4}
calculate area
50.24
50.24
after calculate area
{'radius': 4, 'area': 50.24}
{'radius': 5, 'area': 50.24}
50.24
{'radius': 5, 'area': 50.24}

可以看到,area只在第一次调用时计算了一次,同时在调用以后area变成了对象的一个属性,同时值并不随半径的变化而变化。

在这里,lazy类有__get__方法,说明是个描述器,第一次执行c.area的时候,因为顺序问题,先去c.__dict__中找,没找到,就去类空间找,在类Circle中,有area()方法,于是就被__get__拦截。

__get__中,调用实例的area()方法算出结果,并动态给实例添加个同名属性把结果赋给它,即加到c.__dict__中去。

再次执行c.area的时候,先去c.__dict__找,因为此时已经有了,就不会经过area()方法和__get__了。

2、使用python修饰符

def lazy_property(func):
    attr_name = "_lazy_" + func.__name__
 
    @property
    def _lazy_property(instance):
        if not hasattr(instance, attr_name):
            setattr(instance, attr_name, func(instance))
        return getattr(instance, attr_name)
 
    return _lazy_property
 
class Circle(object): 
  def __init__(self, radius): 
    self.radius = radius 
  
  @lazy_property
  def area(self): 
    return 3.14 * self.radius ** 2
 
c = Circle(4) 
print('before calculate area')
print(c.__dict__)
print(c.area)
print(c.area)
print('after calculate area')
print(c.__dict__)
c.radius = 5
print(c.__dict__)
print(c.area)

输出为:

before calculate area
{'radius': 4}
50.24
50.24
after calculate area
{'radius': 4, '_lazy_area': 50.24}
{'radius': 5, '_lazy_area': 50.24}
50.24

可以看到,area只在第一次调用时计算了一次,同时在调用以后area变成了对象的一个属性,同时值并不随半径的变化而变化。

到此这篇关于Python中lazy property的两种方法小结的文章就介绍到这了,更多相关Python lazy property内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python 中创建 PostgreSQL 数据库连接池

    Python 中创建 PostgreSQL 数据库连接池

    这篇文章主要介绍了Python 中创建 PostgreSQL 数据库连接池,Python 连接 PostgreSQL 是主要有两个包, py-postgresql 和 psycopg2 , 而本文的实例将使用后者,感兴趣的小伙伴可以参考一下
    2021-10-10
  • python subprocess pipe 实时输出日志的操作

    python subprocess pipe 实时输出日志的操作

    这篇文章主要介绍了python subprocess pipe 实时输出日志的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • pycharm激活方法到2099年(激活流程)

    pycharm激活方法到2099年(激活流程)

    这篇文章主要介绍了pycharm激活方法到2099年,文末给大家提到了idea和pycharm最新版激活方法,非常不错对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • Python脚本实现音频和视频格式转换

    Python脚本实现音频和视频格式转换

    这篇文章主要为大家详细介绍了Python如何通过脚本实现音频和视频格式转换,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-03-03
  • python发布模块的步骤分享

    python发布模块的步骤分享

    这篇文章主要介绍了python发布模块的步骤,需要的朋友可以参考下
    2014-02-02
  • 使用Cython中prange函数实现for循环的并行

    使用Cython中prange函数实现for循环的并行

    Cython中提供了一个prange函数,专门用于循环的并行执行。这个 prange的特殊功能是Cython独一无二的,并且prange只能与for循环搭配使用,不能独立存在。本文就将使用 prange 实现 for 循环的并行,感兴趣的可以了解一下
    2022-08-08
  • python对ip地址进行排序、分类的方法详解

    python对ip地址进行排序、分类的方法详解

    这篇文章主要介绍了python对ip地址进行排序、分类的方法详解,IP协议全称为“网际互连协议Internet Protocol”,IP协议是TCP/IP体系中的网络层协议,需要的朋友可以参考下
    2023-07-07
  • python对二维数组赋值问题

    python对二维数组赋值问题

    这篇文章主要介绍了python对二维数组赋值问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • Python实现将HTML转为PDF/图片/XML/XPS格式

    Python实现将HTML转为PDF/图片/XML/XPS格式

    网页内容是信息传播的主要形式之一,这篇文章主要和大家介绍了如何使用Python实现将HTML分别转为PDF/图片/XML/XPS格式等,需要的可以参考下
    2024-03-03
  • 解决pytorch读取自制数据集出现过的问题

    解决pytorch读取自制数据集出现过的问题

    这篇文章主要介绍了解决pytorch读取自制数据集出现过的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-05-05

最新评论