python學習之延遲初始化
阿新 • • 發佈:2019-01-31
概述
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