Python元類與迭代器生成器案例詳解
1.__getattr__和__getattribute__魔法函數
__getattr__是當類調用一個不存在的屬性時才會調用getattr魔法函數,他傳入的值item就是你這個調用的不存在的值。
__getattribute__則是無條件的優先執行,所以如果不是特殊情況最好不要用__getattribute__。
class User(object): def __init__(self, name, info): self.name = name self.info = info def __getattr__(self, item): return self.info[item] ls = User("李四",{"gender":"male"}) print(ls.gender)
2.屬性描述符
屬性描述符介紹
屬性描述符是一個強大的通用協議。它是properties, methods, static methods, class methods 和super()的調用原理。
屬性描述符協議
屬性描述符是實現瞭特定協議的類,隻要實現瞭__get__,__set__和__delete__三個方法中的任意一個,這個類就是描述符,它能實現對多個屬性運用相同存取邏輯的一種方式,通俗來說就是:創建一個實例,作為另一個類的類屬性。
註意
• 如果一個對象同時定義瞭__get__和__set__方法,它被稱做數據描述符(data descriptor)。
• 隻定義__get__方法的對象則被稱為非數據描述符(non-data descriptor)。
使用類方法創建描述符
• 定義一個IntField類為描述符類
• 創建IntField類的實例,作為另一個User類的屬性
class IntField(object): def __set__(self, instance, value): print("__set__") def __get__(self, instance, owner): print("__get__") def __delete__(self, instance): print("__delete__") class User(object): age = IntField() ls = User() ls.age ls.age = 30 del ls.age
使用屬性類型創建描述符
除瞭使用類當作一個屬性描述符,我們之前學習的 property(),就是可以輕松地為任意屬性創建可用的描述符。創建 property() 的語法是 property(fget=None, fset=None, fdel=None, doc=None)
描述符查找順序
• 當為數據描述符時, __get__優先級高於__dict__
• 當為非數據描述符時,__dict__優先級高於__get__
元類
元類介紹
元類實際上就是創建類的類
實現如下:
• 定義創建類的函數create_class
• 如果給create_class傳的參數為user,則創建User類
type()創建元類
• 第一個參數:name表示類名稱,字符串類型
• 第二個參數:bases表示繼承對象(父類),元組類型,單元素使用逗號
• 第三個參數:attr表示屬性,這裡可以填寫類屬性、類方式、靜態方法,采用字典格式,key為屬性名,value為屬性值
def __init__(self, name): self.name = name print("i am __init__") User = type("User", (), {"age":18 , "__init__":__init__}) obj = User("amy") print(obj.name)
metaclass屬性
如果一個類中定義瞭__metalass__ = xxx,Python就會用元類的方式來創建類,就可以控制類的創建行為
比如,以下代碼,再不改變類屬性的抒寫情況下,將屬性名規定為大寫訪問。
class MyClass(object): name = "ls" mc = MyClass() print(mc.name)
Python迭代器
迭代器指的是迭代取值的工具,迭代是指一個重復的過程,每一次重復都是基於上一次結果而來
迭代提供瞭一種通用的不依賴索引的迭代取值方式
可迭代對象
可以用for循環遍歷的對象都是可迭代對象。
• str,list,tuple,dict,set等都是可迭代對象。
• generator,包括生成器和帶yield的生成器函數。
判斷是否可迭代
除瞭看內置是否含有__iter__方法來判斷該對象是否是一個可迭代的對象之外,我們還可以使用 isinstance() 判斷一個對象是否是 Iterable 對象
• isinstance()–>用來判斷對象是否是相應類型,與type()類似。
from collections import Iterable,Iterator print(isinstance('abc',Iterable)) # True print(isinstance([1,2,3,4],Iterable)) # True print(isinstance(123,Iterable)) # False
迭代器對象
• 有內置的__next__()方法的對象,執行該方法可以不依賴索引取值
• 有內置的__iter__()方法的對象,執行迭代器的__iter__()方法得到的依然是迭代器本身
需要註意的是,可迭代對象不一定是迭代器
iter()
可以被next()函數調用並不斷返回下一個值的對象稱為迭代器:Iterator。
那我們可以通過iter()方法將可迭代的對象,轉為迭代器。
li = [1,2,3,4] lis = iter(li) print(type(lis)) # <class 'list_iterator'>
註意:
• 迭代器不可以通過下標取值,而是使用__next__()或者next()。但是隻要超出范圍則直接報錯StopIteration。
print(lis[0]) # 報錯 not subscriptable print(lis.__next__()) print(lis.__next__()) print(lis.__next__()) print(lis.__next__()) print(next(lis)) print(next(lis)) print(next(lis)) print(next(lis))
• next()隻能順延調用,不能往前。
可迭代對象與迭代器區別
• 可用於for循環的都是可迭代類型
• 作用於next()都是迭代器類型
• list、dict、str等都是可迭代的但不是迭代器,因為next()函數無法調用它們。可以通過iter()函數將它們轉為迭代器
• python的for循環本質就是通過不斷調用next()函數實現的
生成器
生成器定義
在Python中,一邊循環一邊計算的機制,稱為生成器:generator。
為什麼要有生成器
列表所有數據都在內存中,如果有海量數據的話會非常消耗內存。
比如說:我們僅僅需要訪問前面幾個元素,但後面絕大多元素占用的內存就會浪費瞭。
那麼生成器就是在循環的過程中根據算法不斷推算出後續的元素,這樣就不用創建整個完整的列表,從而節省大量的空間。
總而言之,就是當我們想要使用龐大數據,又想讓它占用的空間少,那就使用生成器。
如何創建生成器
生成器表達式
生成器表達式來源於迭代和列表解析的組合,生成器和列表解析類似,但是它使用()而不是[]。
g = (x for x in range(5)) print(g) # generator object print(next(g)) print(next(g)) print(next(g)) print(next(g)) print(next(g)) # 超出報錯 print(next(g)) for i in g: print(i)
生成器函數
當一個函數中包含yield關鍵字,那麼這個函數就不再是一個普通的函數,而是一個generator。調用函數就是創建瞭一個生成器對象。其工作原理就是通過重復調用next()或者__next__()方法,直到捕獲一個異常。
比如:
實現斐波那契數列,除第一個和第二個數外,任何一個數都可以由前兩個相加得到:
1,1,2,3,5,8,12,21,34…
def createNums(): print("-----func start-----") a,b = 0,1 for i in range(5): # print(b) print("--1--") yield b print("--2--") a,b = b,a+b print("--3--") print("-----func end-----") g = createNums() print(next(g)) print(next(g)) print(next(g)) print(next(g)) print(next(g))
註意:
• yield返回一個值,並且記住這個返回值的位置,下次遇到next()調用時,代碼從yield的下一條語句開始執行。與return的差別是,return也是返回一個值,但是直接結束函數。
迭代器與生成器
• 生成器能做到迭代器能做的所有事
• 而且因為生成器自動創建瞭iter()和next()方法,生成器顯得簡潔,而且高效。
讀取大文件
文件300G,文件比較特殊,一行 分隔符 {|}
def readlines(f,newline): buf = "" while True: while newline in buf: pos = buf.index(newline) yield buf[:pos] buf = buf[pos + len(newline):] chunk = f.read(4096*10) if not chunk: yield buf break buf += chunk with open('demo.txt') as f: for line in readlines(f,"{|}"): print(line)
到此這篇關於Python元類與迭代器生成器案例詳解的文章就介紹到這瞭,更多相關Python元類與迭代器生成器內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Python淺析迭代器Iterator的使用
- Python生成器與迭代器詳情
- 一篇文章帶你學習Python3的高級特性(2)
- Python高級特性之切片迭代列表生成式及生成器詳解
- 一文搞懂python 中的迭代器和生成器