大家好,我是安果!
本文以內(nèi)部函數(shù)為主線,深入講解內(nèi)部函數(shù)和閉包的應(yīng)用場景和原理,學(xué)會后你的 Python 水平會再上一個臺階,對工作面試或?qū)崙?zhàn)應(yīng)用都會很有幫助
本文包括:
Python 是面向?qū)ο蟮木幊陶Z言,對象是 Python 的一等公民,我們常用的字符串str,整數(shù) int,和其他變量都是對象
函數(shù)也是對象,所以也是一等公民,這就意味著它和變量一樣
def say_hello():
print('hello')
print(say_hello)
def say_something(some_func):
for _ in range(3):
some_func()
say_something(say_hello)
執(zhí)行結(jié)果:
<function say_hello at 0x7ff3d35b9160>
hello
hello
hello
把函數(shù)的內(nèi)部定義函數(shù),就是內(nèi)部函數(shù)(有點像廢話,但就那么個意思)
def outter():
print('我是外部函數(shù)')
def inner():
print('我是outter的內(nèi)部函數(shù)')
print('調(diào)用內(nèi)部函數(shù)')
inner()
print('我再次調(diào)用內(nèi)部函數(shù),自己家的想用就用,隨時用')
inner()
print('還可以返回給大家共用')
return inner
#調(diào)用外部函數(shù),并接受返回值
func = outter()
#調(diào)用outter返回的內(nèi)部函數(shù)
print('在外部調(diào)用內(nèi)部函數(shù)')
func()
注意: 調(diào)用的時候加小括號inner()
,作為參數(shù)或者返回值的時候不加小括號inner
,是引用這個函數(shù)對象
執(zhí)行結(jié)果:
我是外部函數(shù)
調(diào)用內(nèi)部函數(shù)
我是outter的內(nèi)部函數(shù)
我再次調(diào)用內(nèi)部函數(shù),自己家的想用就用,隨時用
我是outter的內(nèi)部函數(shù)
還可以返回給大家共用
在外部調(diào)用內(nèi)部函數(shù)
我是outter的內(nèi)部函數(shù)
如果內(nèi)部函數(shù)只是把函數(shù)定義在函數(shù)的內(nèi)部,那就沒有多大意思了,它還有一個很大的特點,正因為這個特點,它才被稱為:閉包 clsure
學(xué)過 JavaScript 的非小白同學(xué)可能會對這個概念很熟悉
內(nèi)部函數(shù)還有一個很重要的特性:
所以說 Python 中的閉包就是內(nèi)部函數(shù),準確點說,它是使用了 nonlocal 變量的內(nèi)部函數(shù)
import random
def create_room():
room_no = random.randint(1, 100)
print(f'我創(chuàng)建了房間號:{room_no}')
def toilet():
print(f'我是{room_no}的內(nèi)部廁所')
print('上廁所')
toilet()
print('我再次上廁所,自己家的想用就用,隨時用')
toilet()
print('還可以共享給大家共用')
return toilet
#調(diào)用外部函數(shù),并接受返回值
toilet = create_room()
#調(diào)用outter返回的內(nèi)部函數(shù)
print('在外部使用內(nèi)部廁所')
toilet()
print('在外部再次使用內(nèi)部廁所')
toilet()
運行結(jié)果:
我創(chuàng)建了房間號:52
上廁所
我是52的內(nèi)部廁所
我再次上廁所,自己家的想用就用,隨時用
我是52的內(nèi)部廁所
還可以共享給大家共用
在外部使用內(nèi)部廁所
我是52的內(nèi)部廁所
在外部再次使用內(nèi)部廁所
我是52的內(nèi)部廁所
說的這么玄乎,其實就是內(nèi)部函數(shù)使用了外部函數(shù)的局部變量,所以局部變量被內(nèi)部函數(shù)給封存了,也就不會釋放了
內(nèi)部函數(shù)也可以改寫外部函數(shù)的變量值,但需要使用 nonlocal 關(guān)鍵詞聲明這是外部的變量。
回憶一下:函數(shù)內(nèi)部修改全局變量,需要使用 global 關(guān)鍵詞
import random
def create_room():
room_no = random.randint(1, 100)
print(f'我創(chuàng)建了房間號:{room_no}')
def toilet():
nonlocal room_no
room_no = random.randint(1, 100)
print(f'我是{room_no}的內(nèi)部廁所')
print('上廁所')
toilet()
print(f'房間號:{room_no}')
print('我再次上廁所,自己家的想用就用,隨時用')
toilet()
print(f'房間號:{room_no}')
print('還可以共享給大家共用')
return toilet
#調(diào)用外部函數(shù),并接受返回值
toilet = create_room()
#調(diào)用outter返回的內(nèi)部函數(shù)
print('在外部使用內(nèi)部廁所')
toilet()
print('在外部再次使用內(nèi)部廁所')
toilet()
使用 nonlocal 在內(nèi)部函數(shù)改變外部變量 room_no 的值,所以每次上廁所,都會改變房間號:
我創(chuàng)建了房間號:39
上廁所
我是43的內(nèi)部廁所
房間號:43
我再次上廁所,自己家的想用就用,隨時用
我是66的內(nèi)部廁所
房間號:66
還可以共享給大家共用
在外部使用內(nèi)部廁所
我是52的內(nèi)部廁所
在外部再次使用內(nèi)部廁所
我是29的內(nèi)部廁所
其實內(nèi)部函數(shù)就這么點東西了(后面再說它的實現(xiàn)原理),現(xiàn)在來看到底有什么實實在在的用處。
下面來說 3 個應(yīng)用場景:
寫在內(nèi)部是因為只有在內(nèi)部才有用,外部根本不需要,也不想讓他們使用,就像上面的內(nèi)部廁所的例子,實際上是不可能在外面使用的,這種場景叫做 封裝
import random
def create_room():
room_no = random.randint(1, 100)
print(f'我創(chuàng)建了房間號:{room_no}')
def toilet():
print(f'歡迎進入{room_no}的VIP廁所')
print('沖水')
print('請君入廁')
print('洗手')
print('熱毛巾')
print('歡迎下次光臨')
print('上廁所')
toilet()
print('上廁所')
toilet()
print('上廁所')
toilet()
#調(diào)用外部函數(shù),并接受返回值
create_room()
再總結(jié)一下:
內(nèi)部函數(shù)可以方便的生成新的函數(shù),看這個例子:
def team_maker(type, level, temperature):
'''
type: 品種,如綠茶,紅茶
level: 等級,特級,一級,二級
temper: 溫度
'''
def tea(water):
print('正在沏茶中')
print(f'{type},{level}, {temperature}')
print(f'{water}毫升給您沖好了')
return tea
#創(chuàng)建符合我口味的沏茶函數(shù)
mytea = team_maker('綠茶', '特級', '66.6')
#創(chuàng)建符合她口味的沏茶函數(shù)
herteam = team_maker('紅茶', '一級', '88.8')
print('我們來一杯')
mytea(500)
herteam(300)
print('多喝點')
mytea(800)
herteam(600)
print('完了,我喝醉了...,因為有她')
運行結(jié)果:
我們來一杯
正在沏茶中
綠茶,特級, 66.6
500毫升給您沖好了
正在沏茶中
紅茶,一級, 88.8
300毫升給您沖好了
多喝點
正在沏茶中
綠茶,特級, 66.6
800毫升給您沖好了
正在沏茶中
紅茶,一級, 88.8
600毫升給您沖好了
完了,我喝醉了...
裝飾器,對 Python 至關(guān)重要,它也是內(nèi)部函數(shù)的主要使用場景
閉包攜帶了外部函數(shù)的變量,所以可以訪問這些變量,而這些變量也不會被釋放
具體是怎么實現(xiàn)的呢?答案就 1 個字:__closure__屬性
Python 給內(nèi)部函數(shù)添加了這個屬性來攜帶內(nèi)部函數(shù)用到的外部函數(shù)中的變量
import random
def create_room():
room_no = random.randint(1, 100)
print(f'我創(chuàng)建了房間號:{room_no}')
def toilet():
print(f'我是{room_no}的內(nèi)部廁所')
return toilet
print('調(diào)用外部函數(shù)')
toilet = create_room()
print('打印一下toilet函數(shù)的變量,其中有一個是__closure__')
print(dir(toilet))
print('__closure__是一個包含它攜帶的變量的元組')
print(toilet.__closure__)
print('__closure__元組里是cell,通過cell_contents可以訪問所攜帶的變量值')
print(toilet.__closure__[0].cell_contents)
執(zhí)行結(jié)果:
import random
def create_room():
room_no = random.randint(1, 100)
print(f'我創(chuàng)建了房間號:{room_no}')
def toilet():
print(f'我是{room_no}的內(nèi)部廁所')
return toilet
print('調(diào)用外部函數(shù)')
toilet = create_room()
print('打印一下toilet函數(shù)的變量,其中有一個是__closure__')
print(dir(toilet))
print('__closure__是一個包含它攜帶的變量的元組')
print(toilet.__closure__)
print('__closure__元組里是cell,通過cell_contents可以訪問所攜帶的變量值')
print(toilet.__closure__[0].cell_contents)
你動動手,就是對我最大的鼓勵,本文內(nèi)容干貨,首先建議收藏
如果對你有幫助,請 點贊,在看,轉(zhuǎn)發(fā)。謝謝!