概述
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