python avg_python闭包
本文分為如下幾個部分
- 什么是閉包
- 閉包與裝飾器
- 閉包等價——偏函數
- 閉包等價——類
- 閉包等價——其他
- 閉包用于捕獲狀態值
- 閉包等價——協程
- 三種方法實現動態均值
什么是閉包
閉包是攜帶著一些自由變量的函數。
我們直接來看一個例子
def fun_out(a):def fun_in(b):return a + breturn fun_in fun1 = fun_out(1)其中fun1函數是一個閉包,它攜帶著fun_out中定義的a變量,值為1。運行程序的效果是這樣的
>>> fun1 = fun_out(1) >>> fun1(3) 4 >>> fun1(6) 7>>> fun5 = fun_out(5) >>> fun5(3) 8 >>> fun5(6) 11fun1和fun5兩個函數的定義相同,只是攜帶的自由變量不同,便成為了兩個函數。由此閉包可以作為函數工廠,生產出功能類似,但是會有細微差別的函數。
閉包用起來非常直觀,它反映了函數內調用一個變量的搜索路徑,fun_in中調用某變量會
- 先在fun_in函數定義的局部空間中開始搜索,可以找到b
- 如果在局部空間中找不到,則在更上層的局部空間中找,可找到a
- 之后在全局空間中搜索,即函數外定義的變量
- 如果還是找不到會去找python內置變量
- 如果還是找不到則拋出異常
閉包的特點就是,如果單獨看fun_in函數的定義,不看周圍環境,則a是未被定義的;而用這種方式返回則可以將a的值內置到函數中,這就是fun_in與fun1的區別。
我們可以查看閉包中綁定的自由變量
def fun_out(a):def fun_in(b):return a + breturn fun_in>>> fun1 = fun_out(1) >>> fun1.__closure__ (<cell at 0x000001BC7FD3D768: int object at 0x0000000072C96C40>,)另外,有兩點需要注意。第一,下面這個fun_in不是閉包,因為其中沒有調用外面的a值,調用__closure__會為None
def fun_out(a):def fun_in(b):return 1 + breturn fun_in第二,下面這種情況a和c都會內置進fun_in中,其中c內置的值是3
def fun_out(a):c = 1def fun_in(b):return a + b + cc = 3return fun_in第三,注意區分下面這種情況
def fun_out(a):return fun_in()def fun_in():return afun_out(1)如果fun_in不是定義在fun_out里面,則fun_in在尋找變量a時,不會找fun_out的局部空間,而是直接到全局去找。如果代碼只是像上面這樣定義,則調用fun_out會報錯,因為fun_in找不到a這個變量;除非在全局定義一個a才能找到。
閉包與裝飾器
裝飾器是閉包的一個應用,只是攜帶的自由變量是一個函數
def print1(func):def wrapper(*args, **kw):print(1)return func(*args, **kw)return wrapper@print1 def print2():print(2)>>> print2() 1 2@print1命令等價于print2 = print1(print2),所以裝飾器是閉包的一種應用,自由變量是一個函數,傳入和輸出使用了相同的變量名。使用@print1的好處是使編程思路更加直觀,定義好裝飾器后,如果想對這個函數添加這個功能,就裝飾上去,而不用重新思考print2傳入print1是如何調用的、返回的是什么。
閉包等價——偏函數
還是以這個閉包為例
def fun_out(a):def fun_in(b):return a + breturn fun_in fun1 = fun_out(1)下面使用偏函數實現
from functools import partial def fun(a, b):return a + b>>> fun1 = partial(fun, b=1) >>> fun1(3) 4 >>> fun5 = partial(fun, b=5) >>> fun5(3) 8偏函數即固定某些參數的取值,達到函數工廠的作用。
但是這種方法不夠靈活,比如下面一種情況
def fun_out(a):c = a ** 2def fun_in(b):return c + breturn fun_in fun2 = fun_out(2)之后我們要反復使用fun2這個函數, c只有在定義fun2時計算過,不會在之后的步驟中重復計算,而使用偏函數就無法達到這種效果。這種效果下面的類也是可以達到的。
閉包等價——類
還是上面的例子,這里定義一個類來實現
class Add:def __init__(self, b):self.b = bdef __call__(self, a):return a + self.b>>> add1 = Add(1) >>> add1(3) 4 >>> add5 = Add(5) >>> add5(3) 8類相比于普通函數的一個好處是,類可以將一些變量內置進去,讓類中定義的函數隨意調用和修改。類相比于閉包的好處在于,函數運行結束后,可以隨意調用修改屬性(自由變量);比如一個遞歸函數,想查看這個函數被調用了幾次。
閉包等價——其他
1.如果只是使用普通的函數,傳入兩個參數也可以達到類似的效果
def fun(a, b):return a + b fun(3, b=1)但是調用時太麻煩了,應該不算實現了閉包的功能。
2.使用lambda表達式
def fun(a, b):return a + b>>> fun1 = lambda a: fun(a, 1) >>> fun1(3) 4 >>> fun5 = lambda a: fun(a, 5) >>> fun5(3) 8相比于partial來說,lambda表達式會略顯臃腫。
閉包用于捕獲狀態值
除了函數工廠,閉包還可以用于將函數與它的狀態綁定,方便輸出函數的狀態和函數運行結果,比如輸出函數被調用的次數
def fun_out():count = 0def fun_in(something):nonlocal countcount += 1print(count, something)return fun_in>>> fun1 = fun_out() >>> fun1('first') 1 first >>> fun1('second') 2 second這種應用可以改寫成類的形式但是不能用partial等來改寫。不過還有另一種改寫方式——協程。
閉包等價——協程
def fun():count = 0while True:something = yieldcount += 1print(count, something)>>> fun1 = fun() >>> next(fun1) >>> fun1.send('first') 1 first >>> fun1.send('second') 2 second對yield不了解的讀者可以參考這篇文章
三種方法實現動態均值
我們要實現一個函數達到下面的效果
def running_avg(number):passrunning_avg(10) # 10 --> 10 / 1 running_avg(20) # 15 --> (10 + 20) / 2 running_avg(30) # 20 --> (10 + 20 + 30) / 31.類實現
class Running_avg:def __init__(self):self.total = 0self.count = 0def __call__(self, number):self.total += numberself.count += 1return self.total / self.count>>> running_avg = Running_avg() >>> running_avg(10) 10.0 >>> running_avg(20) 15.0 >>> running_avg(30) 20.02.閉包實現
def initial():total = 0count = 0def func(number):nonlocal totalnonlocal counttotal += numbercount += 1return total / countreturn func>>> running_avg = initial() >>> running_avg(10) 10.0 >>> running_avg(20) 15.0 >>> running_avg(30) 20.03.協程實現
def Running_avg():total = 0count = 0average = Nonewhile True:number = yield averagecount += 1total += numberaverage = total / count>>> running_avg = Running_avg() >>> next(running_avg) >>> running_avg.send(10) 10.0 >>> running_avg.send(20) 15.0 >>> running_avg.send(30) 20.0參考資料
下面是本文的參考資料
- 這篇文章對什么是閉包解釋的非常清楚
- 閉包的應用和替代方案
- 閉包捕獲狀態值
- 動態均值參考fluent python一書第16章
專欄信息
專欄主頁:python編程
專欄目錄:目錄
版本說明:軟件及包版本說明
總結
以上是生活随笔為你收集整理的python avg_python闭包的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win8装bios怎么设置u盘启动不了
- 下一篇: python多线程操作字典_在Pytho