python3-基礎6

函數嵌套調用: 再調用一個函數的過程當中,又調用了其餘的函數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相關
相關文章
相關標籤/搜索