python 小兵(6)迭代器
閱讀目錄
回到頂部函式名的使用以及第一類物件
函式名的運用
函式名是一個變量, 但它是一個特殊的變量, 與括號配合可以執行函式的變量
1.函式名的記憶體地址
1 2 3 4 |
def func():
print
(
"呵呵"
)
print
(func)
結果: <function func at
0x1101e4ea0
>
|
2. 函式名可以賦值給其他變數
1 2 3 4 5 |
def
func():
print
(
"呵呵"
)
print
(func)
a
=
func
# 把函式當成一個變量賦值給另一個變量
a()
# 函式呼叫 func()
|
3. 函式名可以當做容器類的元素
1 2 3 4 5 6 7 8 9 10 11 |
def
func1():
print
(
"呵呵"
)
def
func2():
print
(
"呵呵"
)
def
func3():
print
(
"呵呵"
)
def
func4():
print
(
"呵呵"
)
lst
=
[func1, func2, func3]
for
i
in
lst:
i()
|
4. 函式名可以當做函式的引數
1 2 3 4 5 6 7 |
def
func():
print
(
"吃了麼"
)
def
func2(fn):
print
(
"我是func2"
)
fn()
# 執行傳遞過來的fn
print
(
"我是func2"
)
func2(func)
# 把函式func當成引數傳遞給func2的引數fn.
|
5. 函式名可以作為函式的返回值
1 2 3 4 5 6 7 8 9 |
def
func_1():
print
(
"這⾥裡里是函式1"
)
def
func_2():
print
(
"這⾥裡里是函式2"
)
print
(
"這⾥裡里是函式1"
)
return
func_2
fn
=
func_1()
# 執行函式1. 函式1返回的是函式2, 這時fn指向的就是上⾯面函式2
fn()
# 執行上面返回的函
|
閉包
什麼是閉包? 閉包就是內層函式, 對外層函式(非全域性)的變量的引用. 叫閉包
1 2 3 4 5 6 7 8 |
def
func1():
name
=
"alex"
def
func2():
print
(name)
# 閉包
func2()
func1()
# 結果: alex
|
我們可以使用__closure__來檢測函式是否是閉包. 使用函式名.__closure__返回cell就是
閉包. 返回None就不是閉包
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def
func1():
name
=
"alex"
def
func2():
print
(name)
# 閉包
func2()
print
(func2.__closure__)
func1()
結果:
alex
(<cell at
0x0000020077EFC378
:
str
object
at
0x00000200674DC340
>,)
返回的結果不是
None
就是閉包
|
現在有個問題,這個閉包只能在裡邊呼叫啊,外邊的怎麼呼叫呢?
1 2 3 4 5 6 7 8 |
def
outer():
name
=
"alex"
# 內部函式
def
inner():
print
(name)
return
inner
fn
=
outer()
# 訪問外部函式, 獲取到內部函式的函式地址
fn()
# 訪問內部函式
|
這樣就實現了外部訪問,那如果多層巢狀呢?很簡單,只需要一層一層的往外層返回就行了
1 2 3 4 5 6 7 |
def
func1():
def
func2():
def
func3():
print
(
"嘿嘿"
)
return
func3
return
func2
func1()()()
|
由它我們可以引出閉包的好處. 由於我們在外界可以訪問內部函式. 那這個時候內部函式訪問的時間和時機就不一定了, 因為在外部, 我可以選擇在任意的時間去訪問內部函式. 這 個時候. 想一想. 我們之前說過, 如果一個函式執行完畢. 則這個函式中的變量以及區域性名稱空間中的內容都將會被銷燬. 在閉包中. 如果變量被銷燬了. 那內部函式將不能正常執行. 所 以. python規定. 如果你在內部函式中訪問了外層函式中的變量. 那麼這個變量將不會消亡. 將會常駐在記憶體中. 也就是說. 使用閉包, 可以保證外層函式中的變量在記憶體中常駐. 這樣做 有什麼好處呢? 非常大的好處. 我們來看看下邊的程式碼
1 2 3 4 5 6 7 8 9 10 |
def func():
name =
'alex'
def foo():
print(name)
return
foo
msg = func()
msg() #這樣的話就是將name=
'alex'
存放在一個常駐的記憶體中,並且外界不能修改
|
閉包的作用就是讓一個變數能夠常駐記憶體,供後面的程式使用
回到頂部迭代器
我們之前一直在用可迭代物件進行操作,那麼到底什麼是可迭代物件.我們現在就來討論討論可迭代物件.首先我們先回顧下我們
熟知的可迭代物件有哪些:
str list tuple dic set 那為什麼我們稱他們為可迭代物件呢?因為他們都遵循了可迭代協議,那什麼又是可迭代協議呢.首先我們先看一段錯誤的程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
對的
s
=
'abc'
for
i
in
s:
print
(i)
結果:
a
b
c
錯的
for
i
in
123
:
print
(i)
結果
Traceback (most recent call last):
File
"D:/python_object/二分法.py"
, line
62
,
in
<module>
for
i
in
123
:
TypeError:
'int'
object
is
not
iterable
|
注意看報錯資訊,報錯資訊中有這樣一句話: 'int' object is not iterable 翻譯過來就是整數型別物件是不可迭代的.
iterable表示可迭代的.表示可迭代協議 那麼如何進行驗證你的資料型別是否符合可迭代協議.我們可以通過dir函式來檢視類中定義好的
所有方法
1 2 3 |
a
=
'abc'
print
(
dir
(a))
# dir檢視物件的方法和函式
# 在列印結果中尋找__iter__ 如果存在就表示當前的這個型別是個可迭代物件
|
我們剛剛測了字串中是存在__iter__的,那我們來看看 列表,元祖,字典.集合中是不是有存在__iter__
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 列表
lst
=
[
1
,
2
]
print
(
dir
(lst))
# 元祖
tuple
=
(
1
,
2
)
print
(
dir
(
tuple
))
# 字典
dic
=
{
'a'
:
1
,
'b'
:
2
}
print
(
dir
(dic))
# 集合
se
=
{
1
,
2
,
3
,
4
,
4
}
print
(
dir
(se))
|
是不是發現以上都有__iter__並且還很for迴圈啊,其實也可以這麼說可以for迴圈的就有__iter__方法,包括range
1 |
print
(
dir
(
range
))
|
這是檢視一個物件是否是可迭代物件的第一種方法,我們還可以通過isinstence()函式來檢視一個物件是什麼型別的
1 2 3 4 5 6 7 8 |
l
=
[
1
,
2
,
3
]
l_iter
=
l.__iter__()
from
collections
import
Iterable
from
collections
import
Iterator
print
(
isinstance
(l,Iterable))
#True #檢視是不是可迭代物件
print
(
isinstance
(l,Iterator))
#False #檢視是不是迭代器
print
(
isinstance
(l_iter,Iterator))
#True
print
(
isinstance
(l_iter,Iterable))
#True
|
通過上邊的我們可以確定.如果物件中有__iter__函式,那麼我們認為這個物件遵守了可迭代協議.就可以獲取到相應的迭代器
.這裡的__iter__是幫助我們獲取到物件的迭代器.我們使用迭代器中的__next__()來獲取到一個迭代器的元素,那麼我們之前所講的
for的工作原理到底是什麼? 繼續向下看:
1 2 3 4 5 6 7 8 9 10 |
s
=
"我愛北京天安⻔"
c
=
s.__iter__()
# 獲取迭代器
print
(c.__next__())
# 使⽤迭代器進⾏迭代. 獲取⼀個元素 我
print
(c.__next__())
# 愛
print
(c.__next__())
# 北
print
(c.__next__())
# 京
print
(c.__next__())
# 天
print
(c.__next__())
# 安
print
(c.__next__())
# ⻔
print
(c.__next__())
# StopIteration
|
for迴圈是不是也可以,並且還不報錯啊,其實上邊就是for的機制,
我們使用while迴圈和迭代器來模擬for迴圈: 必須要會
1 2 3 4 5 6 7 8 9 |
lst
=
[
6
,
5
,
4
]
l
=
lst.__iter__()
while
True
:
try
:
i
=
l.__next__()
print
(i)
except
StopIteration:
break
|
注意: 迭代器不能反覆,只能向下執行
總結:
Iterable: 可迭代物件. 內部包含__iter__()函式
Iterator: 迭代器. 內部包含__iter__() 同時包含__next__().
迭代器的特點:
1. 節省記憶體.
2. 惰性機制
3. 不能反覆, 只能向下執行.
我們可以把要迭代的內容當成子彈. 然後呢. 獲取到迭代器__iter__(), 就把子彈都裝在彈夾中. 然後發射就是__next__()把每一個子彈(元素)打出來. 也就是說, for迴圈的時候.一開始的 時候是__iter__()來獲取迭代器. 後面每次獲取元素都是通過__next__()來完成的. 當程式遇到 StopIteration將結束迴圈.