函数、迭代器、生成器、装饰器
函數(shù)
函數(shù)基礎(chǔ)
一、為什么要使用函數(shù)
把重復(fù)的代碼提取出來,節(jié)省代碼量
?
二、函數(shù)分類
內(nèi)置函數(shù),如len(),sun()
自定義函數(shù)
?
三、如何定義函數(shù)
def 函數(shù)名(參數(shù)1,參數(shù)2,參數(shù)3,...):'''注釋'''函數(shù)體return 返回的值?
四、函數(shù)使用的原則:先定義,后使用
?
五、調(diào)用函數(shù)
1、如何調(diào)用函數(shù):函數(shù)名加括號
2、函數(shù)的返回值
函數(shù)的返回值通常都是一個,如果出現(xiàn) return a,b 這種情況,其實(shí)是表示一個元組
?
?六、函數(shù)的參數(shù)
#1、位置參數(shù):按照從左到右的順序定義的參數(shù)
位置形參:必選參數(shù)
位置實(shí)參:按照位置給形參傳值,順序必須一一對應(yīng)
?
#2、關(guān)鍵字參數(shù):按照key=value的形式定義的實(shí)參
無需按照位置為形參傳值
注意的問題:
1. 關(guān)鍵字實(shí)參必須在位置實(shí)參右面
2. 對同一個形參不能重復(fù)傳值
?
#3、默認(rèn)參數(shù):形參在定義時就已經(jīng)為其賦值
可以傳值也可以不傳值,經(jīng)常需要變得參數(shù)定義成位置形參,變化較小的參數(shù)定義成默認(rèn)參數(shù)(形參)
注意的問題:
1. 只在定義時賦值一次
2. 默認(rèn)參數(shù)的定義應(yīng)該在位置形參右面
3. 默認(rèn)參數(shù)通常應(yīng)該定義成不可變類型,舉例
def add_end(L=[]):print(id(L))L.append('END')return Lprint(add_end()) print(add_end())結(jié)果:
2822355733768
['END']
2822355733768
['END', 'END']
?
解釋:python函數(shù)在定義好時,形參、內(nèi)部參數(shù)等變量就已經(jīng)有了固定的內(nèi)存空間(變量始終指向那一塊內(nèi)存,不會更改)。默認(rèn)參數(shù)形參變量如果是可變的,下一次調(diào)用該函數(shù)時,默認(rèn)參數(shù)就已經(jīng)發(fā)生了變化。
?
#4、可變長參數(shù): 可變長指的是實(shí)參值的個數(shù)不固定
而實(shí)參有按位置和按關(guān)鍵字兩種形式定義,針對這兩種形式的可變長,形參對應(yīng)有兩種解決方案來完整地存放它們,分別是*args,**kwargs
===========*args===========
def foo(x,y,*args):print(x,y)
print(args)
foo(1,2,3,4,5)
def foo(x,y,*args):
print(x,y)
print(args)
foo(1,2,*[3,4,5])
def foo(x,y,z):
print(x,y,z)
foo(*[1,2,3])
當(dāng)形參為*args,實(shí)參可以用 *()或者 *[] 的方式將若干個參數(shù)打包發(fā)送給形參
===========**kwargs===========
def foo(x,y,**kwargs):print(x,y)
print(kwargs)
foo(1,y=2,a=1,b=2,c=3)
def foo(x,y,**kwargs):
print(x,y)
print(kwargs)
foo(1,y=2,**{'a':1,'b':2,'c':3})
def foo(x,y,z):
print(x,y,z)
foo(**{'z':1,'x':2,'y':3})
當(dāng)形參為**kwargs時,實(shí)參可以通過**{}的方式將若干參數(shù)打包發(fā)送給形參
?
===========*args+**kwargs===========
def foo(x,y):print(x,y)
def wrapper(*args,**kwargs):
print('====>')
foo(*args,**kwargs)
?
#5、命名關(guān)鍵字參數(shù):*后定義的參數(shù),必須被傳值(有默認(rèn)值的除外),且必須按照關(guān)鍵字實(shí)參的形式傳遞,可以保證,傳入的參數(shù)中一定包含某些關(guān)鍵字
和關(guān)鍵字參數(shù)**kw不同,命名關(guān)鍵字參數(shù)需要一個特殊分隔符*,*后面的參數(shù)被視為命名關(guān)鍵字參數(shù)(如果參數(shù)中已經(jīng)有了可變參數(shù)*args,則不需要 * 來特別標(biāo)明)
def foo(x,y,*args,a=1,b,**kwargs):print(x,y)print(args)print(a)print(b)print(kwargs) foo(1,2,3,4,5,b=3,c=4,d=5)結(jié)果: 1 2 (3, 4, 5) 1 3 {'c': 4, 'd': 5}
?
閉包
一、函數(shù)是對象
函數(shù)可以當(dāng)做參數(shù)傳遞,也可以返回一個函數(shù)
利用該特性,可以寫成一個類似C語言switch case語法的功能,取代多分支if
def foo():print('foo')def bar():print('bar')dic={'foo':foo,'bar':bar, } while True:choice=input('>>: ').strip()if choice in dic:dic[choice]()?
二、函數(shù)嵌套、名稱空間、作用域的概念
1、函數(shù)嵌套調(diào)用
函數(shù)嵌套定義
?
2、名稱空間
存放名字的地方,x=1,1存3、放于內(nèi)存中,那名字x存放在哪里呢?名稱空間是存放名字x與1綁定關(guān)系的地方,L = [1,2,3],L存放在名稱空間里,真正的列表在其他地方。
3、作用域?
#1、作用域即范圍
-全局范圍(內(nèi)置名稱空間與全局名稱空間屬于該范圍):全局存活,全局有效
-局部范圍(局部名稱空間屬于該范圍):臨時存活,局部有效
內(nèi)部的函數(shù)可以訪問外部的變量,但是外面的函數(shù)訪問不了內(nèi)部函數(shù)
?
#2、作用域關(guān)系是在函數(shù)定義階段就已經(jīng)固定的,與函數(shù)的調(diào)用位置無關(guān)
?
#3、查看作用域:globals(),locals()
python中g(shù)lobals和nonlocals的用法
?
LEGB 代表名字查找順序: locals -> enclosing function -> globals -> __builtins__
locals 是函數(shù)內(nèi)的名字空間,包括局部變量和形參
enclosing 外部嵌套函數(shù)的名字空間(閉包中常見)
globals 全局變量,函數(shù)定義所在模塊的名字空間
builtins 內(nèi)置模塊的名字空間
?
閉包函數(shù)
內(nèi)部函數(shù)包含對外部函數(shù)而非全局作用域的引用
def counter():n = 0def incr():nonlocal nx = nn += 1return xreturn incrc = counter() print(c()) print(c()) print(c()) print(c.__closure__[0].cell_contents) # 查看閉包的元素?
結(jié)果:
0
1
2
3
?
閉包函數(shù)的意義:
返回的函數(shù)對象,不僅僅是一個函數(shù),在該函數(shù)外還包裹了一個外層作用域(一般含是變量),這使得,該函數(shù)無論在何處調(diào)用,都優(yōu)先使用自己外層包裹的作用域,相對于更外層的比如全局作用域。
實(shí)現(xiàn)延遲計算的功能:
普通函數(shù)調(diào)用后立即函數(shù)執(zhí)行,閉包則是給函數(shù)包裹上了一層外層作用域,傳了參數(shù)的功能。
?
裝飾器
1、裝飾器是閉包函數(shù)的一種應(yīng)用
2、裝飾器可以在不修改原函數(shù)功能的前提下,添加額外功能。開放封閉原則
?
#不帶參數(shù)的裝飾器
import time def timmer(func):def wrapper(*args,**kwargs):start_time=time.time()res=func(*args,**kwargs)stop_time=time.time()print('run time is %s' %(stop_time-start_time))return resreturn wrapper@timmer #相當(dāng)于foo = timmer(foo) def foo():time.sleep(3)print('from foo') foo()#利用閉包的特點(diǎn):1、具有延遲計算的功能,函數(shù)不是立即執(zhí)行;2、閉包相當(dāng)于包裹了一個外層作用域的函數(shù)。
當(dāng)timmer函數(shù)執(zhí)行后,形參 func,原函數(shù)作為一個對象傳到了形參,傳給了內(nèi)層函數(shù) wrapper,即使timmer執(zhí)行完畢了,形參會一直存在著。
?
#帶參數(shù)的裝飾器
def auth(driver='file'):def auth2(func):def wrapper(*args,**kwargs):name=input("user: ")pwd=input("pwd: ")if driver == 'file':if name == 'egon' and pwd == '123':print('login successful')res=func(*args,**kwargs) #原函數(shù)其實(shí)就一個調(diào)用,其他都是額外功能,裝飾作用return reselif driver == 'ldap':print('ldap')return wrapperreturn auth2@auth(driver='file') def foo(name):print(name)foo('egon')?
迭代器
1、為什么要有迭代器:
有序序列,如列表,字符串可以通過索引的方式取出其中的元素,而對于字典,文件,集合等類型,是沒用索引的,所以需要通過不依靠索引的迭代方式,就是迭代器。
2、什么是可迭代對象:
可迭代對象指的是實(shí)現(xiàn)了__iter__方法的對象
3、什么是迭代器(對象):
可迭代對象執(zhí)行obj.__iter__()得到的結(jié)果就是迭代器對象,同時,迭代器內(nèi)部還有__next__方法
文件類型是可迭代器對象 open('a.txt').__iter__() open('a.txt').__next__()注意:
迭代器對象一定是可迭代對象,而可迭代對象不一定是迭代器對象
?
迭代器對象的使用
dic = {'a':1, 'b':2, 'c':3}
iter_dic = dic.__iter__()? ?#得到一個迭代器
注意:迭代器.__iter__() 得到仍然是迭代器本身
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #拋出異常StopIteration,或者說結(jié)束標(biāo)志
iter_dic=dic.__iter__() while 1:try:k=next(iter_dic)print(dic[k])except StopIteration:break通過next方法從迭代器取數(shù)據(jù),需要手動捕獲異常
?
?
for循環(huán)執(zhí)行next方法,并且自動捕獲異常
dic={'a':1,'b':2,'c':3} for k in dic:print(dic[k])for循環(huán)的工作原理 1:執(zhí)行in后對象的dic.__iter__()方法,得到一個迭代器對象iter_dic 2: 執(zhí)行next(iter_dic),將得到的值賦值給k,然后執(zhí)行循環(huán)體代碼 3: 重復(fù)過程2,直到捕捉到異常StopIteration,結(jié)束循環(huán)?
生成器
概念:
只要函數(shù)內(nèi)部包含有yield關(guān)鍵字,那么函數(shù)名()的到的結(jié)果就是生成器,加括號不會執(zhí)行函數(shù)內(nèi)部的代碼
注意:生成器是一種特殊的迭代器,生成器內(nèi)置__next__,__iter__方法
?
send方法的使用:
def range2(n):count =0while count < n:print('count',count)count += 1sign = yield countprint('-------sign',sign)return 333new_range = range2(3) n1 = next(new_range)print('do sth else') new_range.send('stop!')?
?
send與next的區(qū)別:
兩者都可以喚醒生成器,來繼續(xù)執(zhí)行
send的作用:外界可以發(fā)送一個信號給生成器內(nèi)部,表達(dá)式?sign = yield count,?而next是默認(rèn)發(fā)送為None的
只使用send的情況:
#yield關(guān)鍵字的另外一種使用形式:表達(dá)式形式的yield def eater(name):print('%s 準(zhǔn)備開始吃飯啦' %name)food_list=[]while True:food=yield food_listprint('%s 吃了 %s' % (name,food))food_list.append(food)g=eater('egon') g.send(None) #對于表達(dá)式形式的yield,在使用時,第一次必須傳None,g.send(None)等同于next(g) g.send('蒸羊羔') g.send('蒸鹿茸') g.send('蒸熊掌') g.send('燒素鴨') g.close() g.send('燒素鵝') g.send('燒鹿尾')?
轉(zhuǎn)載于:https://www.cnblogs.com/liyuexi/p/10702873.html
總結(jié)
以上是生活随笔為你收集整理的函数、迭代器、生成器、装饰器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: freemaker中小数展示为整数的问题
- 下一篇: ngnix高并发的原理实现(转)