python学习之延迟初始化

本文介绍了Python中的延迟初始化,旨在提高性能和减少内存消耗。通过使用property和lazy装饰器,详细阐述了如何实现类属性的延迟计算,确保在首次访问时计算并缓存结果,以降低CPU资源的使用。

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

概述

Python的延迟初始化主要是指对象第一次被创建时才进行初始化,其主要是为了提高计算性能,避免计算浪费并减少,减少程序的内存需求, 总结起来如下:

类属性的延迟计算就是将类的属性定义成一个property,只在访问的时候才会计算,而且一旦被访问后,结果将会被缓存起来,不用每次都计算

下面一步步了解一下python的这一功能

原始程序
class my_class(object):
    def __init__(self,name):
        self.name = name

    def get_name(self):
        print '获取姓名'
        return self.name

# 原始
obj = my_class('xiaoming')
print obj.name
print obj.get_name()
print obj.get_name()


# 结果
xiaoming
获取姓名
xiaoming
获取姓名
xiaoming

上面程序中,my_class的属性有name,而get_name主要是类获取属性值的方法,具体方法如上述代码所示

注意到,获取类的属性值,必须先实例化一个对象,再通过对象调用类内的方法,这样不免不好,若是将方法转为属性,直接访问不是更好,因此就有了property

property

property主要将方法的访问转变成属性的方法

class my_class(object):
    def __init__(self,name):
        self.name = name

    @property
    def get_name(self):
        print '获取姓名'
        return self.name

obj = my_class('xiaoming')
print obj.name
print obj.get_name
print obj.get_name
print obj.get_name


# 结果
xiaoming
获取姓名
xiaoming
获取姓名
xiaoming
获取姓名
xiaoming

注意到,get_name虽然定义成一个方法的形式,但是加上@property后,便可直接执行obj.get_name当成属性访问。

但注意到,每次调用obj.get_name时,都会计算一次,浪费CPU,但是怎样只计算一次,已减少CPU资源呢,这就用到了lazy延迟初始化

lazy
from lazy import lazy

class my_class(object):
    def __init__(self, name):
        self.name = name

    @lazy
    def get_name(self):
        print '获取姓名'
        return self.name

obj = my_class('xiaoming')
print obj.name
print obj.get_name
print obj.get_name
print obj.get_name

# 结果
xiaoming
获取姓名
xiaoming
xiaoming
xiaoming

注意到,输出结果时,只有第一次进行计算,而并不是每次都进行计算,减少了CPU资源, 那么是如何做到的,我们细细来看

lazy源码

class lazy(object):
    """lazy descriptor

    Used as a decorator to create lazy attributes. Lazy attributes
    are evaluated on first use.
    """

    def __init__(self, func):
        self.__func = func
        functools.wraps(self.__func)(self)

    def __get__(self, inst, inst_cls):
        if inst is None:
            return self

        if not hasattr(inst, '__dict__'):
            raise AttributeError("'%s' object has no attribute '__dict__'" % (inst_cls.__name__,))

        name = self.__name__
        if name.startswith('__') and not name.endswith('__'):
            name = '_%s%s' % (inst_cls.__name__, name)

        value = self.__func(inst)
        inst.__dict__[name] = value
        return value
    ...
  • lazy描述符:在lazy类中,定义了__get__()方法,因此它是一个描述符(任何实现了__get()__, __set()__, __delete()__方法的对象就是描述符,而一个class的属性是一个描述符的时候,对这个属性的访问都会触发特定的绑定行为)。
  • my_class:类内部的方法get_name用@lazy装饰,说白了就是调用了lazy(get_name),这时候就会对lazy类初始化,其中def __init__(self, func)的func便是get_name
  • obj.get_name:从第一条,我们已经知道lazy是一个描述符,第二条中用lazy装饰get_name,当执行obj.get_name时,python解释器会先从obj.__dict__中查找,若没找到就从my_class.__dict__中进行查找,由于lazy为描述符,便会调用__get()__方法
  • __get()__: 直接看代码如下,注意到,比如调用get_name()方法返回的值为value,返回后设置对象实例的__dict__,因此这样便实现了延迟化,然下次调用是直接就从__dict__中便可查找到想要的数据
value = self.__func(inst)
inst.__dict__[name] = value
我的lazy
class lazy(object):
    def __init__(self,func):
        self._func = func

    def __get__(self, instance, owner):
        value = self._func(instance)
        setattr(instance, self._func.__name__, value)
        return value

class my_class(object):
    def __init__(self, name):
        self.name = name

    @lazy
    def get_name(self):
        print '获取姓名'
        return self.name

obj = my_class('xiaoming')
print obj.name
print obj.__dict__
print obj.get_name
print obj.__dict__
print obj.get_name
print obj.get_name

# 结果
xiaoming
{'name': 'xiaoming'}
获取姓名
xiaoming
{'name': 'xiaoming', 'get_name': 'xiaoming'}
xiaoming
xiaoming
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值