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