函數名的使用以及第一類對象
函數名的運用
函數名是一個變量, 但它是一個特殊的變量, 與括號配合可以執行函數的變量
1.函數名的內存地址
def func():
print("呵呵")
print(func)
結果: <function func at 0x1101e4ea0>
2. 函數名可以賦值給其他變量
def func():
print("呵呵")
print(func)
a = func # 把函數當成一個變量賦值給另一個變量
a() # 函數調用 func()
3. 函數名可以當做容器類的元素
def func1():
print("呵呵")
def func2():
print("呵呵")
def func3():
print("呵呵")
def func4():
print("呵呵")
lst = [func1, func2, func3]
for i in lst:
i()
4. 函數名可以當做函數的參數
def func():
print("吃了么")
def func2(fn):
print("我是func2")
fn() # 執行傳遞過來的fn
print("我是func2")
func2(func) # 把函數func當成參數傳遞給func2的參數fn.
5. 函數名可以作為函數的返回值
def func_1():
print("這⾥里里是函數1")
def func_2():
print("這⾥里里是函數2")
print("這⾥里里是函數1")
return func_2
fn = func_1()
# 執行函數1. 函數1返回的是函數2, 這時fn指向的就是上⾯面函數2
fn() # 執行上面返回的函
閉包
什么是閉包? 閉包就是內層函數, 對外層函數(非全局)的變量的引用. 叫閉包
def func1():
name = "alex"
def func2():
print(name)
# 閉包
func2()
func1()
# 結果: alex
我們可以使用__closure__來檢測函數是否是閉包. 使用函數名.__closure__返回cell就是
閉包. 返回None就不是閉包
def func1():
name = "alex"
def func2():
print(name)
# 閉包
func2()
print(func2.__closure__)
func1()
結果:
alex
(<cell at 0x0000020077EFC378: str object at 0x00000200674DC340>,)
返回的結果不是None就是閉包
現在有個問題,這個閉包只能在里邊調用啊,外邊的怎么調用呢?
def outer():
name = "alex"
# 內部函數
def inner():
print(name)
return inner
fn = outer() # 訪問外部函數, 獲取到內部函數的函數地址
fn() # 訪問內部函數
這樣就實現了外部訪問,那如果多層嵌套呢?很簡單,只需要一層一層的往外層返回就行了
def func1():
def func2():
def func3():
print("嘿嘿")
return func3
return func2
func1()()()
由它我們可以引出閉包的好處. 由於我們在外界可以訪問內部函數. 那這個時候內部函數訪問的時間和時機就不一定了, 因為在外部, 我可以選擇在任意的時間去訪問內部函數. 這 個時候. 想一想. 我們之前說過, 如果一個函數執行完畢. 則這個函數中的變量以及局部命名空間中的內容都將會被銷毀. 在閉包中. 如果變量被銷毀了. 那內部函數將不能正常執行. 所 以. python規定. 如果你在內部函數中訪問了外層函數中的變量. 那么這個變量將不會消亡. 將會常駐在內存中. 也就是說. 使用閉包, 可以保證外層函數中的變量在內存中常駐. 這樣做 有什么好處呢? 非常大的好處. 我們來看看下邊的代碼
def func():
name = 'alex'
def foo():
print(name)
return foo
msg = func()
msg() #這樣的話就是將name='alex'存放在一個常駐的內存中,並且外界不能修改
閉包的作用就是讓一個變量能夠常駐內存,供后面的程序使用
迭代器
我們之前一直在用可迭代對象進行操作,那么到底什么是可迭代對象.我們現在就來討論討論可迭代對象.首先我們先回顧下我們
熟知的可迭代對象有哪些:
str list tuple dic set 那為什么我們稱他們為可迭代對象呢?因為他們都遵循了可迭代協議,那什么又是可迭代協議呢.首先我們先看一段錯誤的代碼:
對的
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函數來查看類中定義好的
所有方法
a = 'abc' print(dir(a)) # dir查看對象的方法和函數 # 在打印結果中尋找__iter__ 如果存在就表示當前的這個類型是個可迭代對象
我們剛剛測了字符串中是存在__iter__的,那我們來看看 列表,元祖,字典.集合中是不是有存在__iter__
# 列表
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
print(dir(range))
這是查看一個對象是否是可迭代對象的第一種方法,我們還可以通過isinstence()函數來查看一個對象是什么類型的
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的工作原理到底是什么? 繼續向下看:
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循環: 必須要會
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將結束循環.
