前言
去年買了一本《圖解設計模式》,是用 java 寫的,今年不寫 java了,已經入了python 的坑,現在打算把書拿起來研讀,把裏面的 java 代碼全部轉成 python,這樣設計模式基本可以入門了。如果大家對設計模式感興趣的話,可以買一本看看,不過程序員的經典之作還是 GoF(the gang of four),翻譯成中文 “四人幫”,最開始就是四個人的論文整合的,不得不說老外起名字很有特點。
1.爲啥用迭代
萌新程序猿一般都對設計模式不大感冒,項目不大的話壓根就用不上。迭代模式的意義就是降低內存,你建了10個對象都存 list,一點問題沒有,1000萬個對象貌似對內存有點壓力了,所以迭代一般都涉及稍微大點的項目。
2.初識迭代
迭代就是建一個對象,然後不斷的用 next 方法獲取裏面的數據,聽起來和列表很像的,但是列表是佔內存的,可以改造一下列表讓它變成迭代器,如下:
import sys
if __name__ == '__main__':
list_ = []
list_iter = iter(list_)
for i in xrange(100000):
list_.append(i)
print 'list size:', sys.getsizeof(list_)
print type(list_)
print 'list iter size:', sys.getsizeof(list_iter)
print type(list_iter)
count = 0
for i in list_iter:
print i
count = count + 1
if count == 2:
break
結果:
list size: 824464
<type 'list'>
list iter size: 56
<type 'listiterator'>
0
1
首先說一下 sys.getsizeof() 函數(單位byte),它是獲取對象佔用內存的(計算方式有不精確的地方,但是整體上沒有大問題)
我們創建了10萬個數字放到列表裏面,總共佔內存 824464,把列表轉成迭代器以後佔比只有 56,不及萬分之一。
這是調用 python 已有的方式轉成迭代器,如果對象比較複雜不能用已有的方法怎麼辦,就需要自己建一個迭代器。
2.實現迭代器
python 中迭代器有兩個關鍵函數,如下,實現這兩個函數就可以了
__iter__ 用於生成迭代器
next 用於獲取遍歷獲取內容
給大家舉一個普遍的例子,幾乎在所有的迭代模式裏面都會有這個例子
import sys
class MyIter(object):
def __init__(self, n):
self.index = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.index < self.n:
value = self.index ** 2
self.index += 1
return value
else:
raise StopIteration()
if __name__ == '__main__':
my_iter = MyIter(100000000)
print 'my_iter size:', sys.getsizeof(my_iter)
print type(my_iter)
print my_iter.next()
print my_iter.next()
結果:
my_iter size: 56
<class '__main__.MyIter'>
0
1
我們創建了一個有1億數據的迭代對象,其內存佔比只有 56 (其實不止56,可能400左右,具體可以把迭代類的隱藏函數內存佔比都加起來,但是400仍然很小),如果建1億的對象存起來,內存肯定達到 M 單位,所以迭代類很省空間。
3.迭代類
其實看上面那個例子是有點懵的,例子很簡單,有時候搞不懂咋變換使用,所以下面建一個類,然後把類封裝成迭代類,這樣好看懂一些。
首先,建一個 Book 類,裏面寫一些需要的操作函數,get 等。
class Book(object):
def __init__(self, name, page):
self.name = name
self.page = page
def get_name(self):
return self.name
def get_page(self):
return self.page
把Book類當內容主體,實現迭代有兩種方式:
第一種藉助已有的迭代生成器工具類,例如1中的 listiterator,另外還有 itertools、enumerate、map、filter等
if __name__ == '__main__':
list_ = []
list_iter = iter(list_)
for i in xrange(100000):
list_.append(Book(name=str(i), page=10))
print type(list_iter)
print list_iter.next().get_name()
print list_iter.next().get_name()
結果:
<type 'listiterator'>
0
1
第二種是自己實現迭代類,下面這個是我仿照 java 的迭代設計模式寫的:
import sys
class BookShelf(object):
book_shelf = []
length = 0
def add(self, name, page):
self.book_shelf.append(Book(name=name, page=page))
self.length = self.length + 1
def __iter__(self):
return self
def next(self):
if self.length > 0:
return self.book_shelf.pop(0)
else:
raise StopIteration()
if __name__ == '__main__':
book_iter = BookShelf()
for i in xrange(1000000):
book_iter.add(name=str(i), page=11)
print sys.getsizeof(book_iter)
print type(book_iter)
print book_iter.next().get_name()
print book_iter.next().get_name()
結果:
56
<class '__main__.BookShelf'>
0
1
4.note
爲了節約內存有些函數建議少用,上面在生成列表的時候用的是 xrange,爲啥呢,因爲xrange是一個生成器,range少用,它生成的是列表,舉例說明:
import sys
if __name__ == '__main__':
a = xrange(1, 100000000, 2)
print sys.getsizeof(a)
print type(a)
b = range(1, 100000000, 2)
print sys.getsizeof(b)
print type(b)
結果:
32
<type 'xrange'>
400000064
<type 'list'>
另外,上面巴拉巴拉說那麼多,其實python最好的迭代方式是用yield生成器,仔細研究一下yield用法你就會發現它存在的意義就是爲了實現迭代。