迭代器
迭代的概念
使用for循環遍歷取值的過程叫做迭代,比如:使用for循環遍歷列表獲取值的過程
使用for循環遍歷取值的對象叫做可迭代對象, 比如:列表、元組、字典、集合、range、字符串
判斷對象是否是指定類型
~~~
from collections import Iterable
result = isinstance((3,5), Iterable)
~~~
自定義可迭代對象: 在類里面定義__iter__方法創建的對象就是可迭代對象
可迭代對象的本質:遍歷可迭代對象的時候其實獲取的是可迭代對象的迭代器, 然后通過迭代器獲取對象中的數據
自定義迭代器對象: 在類里面定義__iter__和__next__方法創建的對象就是迭代器對象
注:在這里有兩個概念,一個是?可迭代對象 一個是?迭代器對象 不要混淆
demo:
~~~
from collections import Iterator
# 自定義可迭代對象: 在類里面定義__iter__方法創建的對象就是可迭代對象
class MyList(object):
????def __init__(self):
????????self.my_list =list()
????# 添加指定元素
? ? def append_item(self, item):
????????self.my_list.append(item)
????def __iter__(self):
????# 可迭代對象的本質:遍歷可迭代對象的時候其實獲取的是可迭代對象的迭代器, 然后通過迭代器獲取對象中的數據
? ? ? ? my_iterator = MyIterator(self.my_list)
????????return my_iterator
# 自定義迭代器對象: 在類里面定義__iter__和__next__方法創建的對象就是迭代器對象
class MyIterator(object):
????def __init__(self, my_list):
????????self.my_list = my_list
????????# 記錄當前獲取數據的下標
? ? ? ? self.current_index =0
? ? ? ? # 判斷當前對象是否是迭代器
? ? ? ? result =isinstance(self, Iterator)
????????print("MyIterator創建的對象是否是迭代器:", result)
????def __iter__(self):
????????return self
? ? # 獲取迭代器中下一個值
? ? def __next__(self):
????????if self.current_index
????????????self.current_index +=1
? ? ? ? ????return self.my_list[self.current_index -1]
????????else:
????????????# 數據取完了,需要拋出一個停止迭代的異常
? ? ? ? ? ? raise StopIteration
my_list = MyList()
my_list.append_item(1)
my_list.append_item(2)
result =isinstance(my_list, Iterable)
print(result)
for value in my_list:
????print(value)
~~~
iter()函數與next()函數
iter函數: 獲取可迭代對象的迭代器,會調用可迭代對象身上的__iter__方法
next函數: 獲取迭代器中下一個值,會調用迭代器對象身上的__next__方法
?for循環的本質
遍歷的是可迭代對象
for item in Iterable 循環的本質就是先通過iter()函數獲取可迭代對象Iterable的迭代器,然后對獲取到的迭代器不斷調用next()方法來獲取下一個值并將其賦值給item,當遇到StopIteration的異常后循環結束。
遍歷的是迭代器
for item in Iterator 循環的迭代器,不斷調用next()方法來獲取下一個值并將其賦值給item,當遇到StopIteration的異常后循環結束。
生成器
生成器是一類特殊的迭代器,它不需要再像上面的類一樣寫__iter__()和__next__()方法了, 使用更加方便,它依然可以使用next函數和for循環取值
創建生成器方法1
通過列表生成式,我們可以直接創建一個列表。但是,受到內存限制,列表容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。
所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器(Generator)。
要創建一個generator,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[]改成(),就創建了一個generator:
(i for i in range(5))
如果我們打印它的類型我們可以看到是<class 'generator'>?
創建L和g的區別僅在于最外層的[]和(),L是一個list,而g是一個generator。
我們可以直接打印出list的每一個元素,但我們怎么打印出generator的每一個元素呢?
如果要一個一個打印出來,可以通過generator的next()方法:
創建生成器方法2
在def函數里面看到有yield關鍵字那么就是生成器
demo(斐波那契額數列)
~~~
def fibonacci(num):
????a =0
? ? b =1
? ? # 記錄生成fibonacci數字的下標
? ? current_index =0
????while current_index < num:
????????result = a
????????a, b = b, a + b
????????current_index +=1
????????# 代碼執行到yield會暫停,然后把結果返回出去,下次啟動生成器會在暫停的位置繼續往下執行
? ? ? ? yield result
for i in?fibonacci(5):
? ? print(i)
只要在def中有yield關鍵字的 就稱為 生成器
使用send方法啟動生成器并傳參
def gen():
? ?i =0
? ?while i<5:
? ? ? ? temp =yield i????
????????print(temp)
????????i+=1
我們可以調用send方法給yield傳值
f = gen()
next(f)? ?#首次調用必須是next,因為生成器還沒有執行到yield 無法進行接收值
f.send('haha')? ?#send 發送值 傳遞給生成器 由temp進行接收
next(f)? ?#再次調用next? 相當于send了一個None
f.send('haha')
~~~
yield和return的對比
使用了yield關鍵字的函數不再是函數,而是生成器。(使用了yield的函數就是生成器)
代碼執行到yield會暫停,然后把結果返回出去,下次啟動生成器會在暫停的位置繼續往下執行
每次啟動生成器都會返回一個值,多次啟動可以返回多個值,也就是yield可以返回多個值
return只能返回一次值,代碼執行到return語句就停止迭代,拋出停止迭代異常
yield可以在下次執行的時候接受傳遞的值
生成器創建有兩種方式,一般都使用yield關鍵字方法創建生成器
yield特點是代碼執行到yield會暫停,把結果返回出去,再次啟動生成器在暫停的位置繼續往下執行
裝飾器
裝飾器使用的原理就是閉包,在函數的時候介紹過閉包的一個使用:
http://www.lxweimin.com/p/40ab45debf43
裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象。它經常用于有切面需求的場景,比如:插入日志、性能測試、事務處理、緩存、權限校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼并繼續重用。
概括的講,裝飾器的作用就是為已經存在的函數或對象添加額外的功能。
簡單裝飾器
~~~
def test(fun):
????def test_in():
????#增加的功能
? ? ? ? print("這是我新增的功能")
????????return? fun()? ? #一般我們把函數的結果返回,不會自己直接執行
????# 其實這里返回的就是閉包的結果
? ? return test_in#返回一定是內部函數的引用,不能帶有小括號
@test
def fun():
????return "rookieyu"
print(fun())
~~~
簡單的裝飾器,函數沒有參數傳遞
裝飾器 @test
相當于 fun = test(fun)??
在這里調用的test ,然后把fun這個函數當做一個對象傳遞給test函數,返回的是test_in函數重新賦值給fun
最后我們在執行fun()的時候,相當于執行的是test(fun)() 是test_in函數。執行后會在函數里面先執行我們增加的功能,之后把函數之后的結果返回。
函數帶參數裝飾器
~~~
def debug(func):
????def wrapper(*args, **kwargs):# 指定宇宙無敵參數
? ? ? ? print("[DEBUG]: enter {}()".format(func.__name__))
????????print('Prepare and say...',)
????????return func(*args, **kwargs)
????return wrapper? # 返回
@debug
def say(something):
????print("hello {}!".format(something))
say("rookieyu")
~~~
我們采用的是*args, **kwargs,這樣我們再調用裝飾器的時候可以隨意設置變量,讓我們的裝飾器更具有通用性
裝飾器帶參數,在原有裝飾器的基礎上,設置外部變量
~~~
def logging(level):
????def wrapper(func):
????????def inner_wrapper(*args, **kwargs):
????????????print("[{level}]: enter function {func}()".format(level=level,func=func.__name__))
????????????return func(*args, **kwargs)
????????return inner_wrapper
????return wrapper
@logging(level='INFO')
def say(something):
????print("say {}!".format(something))
# 如果沒有使用@語法,等同于
# say = logging(level='INFO')(say)
@logging(level='DEBUG')
def do(something):
????print("do {}...".format(something))
if __name__ =='__main__':
????say('hello')
????do("my work")
~~~
在這里我們可以看到裝飾器也可以傳參數的,但是我們必須要再去做一層嵌套的使用。
基于類實現的裝飾器
裝飾器函數其實是這樣一個接口約束,它必須接受一個callable對象作為參數,然后返回一個callable對象。在Python中一般callable對象都是函數,但也有例外。只要某個對象重載了__call__()方法,那么這個對象就是callable的。
demo:
~~~
class logging(object):
????def __init__(self, func):
????????self.func = func
????def __call__(self, *args, **kwargs):
????????print("[DEBUG]: enter function {func}()".format(func=self.func.__name__))
????????return self.func(*args, **kwargs)
@logging
def say(something):
????print("say {}!".format(something))
say("rookie")
~~~
內置的裝飾器
內置的裝飾器和普通的裝飾器原理是一樣的,只不過返回的不是函數,而是類對象,所以更難理解一些。
@property? 可以將一個方法,當做一個屬性來使用
@staticmethod? ?將類方法裝換成一個靜態方法
@classmethod? 將類方法裝換成為類方法