Python??序列化反序列化和異常處理的問題小結
1.迭代器
迭代是訪問集合元素的一種方式。迭代器是一個可以記住遍歷的位置的對象。迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會后退。
1.1 可迭代對象
我們已經(jīng)知道可以對list、tuple、str等類型的數(shù)據(jù)使用for...in...的循環(huán)語法從其中依次拿到數(shù)據(jù)進行使用,我們把這樣的過程稱為遍歷,也叫迭代。
但是,是否所有的數(shù)據(jù)類型都可以放到for...in...的語句中,然后讓for...in...每次從中取出一條數(shù)據(jù)供我們使用,即供我們迭代嗎?
>>> for i in 100: ... print(i) ... Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'int' object is not iterable >>> # int整型不是iterable,即int整型不是可以迭代的
我們把可以通過for...in...這類語句迭代讀取一條數(shù)據(jù)供我們使用的對象稱之為可迭代對象(Iterable)。
1.2 如何判斷一個對象是否可以迭代
可以使用 isinstance() 判斷一個對象是否是 Iterable 對象:
In [50]: from collections import Iterable In [51]: isinstance([], Iterable) Out[51]: True In [52]: isinstance({}, Iterable) Out[52]: True In [53]: isinstance('abc', Iterable) Out[53]: True In [54]: isinstance(mylist, Iterable) Out[54]: False In [55]: isinstance(100, Iterable) Out[55]: False
1.3 可迭代對象的本質
我們分析對可迭代對象進行迭代使用的過程,發(fā)現(xiàn)每迭代一次(即在for...in...中每循環(huán)一次)都會返回對象中的下一條數(shù)據(jù),一直向后讀取數(shù)據(jù)直到迭代了所有數(shù)據(jù)后結束。那么,在這個過程中就應該有一個“人”去記錄每次訪問到了第幾條數(shù)據(jù),以便每次迭代都可以返回下一條數(shù)據(jù)。我們把這個能幫助我們進行數(shù)據(jù)迭代的“人”稱為迭代器(Iterator)。
可迭代對象的本質就是可以向我們提供一個這樣的中間“人”即迭代器幫助我們對其進行迭代遍歷使用。
可迭代對象通過__iter__
方法向我們提供一個迭代器,我們在迭代一個可迭代對象的時候,實際上就是先獲取該對象提供的一個迭代器,然后通過這個迭代器來依次獲取對象中的每一個數(shù)據(jù).
那么也就是說,一個具備了__iter__
方法的對象,就是一個可迭代對象。
from collections.abc import Iterable class Demo(object): def __init__(self, n): self.n = n self.current = 0 def __iter__(self): pass demo = Demo(10) print(isinstance(demo, Iterable)) # True for d in demo: # 重寫了 __iter__ 方法以后,demo就是一個一個可迭代對象了,可以放在for...in的后面 print(d) # 此時再使用for...in循環(huán)遍歷,會提示 TypeError: iter() returned non-iterator of type 'NoneType' # 這是因為,一個可迭代對象如果想要被for...in循環(huán),它必須要有一個迭代器
1.4 迭代器Iterator
通過上面的分析,我們已經(jīng)知道,迭代器是用來幫助我們記錄每次迭代訪問到的位置,當我們對迭代器使用next()函數(shù)的時候,迭代器會向我們返回它所記錄位置的下一個位置的數(shù)據(jù)。實際上,在使用next()函數(shù)的時候,調(diào)用的就是迭代器對象的__next__
方法(Python3中是對象的__next__
方法,Python2中是對象的next()方法)。所以,我們要想構造一個迭代器,就要實現(xiàn)它的*next*方法。但這還不夠,python要求迭代器本身也是可迭代的,所以我們還要為迭代器實現(xiàn)__iter__
方法,而__iter__
方法要返回一個迭代器,迭代器自身正是一個迭代器,所以迭代器的__iter__
方法返回自身即可。
一個實現(xiàn)了*iter*方法和*next*方法的對象,就是迭代器。
class MyIterator(object): def __init__(self, n): self.n = n self.current = 0 # 自定義迭代器需要重寫__iter__和__next__方法 def __iter__(self): return self def __next__(self): if self.current < self.n: value = self.current self.current += 1 return value else: raise StopIteration my_it = MyIterator(10) for i in my_it: # 迭代器重寫了__iter__方法,它本身也是一個可迭代對象 print(i)
1.5 如何判斷一個對象是否迭代器
調(diào)用一個對象的__iter__
方法,或者調(diào)用iter()內(nèi)置函數(shù),可以獲取到一個可迭代對象的迭代器。
names = ['hello', 'good', 'yes'] print(names.__iter__()) # 調(diào)用對象的__iter__()方法 print(iter(names)) # 調(diào)用iter()內(nèi)置函數(shù)
可以使用 isinstance() 判斷一個對象是否是 Iterator 對象:
from collections.abc import Iterator names = ['hello', 'good', 'yes'] print(isinstance(iter(names), Iterator))
1.6 for...in...循環(huán)的本質
for item in Iterable 循環(huán)的本質就是先通過iter()函數(shù)獲取可迭代對象Iterable的迭代器,然后對獲取到的迭代器不斷調(diào)用next()方法來獲取下一個值并將其賦值給item,當遇到StopIteration的異常后循環(huán)結束。
1.7 迭代器的應用場景
我們發(fā)現(xiàn)迭代器最核心的功能就是可以通過next()函數(shù)的調(diào)用來返回下一個數(shù)據(jù)值。如果每次返回的數(shù)據(jù)值不是在一個已有的數(shù)據(jù)集合中讀取的,而是通過程序按照一定的規(guī)律計算生成的,那么也就意味著可以不用再依賴一個已有的數(shù)據(jù)集合,也就是說不用再將所有要迭代的數(shù)據(jù)都一次性緩存下來供后續(xù)依次讀取,這樣可以節(jié)省大量的存儲(內(nèi)存)空間。
舉個例子,比如,數(shù)學中有個著名的斐波拉契數(shù)列(Fibonacci),數(shù)列中第一個數(shù)為0,第二個數(shù)為1,其后的每一個數(shù)都可由前兩個數(shù)相加得到:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
現(xiàn)在我們想要通過for...in...循環(huán)來遍歷迭代斐波那契數(shù)列中的前n個數(shù)。那么這個斐波那契數(shù)列我們就可以用迭代器來實現(xiàn),每次迭代都通過數(shù)學計算來生成下一個數(shù)。
class FibIterator(object): """斐波那契數(shù)列迭代器""" def __init__(self, n): """ :param n: int, 指明生成數(shù)列的前n個數(shù) """ self.n = n # current用來保存當前生成到數(shù)列中的第幾個數(shù)了 self.current = 0 # num1用來保存前前一個數(shù),初始值為數(shù)列中的第一個數(shù)0 self.num1 = 0 # num2用來保存前一個數(shù),初始值為數(shù)列中的第二個數(shù)1 self.num2 = 1 def __next__(self): """被next()函數(shù)調(diào)用來獲取下一個數(shù)""" if self.current < self.n: num = self.num1 self.num1, self.num2 = self.num2, self.num1+self.num2 self.current += 1 return num else: raise StopIteration def __iter__(self): """迭代器的__iter__返回自身即可""" return self if __name__ == '__main__': fib = FibIterator(10) for num in fib: print(num, end=" ")
2.生成器
利用迭代器,我們可以在每次迭代獲取數(shù)據(jù)(通過next()方法)時按照特定的規(guī)律進行生成。但是我們在實現(xiàn)一個迭代器時,關于當前迭代到的狀態(tài)需要我們自己記錄,進而才能根據(jù)當前狀態(tài)生成下一個數(shù)據(jù)。為了達到記錄當前狀態(tài),并配合next()函數(shù)進行迭代使用,我們可以采用更簡便的語法,即生成器(generator)。生成器是一類特殊的迭代器。
2.1 創(chuàng)建生成器方法1
要創(chuàng)建一個生成器,有很多種方法。第一種方法很簡單,只要把一個列表生成式的 [ ] 改成 ( )
In [15]: L = [ x*2 for x in range(5)] In [16]: L Out[16]: [0, 2, 4, 6, 8] In [17]: G = ( x*2 for x in range(5)) In [18]: G Out[18]: <generator object <genexpr> at 0x7f626c132db0> In [19]:
創(chuàng)建 L 和 G 的區(qū)別僅在于最外層的 [ ] 和 ( ) , L 是一個列表,而 G 是一個生成器。我們可以直接打印出列表L的每一個元素,而對于生成器G,我們可以按照迭代器的使用方法來使用,即可以通過next()函數(shù)、for循環(huán)、list()等方法使用。
In [19]: next(G) Out[19]: 0 In [20]: next(G) Out[20]: 2 In [21]: next(G) Out[21]: 4 In [22]: next(G) Out[22]: 6 In [23]: next(G) Out[23]: 8 In [24]: next(G) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-24-380e167d6934> in <module>() ----> 1 next(G) StopIteration: In [25]: In [26]: G = ( x*2 for x in range(5)) In [27]: for x in G: ....: print(x) ....: 0 2 4 6 8 In [28]:
2.2 創(chuàng)建生成器方法2
generator非常強大。如果推算的算法比較復雜,用類似列表生成式的 for 循環(huán)無法實現(xiàn)的時候,還可以用函數(shù)來實現(xiàn)。
我們?nèi)匀挥蒙弦还?jié)提到的斐波那契數(shù)列來舉例,回想我們在上一節(jié)用迭代器的實現(xiàn)方式:
class FibIterator(object): """斐波那契數(shù)列迭代器""" def __init__(self, n): """ :param n: int, 指明生成數(shù)列的前n個數(shù) """ self.n = n # current用來保存當前生成到數(shù)列中的第幾個數(shù)了 self.current = 0 # num1用來保存前前一個數(shù),初始值為數(shù)列中的第一個數(shù)0 self.num1 = 0 # num2用來保存前一個數(shù),初始值為數(shù)列中的第二個數(shù)1 self.num2 = 1 def __next__(self): """被next()函數(shù)調(diào)用來獲取下一個數(shù)""" if self.current < self.n: num = self.num1 self.num1, self.num2 = self.num2, self.num1+self.num2 self.current += 1 return num else: raise StopIteration def __iter__(self): """迭代器的__iter__返回自身即可""" return self
注意,在用迭代器實現(xiàn)的方式中,我們要借助幾個變量(n、current、num1、num2)來保存迭代的狀態(tài)?,F(xiàn)在我們用生成器來實現(xiàn)一下。
In [30]: def fib(n): ....: current = 0 ....: num1, num2 = 0, 1 ....: while current < n: ....: yield num1 ....: num1, num2 = num2, num1+num2 ....: current += 1 ....: return 'done' ....: In [31]: F = fib(5) In [32]: next(F) Out[32]: 1 In [33]: next(F) Out[33]: 1 In [34]: next(F) Out[34]: 2 In [35]: next(F) Out[35]: 3 In [36]: next(F) Out[36]: 5 In [37]: next(F) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-37-8c2b02b4361a> in <module>() ----> 1 next(F) StopIteration: done
在使用生成器實現(xiàn)的方式中,我們將原本在迭代器__next__
方法中實現(xiàn)的基本邏輯放到一個函數(shù)中來實現(xiàn),但是將每次迭代返回數(shù)值的return換成了yield,此時新定義的函數(shù)便不再是函數(shù),而是一個生成器了。簡單來說:只要在def中有yield關鍵字的 就稱為 生成器
此時按照調(diào)用函數(shù)的方式( 案例中為F = fib(5) )使用生成器就不再是執(zhí)行函數(shù)體了,而是會返回一個生成器對象( 案例中為F ),然后就可以按照使用迭代器的方式來使用生成器了。
In [38]: for n in fib(5): ....: print(n) ....: 1 1 2 3 5 In [39]:
但是用for循環(huán)調(diào)用generator時,發(fā)現(xiàn)拿不到generator的return語句的返回值。如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中:
In [39]: g = fib(5) In [40]: while True: ....: try: ....: x = next(g) ....: print("value:%d"%x) ....: except StopIteration as e: ....: print("生成器返回值:%s"%e.value) ....: break ....: value:1 value:1 value:2 value:3 value:5 生成器返回值:done In [41]:
總結:
- 使用了yield關鍵字的函數(shù)不再是函數(shù),而是生成器。(使用了yield的函數(shù)就是生成器)
- yield關鍵字有兩點作用:
- 保存當前運行狀態(tài)(斷點),然后暫停執(zhí)行,即將生成器(函數(shù))掛起
- 將yield關鍵字后面表達式的值作為返回值返回,此時可以理解為起到了return的作用
- 可以使用next()函數(shù)讓生成器從斷點處繼續(xù)執(zhí)行,即喚醒生成器(函數(shù))
- Python3中的生成器可以使用return返回最終運行的返回值,而Python2中的生成器不允許使用return返回一個返回值(即可以使用return從生成器中退出,但return后不能有任何表達式)。
2.3 使用send喚醒
我們除了可以使用next()函數(shù)來喚醒生成器繼續(xù)執(zhí)行外,還可以使用send()函數(shù)來喚醒執(zhí)行。使用send()函數(shù)的一個好處是可以在喚醒的同時向斷點處傳入一個附加數(shù)據(jù)。
例子:執(zhí)行到y(tǒng)ield時,gen函數(shù)作用暫時保存,返回i的值; temp接收下次c.send("python"),send發(fā)送過來的值,c.next()等價c.send(None)
In [10]: def gen(): ....: i = 0 ....: while i<5: ....: temp = yield i ....: print(temp) ....: i+=1 ....:
使用send
In [43]: f = gen() In [44]: next(f) Out[44]: 0 In [45]: f.send('haha') haha Out[45]: 1 In [46]: next(f) None Out[46]: 2 In [47]: f.send('haha') haha Out[47]: 3 In [48]:
使用next函數(shù)
In [18]: f = gen() In [19]: f.__next__() Out[19]: 0 In [20]: f.__next__() None Out[20]: 1 In [21]: f.__next__() None Out[21]: 2 In [22]: f.__next__() None Out[22]: 3 In [23]: f.__next__() None Out[23]: 4 In [24]: f.__next__() None --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-24-39ec527346a9> in <module>() ----> 1 f.__next__() StopIteration:
使用__next__()
方法(不常使用)
In [18]: f = gen() In [19]: f.__next__() Out[19]: 0 In [20]: f.__next__() None Out[20]: 1 In [21]: f.__next__() None Out[21]: 2 In [22]: f.__next__() None Out[22]: 3 In [23]: f.__next__() None Out[23]: 4 In [24]: f.__next__() None --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-24-39ec527346a9> in <module>() ----> 1 f.__next__() StopIteration:
3.property屬性的使用
property屬性是一種用起來像是實例屬性一樣的特殊屬性,可以對應于某個方法。
class Foo: def func(self): pass # 定義property屬性 @property def prop(self): pass # ############### 調(diào)用 ############### foo_obj = Foo() foo_obj.func() # 調(diào)用實例方法 foo_obj.prop # 調(diào)用property屬性
property屬性的定義和調(diào)用要注意一下幾點:
- 定義時,在實例方法的基礎上添加 @property 裝飾器;并且僅有一個self參數(shù)
- 調(diào)用時,無需括號
方法:foo_obj.func() property屬性:foo_obj.prop
簡單的實例
對于京東商城中顯示電腦主機的列表頁面,每次請求不可能把數(shù)據(jù)庫中的所有內(nèi)容都顯示到頁面上,而是通過分頁的功能局部顯示,所以在向數(shù)據(jù)庫中請求數(shù)據(jù)時就要顯示的指定獲取從第m條到第n條的所有數(shù)據(jù) 這個分頁的功能包括:
- 根據(jù)用戶請求的當前頁和總數(shù)據(jù)條數(shù)計算出 m 和 n
- 根據(jù)m 和 n 去數(shù)據(jù)庫中請求數(shù)據(jù)
# ############### 定義 ############### class Pager: def __init__(self, current_page): # 用戶當前請求的頁碼(第一頁、第二頁...) self.current_page = current_page # 每頁默認顯示10條數(shù)據(jù) self.per_items = 10 @property def start(self): val = (self.current_page - 1) * self.per_items return val @property def end(self): val = self.current_page * self.per_items return val # ############### 調(diào)用 ############### p = Pager(1) p.start # 就是起始值,即:m p.end # 就是結束值,即:n
從上述可見
Python的property屬性的功能是:property屬性內(nèi)部進行一系列的邏輯計算,最終將計算結果返回。
3.1 property屬性的兩種方式
- 裝飾器 即:在方法上應用裝飾器
- 類屬性 即:在類中定義值為property對象的類屬性
3.1.1 裝飾器
- 在類的實例方法上應用@property裝飾器
Python中的類有經(jīng)典類和新式類,新式類的屬性比經(jīng)典類的屬性豐富。( 如果類繼object,那么該類是新式類 )
經(jīng)典類的實現(xiàn):
class Goods: @property def price(self): return "laowang" obj = Goods() result = obj.price # 自動執(zhí)行 @property 修飾的 price 方法,并獲取方法的返回值 print(result)
新式類的實現(xiàn):
class Goods: """ 只有在python3中才有@xxx.setter @xxx.deleter """ def __init__(self): # 原價 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() obj.price # 獲取商品價格 obj.price = 200 # 修改商品原價 del obj.price # 刪除商品原價
總結:
- 經(jīng)典類中的屬性只有一種訪問方式,其對應被 @property 修飾的方法
- 新式類中的屬性有三種訪問方式,并分別對應了三個被@property、@方法名.setter、@方法名.deleter修飾的方法
3.1.2 類屬性方式
當使用類屬性的方式創(chuàng)建property屬性時,經(jīng)典類和新式類無區(qū)別。
class Foo: def get_bar(self): return 'laowang' BAR = property(get_bar) obj = Foo() reuslt = obj.BAR # 自動調(diào)用get_bar方法,并獲取方法的返回值 print(reuslt)
property方法中有個四個參數(shù)
- 第一個參數(shù)是方法名,調(diào)用 對象.屬性 時自動觸發(fā)執(zhí)行方法
- 第二個參數(shù)是方法名,調(diào)用 對象.屬性 = XXX 時自動觸發(fā)執(zhí)行方法
- 第三個參數(shù)是方法名,調(diào)用 del 對象.屬性 時自動觸發(fā)執(zhí)行方法
- 第四個參數(shù)是字符串,調(diào)用 對象.屬性.doc ,此參數(shù)是該屬性的描述信息
class Foo(object): def get_bar(self): print("getter...") return 'laowang' def set_bar(self, value): """必須兩個參數(shù)""" print("setter...") return 'set value' + value def del_bar(self): print("deleter...") return 'laowang' BAR = property(get_bar, set_bar, del_bar, "description...") obj = Foo() obj.BAR # 自動調(diào)用第一個參數(shù)中定義的方法:get_bar obj.BAR = "alex" # 自動調(diào)用第二個參數(shù)中定義的方法:set_bar方法,并將“alex”當作參數(shù)傳入 desc = Foo.BAR.__doc__ # 自動獲取第四個參數(shù)中設置的值:description... print(desc) del obj.BAR # 自動調(diào)用第三個參數(shù)中定義的方法:del_bar方法
總結:
- 定義property屬性共有兩種方式,分別是【裝飾器】和【類屬性】,而【裝飾器】方式針對經(jīng)典類和新式類又有所不同。
- 通過使用property屬性,能夠簡化調(diào)用者在獲取數(shù)據(jù)的流程。
到此這篇關于Python 序列化反序列化和異常處理的文章就介紹到這了,更多相關Python 序列化反序列化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
scikit-learn線性回歸,多元回歸,多項式回歸的實現(xiàn)
這篇文章主要介紹了scikit-learn線性回歸,多元回歸,多項式回歸的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-08-08python讀取json文件并將數(shù)據(jù)插入到mongodb的方法
這篇文章主要介紹了python讀取json文件并將數(shù)據(jù)插入到mongodb的方法,實例分析了Python操作json及mongodb數(shù)據(jù)庫的技巧,需要的朋友可以參考下2015-03-03Python?Pandas刪除替換并提取其中的缺失值NaN(dropna,fillna,isnull)
這篇文章主要給大家介紹了關于Python?Pandas刪除替換并提取其中的缺失值NaN(dropna,fillna,isnull)的相關資料,文中通過實例代碼介紹的非常詳細,對大家學習或者使用Pandas具有一定的參考學習價值,需要的朋友可以參考下2022-01-01Python中輸入若干整數(shù)以逗號間隔實現(xiàn)統(tǒng)計每個整數(shù)出現(xiàn)次數(shù)
這篇文章主要介紹了Python中輸入若干整數(shù)以逗號間隔實現(xiàn)統(tǒng)計每個整數(shù)出現(xiàn)次數(shù)的相關資料,需要的小伙伴可以參考一下,希望對你有所幫助2022-04-04