函數嵌套調用: 再調用一個函數的過程當中,又調用了其餘的函數python
函數的嵌套定義:在一個函數的內部,又定義另一個函數,函數內部定義的變量,在外部不能被調用mysql
名稱空間:一種隔離的概念,專門用來存放名字的地方,準確的說是存放名字與變量值綁定關係的地方,一個內存空間與另外一個內存空間徹底隔離web
python中,有哪些名稱空間,名稱空間和函數沒毛關係sql
內置名稱空間:python自帶的名字,在python解釋器啓動時產生,存放一些python內置的名字數據庫
全局名稱空間:再執行文件時,存放文件級別定義的名字,沒有縮進定義的名字,就是全局的名字小程序
局部名稱空間:在執行文件的過程當中,若是調用了函數,則會產生該函數的局部名稱空間,用來存放該函數內定義的名字緩存
該名字在函數調用時生效,在函數調用結束後失效。也就是函數內定義的名字。閉包
優先掌握app
加載順序: 內置---》全局---》局部函數
取值順序: 局部---》全局---》內置 (參照點爲局部) ,須要注意的是:在全局沒法查看局部的,在局部能夠查看全局的,
一、做用域即範圍
- 全局範圍(內置名稱空間與全局名稱空間屬於該範圍):全局存活,全局有效 globals()
- 局部範圍(局部名稱空間屬於該範圍):臨時存活,局部有效 locals()
二、做用域關係是在函數定義階段就已經固定的,與函數的調用位置無關,
global 在局部位置修改全局參數
nonlocal 只在局部生效,在局部位置修改上層參數
優先掌握: 做用域關係,在函數定義時就已經固定,與調用位置無關
再調用函數時,必須必須回到函數原來定義的位置去找做用域關係。
LEGB 表明名字查找順序: locals -> enclosing function -> globals -> __builtins__
locals 是函數內的名字空間,包括局部變量和形參
enclosing 外部嵌套函數的名字空間(閉包中常見)
globals 全局變量,函數定義所在模塊的名字空間
builtins 內置模塊的名字空間
閉包函數:閉合起來 , 包裹關係,內部函數,包含對外部做用域的一個引用
一、定義在函數內部的函數
二、包含對外部做用域名字的引用,而不是對全局做用域名字的引用,那麼該內部函數就稱爲閉包函數
1 #閉包函數 2 x=1 3 def f1(): 4 x=11111111111 5 def f2(): #定義在函數f1內部的函數 6 print(x) #調用外部做用域的名字x,此x並非全局下的x 7 return f2 #任何得地方均可以調用,打破只限於局部使用 8 9 func=f1() #func 拿到的是f2內存地址,不管func在哪運行,都以x=1111111111 這個值爲準 10 print(func) 11 12 x=100 13 func() 14 print(func()) #空值 15 #f2()稱爲閉包函數 #結論 <function f1.<locals>.f2 at 0x00000000067FFC80> 11111111111 11111111111 None
閉包函數有什麼用
閉包函數應用:延遲計算、惰性計算
閉包的意義:返回的函數對象,不只僅是一個函數對象,在該函數外還包裹了一層做用域,這使得,該函數不管在何處調用,優先使用本身外層包裹的做用域
應用領域:延遲計算(原來咱們是傳參,如今咱們是包起來)
1 #爬網頁小程序 2 import requests #導入request模塊 3 4 def get(url): #定義一個get函數,參數爲url 5 return requests.get(url).text #返回獲得網站內容的文本信息 6 print(get('http://www.baidu.com')) 7 8 #方法二: 9 def index(url): #定義頁面函數 10 def get(): #定義get函數 11 print(requests.get(url).text) #輸出獲得的網站內容文本信息 12 return get #返回get信息,即返回網址內容文本信息,使得任意位置能夠調用baidu_web = index('http://www.baidu.com') #調用index()函數 13 baidu_web() #執行函數 14 15 #反法三 16 17 def index(url): 18 x = 'xxxx' 19 y = 'yyyy' 20 def wrapper(): 21 x 22 y 23 return requests.get(url).text 24 return wrapper 25 baidu_web = index('http://www.baidu.com') 26 baidu = baidu_web.__closure__ #closure 是內部 閉合 函數 , 生成一個元組, 元組內容是 x y url 的值構成的元素 27 baidu0 = baidu_web.__closure__[0].cell_contents # 查看元組裏面的值 28 baidu1 = baidu_web.__closure__[1].cell_contents 29 baidu2 = baidu_web.__closure__[2].cell_contents 30 print(baidu) 31 print(type(baidu)) 32 print(baidu0) 33 print(type(baidu0)) 34 print(baidu1) 35 print(type(baidu1)) 36 print(baidu2) 37 print(type(baidu2)) 38 39 ##結果 40 (<cell at 0x0000000008DF0C48: str object at 0x0000000008A5C5D0>, <cell at 0x0000000008DF0E88: str object at 0x0000000006782D88>, <cell at 0x0000000008DF0B88: str object at 0x0000000006782F10>) 41 <class 'tuple'> 42 http://www.baidu.com 43 <class 'str'> 44 xxxx 45 <class 'str'> 46 yyyy 47 <class 'str'>
裝飾器 : (閉包函數的一種應用場景) 裝飾他人的工具,裝飾器目的是爲他人添加新功能
開放封閉原則:對擴展是開放的,對修改是封閉的
裝飾器自己能夠是任意可調用對象,被裝飾的對象自己也能夠是任意可調用對象
裝飾器所遵循的原則:
一、不修改被裝飾對象的源代碼
二、不修改被調用對象的調用方式
裝飾器的目的:
在循序1和2原則的前提下,爲其餘新功能函數添加
1 #裝飾器 2 #統計函數執行時間,函數執行爲源代碼,新增統計時間 3 4 #@裝飾器名A,必須寫在被裝飾對象B的正上方,而且是單獨一行,把正下方的函數名B當作參數傳給@後的函數名A,而後吧返回值在賦值給這個函數B 5 6 import time 7 8 def timmer(func): #裝飾器 定義一個timmer函數,參數爲 func 9 # func=index 10 def wrapper(): #定義wrapper函數 11 start=time.time() #開始時間 12 func() #運行函數 13 stop=time.time() #結束時間 14 print('run time is %s' %(stop-start)) #統計函數執行時間 15 return wrapper #返回函數內存地址 16 17 18 @timmer # 至關於 index=timmer(index) 19 def index(): # 源代碼 定義 index函數 並用 index()方式調用 20 time.sleep(3) 21 print('welcome to index') 22 @timmer # 至關於 home=timmer(home) 23 def home(): ## 源代碼 定義 home函數 並用 home()方式調用 24 time.sleep(2) 25 print('welcome to home page') 26 27 #index=timmer(index) 至關於上面的 @timmer 有其一便可 28 #home=timmer(home) 至關於上面的 @timmer 有其一便可 29 30 index() #調用函數 31 home() 32 33 ####結論 #### 34 welcome to index 35 run time is 3.000300168991089 36 welcome to home page 37 run time is 2.000199794769287
上面爲無參函數, def index(): def home(): ,若是是有參函數 def home(name):
就須要傳參,則
def wrapper(*args, **kwargs): # 以保障能夠容許任意場景使用
1 def timmer(func): #裝飾器 定義一個timmer函數,參數爲 func 2 # func=index 3 def wrapper(*agrs, **kwargs): #定義wrapper函數,添加參數, 4 start=time.time() #開始時間 5 func(*agrs, **kwargs) #運行函數 6 stop=time.time() #結束時間 7 print('run time is %s' %(stop-start)) #統計函數執行時間 8 return wrapper #返回函數內存地址
無參裝飾器
1 #無參裝飾器 2 #原函數爲 index() ,如今添加裝飾器,知足登陸認證,從文件db.txt中驗證帳號密碼,確認後進入頁面 from index 3 4 current_user={'user':None} # 創建記錄用戶名的字典 5 def auth(func): #定義認證函數,帶func參數 6 def wrapper(*args,**kwargs): #定義wrapper函數,可傳任意參數 7 if current_user['user']: #判斷用戶名 8 return func(*args,**kwargs) #返回認證函數 func 9 10 name=input('name: ').strip() #輸入用戶名 11 password=input('password: ').strip() #輸入密碼 12 13 with open('db.txt', encoding='utf-8') as f: #打開數據庫字典文件 14 user_dic = eval(f.read()) #eval用來執行f.read()讀取函數,並把讀取的內網返回給user_dic 15 if name in user_dic and password == user_dic[name]: #判斷帳號密碼若是都正確 16 res=func(*args,**kwargs) #執行func函數,並賦值給res 17 current_user['user']=name #將輸入的name傳給用戶字典 18 return res #返回res 內存地址 19 else: 20 print('user or password error') #用戶名和密碼有錯的話,提示有錯 21 return wrapper #返回wrapper函數內存地址 22 23 @auth #index=auth(index) index=wrapper #應用裝飾器 24 def index(): #定義index函數 25 print('from index') 26 index() #執行index函數
1 #有參裝飾器版本 2 #能夠經過多個認證方式認證 file MySQL 等等 3 4 current_user={'user':None} 5 def auth(auth_type='file'): 6 def deco(func): 7 def wrapper(*args, **kwargs): 8 if auth_type == 'file': 9 if current_user['user']: 10 return func(*args, **kwargs) 11 name = input('name: ').strip() 12 password = input('password: ').strip() 13 14 with open('db.txt', encoding='utf-8') as f: 15 user_dic = eval(f.read()) 16 if name in user_dic and password == user_dic[name]: 17 res = func(*args, **kwargs) 18 current_user['user'] = name 19 return res 20 else: 21 print('user or password error') 22 elif auth_type == 'mysql': 23 print('mysql') 24 25 elif auth_type == 'ldap': 26 print('ldap') 27 else: 28 print('not valid auth_type') 29 return wrapper 30 return deco 31 @auth(auth_type='mysql') #@deco #index=deco(index) 32 def index(): 33 print('from index') 34 @auth(auth_type='file') 35 def home(name): 36 print('welcome %s' %name) 37 index() #wrapper() 38 home('egon')
裝飾器補充內容: ‘’‘ ’‘’ 三引號
查看函數備註能夠經過 help 查看 : print(help(func))
1 #未加wraps 時 2 def wrapper(f): #定義修飾函數 3 def wrapper_function(*args, **kwargs): 4 """這個是修飾函數""" 5 return f(*args, **kwargs) 6 return wrapper_function 7 8 @wrapper 9 def wrapped(): #定義被修飾函數 10 """這個是被修飾的函數""" 11 print('wrapped') 12 13 print(wrapped.__doc__) # 輸出`這個是修飾函數` 14 print(wrapped.__name__) # 輸出`wrapper_function` 15 16 ####結果 17 這個是修飾函數 18 wrapper_function
1 # 添加 wraps 後 2 from functools import wraps 3 4 def wrapper(f): 5 @wraps(f) #在最內層的函數上面添加 6 def wrapper_function(*args, **kwargs): 7 """這個是修飾函數""" 8 return f(*args, **kwargs) 9 return wrapper_function 10 11 @wrapper 12 def wrapped(): 13 """這個是被修飾的函數 14 """ 15 print('wrapped') 16 17 print(wrapped.__doc__) # 輸出`這個是被修飾的函數` 18 print(wrapped.__name__) # 輸出`wrapped` 19 20 ####結果 21 這個是被修飾的函數 22 wrapped
一個函數頭頂上有多個裝飾器嗎? 能夠。
1 ##多個裝飾器 2 import time 3 from functools import wraps 4 5 current_user={'user':None} 6 7 def timmer(func): 8 @wraps(func) 9 def wrapper(*args,**kwargs): 10 start=time.time() 11 res=func(*args,**kwargs) 12 stop=time.time() 13 print('run time is %s' %(stop-start)) 14 return res 15 return wrapper 16 def auth(auth_type='file'): 17 def deco(func): 18 def wrapper(*args, **kwargs): 19 if auth_type == 'file': 20 if current_user['user']: 21 return func(*args, **kwargs) 22 name = input('name: ').strip() 23 password = input('password: ').strip() 24 25 with open('db.txt', encoding='utf-8') as f: 26 user_dic = eval(f.read()) 27 if name in user_dic and password == user_dic[name]: 28 res = func(*args, **kwargs) 29 current_user['user'] = name 30 return res 31 else: 32 print('user or password error') 33 elif auth_type == 'mysql': 34 print('mysql') 35 36 elif auth_type == 'ldap': 37 print('ldap') 38 else: 39 print('not valid auth_type') 40 return wrapper 41 return deco 42 43 # 多個裝飾器,那個在前先生效那個 44 # 直接修飾正下方的函數 45 @auth() # @deco #index=deco(index) #wrapper 46 @timmer #index=timmer(wrapper) 47 # @auth() # @deco #index=deco(index) #wrapper 48 def index(): 49 '''這是index函數''' 50 time.sleep(3) 51 print('welcome to index') 52 return 123 53 54 # print(index.__doc__) 55 # print(help(index)) 56 57 index() 58 59 60 #####結果 61 name: lalala 62 password: 123 63 welcome to index 64 run time is 3.0002999305725098
####練習題####
一:編寫函數,(函數執行的時間是隨機的)
二:編寫裝飾器,爲函數加上統計時間的功能
三:編寫裝飾器,爲函數加上認證的功能
四:編寫裝飾器,爲多個函數加上認證的功能(用戶的帳號密碼來源於文件),要求登陸成功一次,後續的函數都無需再輸入用戶名和密碼
注意:從文件中讀出字符串形式的字典,能夠用eval('{"name":"egon","password":"123"}')轉成字典格式
五:編寫裝飾器,爲多個函數加上認證功能,要求登陸成功一次,在超時時間內無需重複登陸,超過了超時時間,則必須從新登陸
六:編寫下載網頁內容的函數,要求功能是:用戶傳入一個url,函數返回下載頁面的結果
七:爲題目五編寫裝飾器,實現緩存網頁內容的功能:
具體:實現下載的頁面存放於文件中,若是文件內有值(文件大小不爲0),就優先從文件中讀取網頁內容,不然,就去下載,而後存到文件中
擴展功能:用戶能夠選擇緩存介質/緩存引擎,針對不一樣的url,緩存到不一樣的文件中
八:還記得咱們用函數對象的概念,製做一個函數字典的操做嗎,來來來,咱們有更高大上的作法,在文件開頭聲明一個空字典,而後在每一個函數前加上裝飾器,完成自動添加到字典的操做
九 編寫日誌裝飾器,實現功能如:一旦函數f1執行,則將消息2017-07-21 11:12:11 f1 run寫入到日誌文件中,日誌文件路徑能夠指定
注意:時間格式的獲取
import time
time.strftime('%Y-%m-%d %X')
######################################
迭代器 (Iterator):是一個重複的過程,每一次重複,都是基於上一次的結果而來
取值:就是一個循環的過程,
依賴於索引的方式迭代取值:字符串 列表 元組
如列表取值:
l=['a','b','c','d']
count=0
while count < len(l):
print(l[count])
count+=1
不依賴於索引的方式取值:字典取值(無索引,非序列類型) 該方式就是迭代器
可迭代對象 iterable: 凡是對象下有 __iter__ 方法: 對象.__iter__ , 該對象就是可迭代對象
# s='hello'
# l=['a','b','c','d']
# t=('a','b','c','d')
# dic={'name':'egon','sex':'m',"age":18}
# set1={1,2,3}
# f=open('db.txt')
# s.__iter__()
# l.__iter__()
# t.__iter__()
# dic.__iter__()
# set1.__iter__()
# f.__iter__()
執行iter的到的是迭代器對象,是一個內存地址
迭代器對象有next方法
使用next方法能夠去到字典的key鍵值
迭代器對象:
#1 有__iter__ , 執行獲得仍然是迭代自己,執行 iter以後纔會有 next
#2 有__next__ , 一次取一個值
# l=['a','b','c','d'] 列表
# i=iter(l) 迭代器
迭代器對象的優勢:
#1:提供了一種統一的,能夠不依賴於索引的迭代方式
#2:迭代器自己,比起其餘數據類型更省內存 (同一時間只有一個值)
迭代器對象的缺點:
#1:一次性,只能逐步日後取值,不能回退,不如索引取值靈活
#2:沒法預知何時取值結束,即沒法預知長度
文件是迭代器對象,既能夠__iter__ , 又能夠 __next__
1 #迭代器 2 l=['a','b','c','d'] #列表 3 dic={'name':'lalala','sex':'m',"age":28} #字典 4 iter_l=iter(l) #取值,取的是元素的內存地址 5 iter_dic=iter(dic) #取值,取得是字典key鍵的內存地址 6 while True: #創建循環 取值 7 try: #消除異常 8 # print(next(iter_l)) #挨個取列表的值 9 # print(next(iter_dic)) #取字典的key值 10 k=next(iter_dic) # 11 print(k,dic[k]) #取字典的key值和value值 12 except StopIteration: #若是出現此異常,則終止循環 13 break 14 15 #### 結果 16 name lalala 17 sex m 18 age 28
for循環原理
1 # for循環原理 實際上就是迭代器 for循環比while循壞更便捷 2 # 先調用__iter__() 方法 3 # 凡是能被for循環執行循環的,都是可迭代器對象 4 l = ['a', 'b' , 'c', 'd',] 5 for item in l : #iter_l = l.__iter__() #從l中逐個取值,賦值給item, 這裏的item能夠任意定義 6 print(item) 7 8 with open('a.txt') as f: 用with方式打開文件 9 # for line in f: #i=f.__iter__() #for循環文件內容,調用__iter__(),而後執行next,將結果返回給 line line = f.__iter__() 10 # print(line) 11 print(f is f.__iter__()) #常見的類型裏面,只有文件是迭代器對象,其他都是可迭代器對象,迭代器對象既要有 __iter__(),又要有 next()
生成器 (generator) : 只要函數內部包含有yield關鍵字,那麼函數名()的到的結果就是生成器,而且不會執行函數內部代碼
生成器就是迭代器,知足迭代器全部的特色,在next()函數纔會往下執行
1 #生成器 2 #生成器就是迭代器,所以能夠這麼取值 3 # res=next(g) 4 # print(res) 5 #yield的功能: 6 # 1 把函數的結果作生迭代器(以一種優雅的方式封裝好__iter__,__next__) 7 # 2 函數暫停與再繼續運行的狀態是由yield保存 8 9 def func(): 10 print('first') # 11 yield 11111111 #碰到 yield贊停,將後面的返回值返回 12 print('second') #再次執行的時候,從yield以後開始執行 13 yield 2222222 14 print('third') 15 yield 33333333 16 print('fourth') 17 18 g=func() #執行該函數 19 # print(g) 20 # next(g) 21 22 # from collections import Iterator 23 # print(isinstance(g,Iterator)) 24 25 # print(next(g)) #能夠經過next取值 26 # print('======>') 27 # print(next(g)) 28 # print('======>') 29 # print(next(g)) 30 # print('======>') 31 # print(next(g)) 32 33 for i in g: #i=iter(g) #能夠經過for循環取值 34 print(i) 35 36 37 ####結果 38 first 39 11111111 40 second 41 2222222 42 third 43 33333333 44 fourth
1 #用生成器實現range功能 2 # nu = range(1, 10, 2) range(start, stop, [step]) 3 # l1 = list(nu) 4 # t1 = tuple(nu) 5 # print(l1) 6 # print(t1) 7 ###結果 8 #[1, 3, 5, 7, 9] 9 #(1, 3, 5, 7, 9) 10 ########## 11 def range_fun(start, stop, step): 12 while start < stop : 13 yield start 14 start += step 15 nu = range_fun(1, 10, 2) 16 # print(nu) #<generator object range_fun at 0x0000000006B522B0> 17 # print(next(nu)) #1 18 # print(next(nu)) #3 19 20 for i in nu: 21 print(i) 22 23 #####結果 24 1 25 3 26 5 27 7 28 9
實現一個有無窮值的類型:
1 #實現無窮值函數,該函數的值同一時間在內存中只存在一個值,因此不會撐爆內存 2 def func(n): 3 print('=== start ===') 4 while True: #死循環 5 yield n #生成器,遇到yield 暫停,返回n,內存中同一時間只有這一個值 6 n+=1 7 8 g=func(0) #調用函數 9 10 # print(next(g)) 11 # print(next(g)) 12 # print(next(g)) 13 for i in g: #取值 14 print(i)
1 #生成器 實現一個無窮的序列 2 def my_range(start,stop): 3 while True: 4 if start == stop: 5 raise StopIteration #知足條件時,拋出異常。 raise 是本身拋出異常 StopIteration 6 yield start #2 返回 start的值 7 start+=1 #3 返回值+1 8 9 g=my_range(1,3) 10 # 11 # print(next(g)) 12 # print(next(g)) 13 # print(next(g)) 14 15 for i in my_range(5,10): #for循環遇到異常自動中止循環 16 print(i) 17 18 ###結果 19 5 20 6 21 7 22 8 23
#yield與return的比較?
#相同:都有返回值的功能
#不一樣:return只能返回一次值,而yield能夠返回屢次值,能夠掛起/保存函數的運行狀態
1 #生成器實現過濾功能 2 #實現管道符 # python3 tail.py -f access.log | grep 'error' 3 4 import time 5 6 def tail(filepath): 7 with open(filepath, 'r') as f: 8 f.seek(0, 2) 9 while True: 10 line = f.readline() 11 if line: 12 yield line 13 else: 14 time.sleep(0.2) 15 16 17 def grep(pattern,lines): #定義管道函數,參數patteron表示 | 18 for line in lines: 19 if pattern in line: 20 print(line,end='') 21 22 grep('error',tail('access.log')) #讀取文件 access.log , 過濾 error , 只顯示 error相關