1.定義:本質是函數,裝飾其餘函數就是爲其餘函數添加附加功能。html
2.原則:a.不能修改被裝飾的函數的源代碼 b.不能修改被裝飾的函數的調用方式。 python
實例1:裝飾器的使用mysql
1 #Author:http://www.cnblogs.com/Jame-mei 2 #裝飾器的使用 3 import time 4 5 6 def timmer(func): 7 def warpper(*args,**kwargs): 8 start_time=time.time() 9 func() 10 stop_time=time.time() 11 print ("The func run time is %s" %(stop_time-start_time)) 12 return warpper 13 14 15 16 17 18 @timmer #等於 time1=timmer(time1),最後再time1() 19 def time1(): 20 time.sleep(2) 21 print ("in the test1...............") 22 23 24 time1()
實例2:一個函數調用另外一個函數,匿名函數的使用nginx
1 #Author:http://www.cnblogs.com/Jame-mei 2 3 def foo(): 4 print ("in the foo") 5 bar() 6 7 8 9 #foo() 調用bar報錯,由於要將foo()放到函數的後面,由於Python是解釋型語言,須要逐行翻譯! 10 11 def bar(): #bar()能夠放在調用函數的foo的後面 12 print ("in the bar") 13 14 15 calc=lambda x:x*3 #匿名函數的使用 16 17 foo() 18 19 print (calc(10))
3.實現裝飾器的知識儲備: git
1).函數即"變量",見下圖:程序員
其中變量名x,y和函數名test ,本質他們都是內存中的一個地址,因此函數和變量同樣,只不過函數經過()來調用。github
2).高階函數
a.把一個函數名看成實參傳給另一個函數(在不修改被裝飾函數的源代碼的狀況下,爲其添加附加功能)
b.返回值中包含函數名(不修改函數的調用方式)redis
實例3:高階函數的2個功能sql
1 #Author:http://www.cnblogs.com/Jame-mei 2 #高階函數的2個功能 3 4 import time 5 6 7 8 #1.把一個函數看成實參傳給另一個函數 9 def bar(): 10 time.sleep(2.5) 11 print ('in the bar') 12 13 def foo(func): 14 start_time=time.time() 15 func() #bar 的運行時間 16 stop_time=time.time() 17 print ("the func run time is %s" %(stop_time-start_time)) 18 19 20 #foo() 原來的調用方式 21 foo(bar) 22 ------------------------------------output--------------------------------------------------- 23 24 in the bar 25 the func run time is 2.500143051147461 26 27 Process finished with exit code 0
1 #Author:http://www.cnblogs.com/Jame-mei 2 #高階函數的2個功能 3 #2.返回值中包含函數 4 5 def bar(): 6 time.sleep(2.5) 7 print ('in the bar') 8 9 def test2(func): 10 print (func) #打印的是內存地址 11 return func #返回之中包含函數!! 12 13 14 15 16 bar=test2(bar) 17 bar() #表明bar(),打印print
3).嵌套函數和做用域 json
高階函數+嵌套函數=裝飾器
1 def foo(): 2 print ('in the foo()') 3 def bar(): #在函數內定義另外一個函數def ,至關於foo()函數的局部變量。 4 print ('in the bar()') 5 6 bar() #局部變量只能在內部調用!! 7 8 9 10 foo()
1 #Author:http://www.cnblogs.com/Jame-mei 2 #嵌套函數的全局做用域和局部做用域 3 x=0 4 def grandpa(): 5 x=1 6 def dad(): 7 x=2 8 def son(): 9 x=3 10 print (x) 11 son() 12 dad() 13 14 grandpa() 15 print (x)
1 def bar2(): 2 print ('in the bar2') 3 4 def foo2(): 5 print ('in the foo2') 6 bar2() #這叫函數的調用,這裏要區別與函數的嵌套!
1 #Author:http://www.cnblogs.com/Jame-mei 2 import time 3 #寫一個裝飾器,用高階函數+函數嵌套來實現 4 5 6 def timmer(func): 7 def deco(*args,**kwargs): #*args,**kwargs,不管被裝飾的函數有多少參數均可以代替。 8 start_time=time.time() 9 func(*args,**kwargs) 10 stop_time=time.time() 11 print ('the func run time is %s'%(stop_time-start_time)) 12 13 return deco #高階函數返回一個函數名 14 15 16 @timmer # 等於test1=timmer(test1) 17 def test1(): 18 time.sleep(1.123) 19 print ('in the test1...') 20 21 22 @timmer 23 def test2(name,age): 24 time.sleep(2.256) 25 print ('in the test2',name,age) 26 27 28 29 30 31 32 33 34 #用手動的方式來實現裝飾器的功能,@timmer的方式來裝飾test1(),test2() 35 # test1=timmer(test1) #把函數名看成實參,傳給另外一個函數 36 # test2=timmer(test2) 37 38 # print (test1) 39 # print (test2) 40 41 test1() 42 43 test2('Alex',22)
1 #Author:http://www.cnblogs.com/Jame-mei 2 3 user,passwd='alex','abc123' 4 5 def auth(auth_type): 6 7 def outwrapper(func): 8 9 def wrapper(*args, **kwargs): 10 if auth_type=='local': 11 username = input("username:").strip() 12 password = input("password:").strip() 13 if user == username and passwd == password: 14 print("\033[32;1m User has passwd authentication \033[0m") 15 res = func(*args, **kwargs) 16 print('------------->after authentication') 17 return res 18 else: 19 exit("\033[31;1m Ivalid username or password \033[Om") 20 elif auth_type=='ldap': 21 print ('This ldap is disable...') 22 23 return wrapper 24 return outwrapper 25 26 27 28 29 def index(): 30 print ('welcome in the index page') 31 32 @auth(auth_type='local') 33 def home(): 34 print ('welcome %s home page' %user) 35 return 'return from home!' 36 37 38 @auth(auth_type='ldap') 39 def bbs(): 40 print('welcome %s bbs page' %user) 41 42 43 44 #調用,index不用裝飾器,home打印返回值,bbs來驗證local和ldap的差異 45 index() 46 print(home()) 47 bbs()
4.Alex的裝飾器之旅
1):裝飾器之旅1
1 #Author:http://www.cnblogs.com/Jame-mei 2 3 #1.模版 4 '''def home(): 5 print("---首頁----") 6 7 8 def america(): 9 print("----oumei專區----") 10 11 12 def japan(): 13 print("----rihan專區----") 14 15 16 def henan(): 17 print("----北上廣深專區----") 18 19 更改需求: 20 21 想收費得先讓其進行用戶認證,認證經過後,再斷定這個用戶是不是VIP付費會員就能夠了,是VIP就讓看,不是VIP就不讓看就好了唄。 22 你以爲這個需求非常簡單,由於要對多個版塊進行認證,那應該把認證功能提取出來單獨寫個模塊,而後每一個版塊裏調用 就能夠了, 23 與是你輕輕的就實現了下面的功能 。 24 ''' 25 #更改後代碼以下: 26 user_status=False #若是認證後,修改成True 27 28 29 def login(): 30 _username='alex' #假設db中有這2個帳號 31 _password='abc123' 32 global user_status 33 if user_status==False: 34 username=input('please input your username:') 35 password=input('please input your password:') 36 37 if username==_username and password==_password: 38 print ('welcome %s login!' %username) 39 else: 40 print ('input username or password error!') 41 else: 42 print ('用戶已登錄,請勿重複操做!') 43 44 45 def home(): 46 print("---首頁----") 47 48 49 def america(): 50 login() #執行前加上驗證 51 print("----oumei專區----") 52 53 54 def rihan(): 55 print("----rihan專區----") 56 57 58 def bsgs(): 59 login() #執行前加上驗證 60 print("----北上廣深專區----") 61 62 63 home() 64 america() 65 bsgs()
此時你信心滿滿的把這個代碼提交給你的TEAM LEADER審覈,沒成想,沒過5分鐘,代碼就被打回來了。
TEAM LEADER給你反饋是,我如今有不少模塊須要加認證模塊,你的代碼雖然實現了功能,可是須要更改須要加認證的各個模塊的代碼,
這直接違反了軟件開發中的一個原則「開放-封閉」原則,簡單來講,它規定已經實現的功能代碼不容許被修改,但能夠被擴展,即:
封閉:已實現的功能代碼塊不該該被修改
開放:對現有功能的擴展開放
這個原則你仍是第一次據說,我擦,再次感覺了本身這個野生程序員與正規軍的差距,BUT ANYWAY,老大要求的這個怎麼實現呢?
如何在不改原有功能代碼的狀況下加上認證功能呢?你一時想不出思路,只好帶着這個問題回家繼續憋,媳婦不在家,去隔壁老王家串門了,
你正好落的清靜,一不當心就想到了解決方案,不改源代碼能夠呀,
你師從沙河金角大王時,記得他教過你,高階函數,就是把一個函數當作一個參數傳給另一個函數,當時大王說。
有一天,你會用到它的,沒想到這時這個知識點忽然從腦子 裏蹦出來了,我只須要寫個認證方法,每次調用 須要驗證的功能時,
直接 把這個功能 的函數名當作一個參數 傳給 個人驗證模塊不就好了麼,哈哈,機智如我,如是你啪啪啪改寫了以前的代碼
2):裝飾器之旅2
1 #Author:http://www.cnblogs.com/Jame-mei 2 3 user_status = False # 用戶登陸了就把這個改爲True 4 5 6 def login(func): # 把要執行的模塊從這裏傳進來 7 _username = "alex" # 僞裝這是DB裏存的用戶信息 8 _password = "abc!23" # 僞裝這是DB裏存的用戶信息 9 global user_status 10 11 if user_status == False: 12 username = input("user:") 13 password = input("pasword:") 14 15 if username == _username and password == _password: 16 print("welcome login....") 17 user_status = True 18 else: 19 print("wrong username or password!") 20 21 if user_status == True: 22 func() # 看這裏看這裏,只要驗證經過了,就調用相應功能 23 24 25 def home(): 26 print("---首頁----") 27 28 29 def america(): 30 # login() #執行前加上驗證 31 print("----歐美專區----") 32 33 34 def japan(): 35 print("----日韓專區----") 36 37 38 def henan(): 39 # login() #執行前加上驗證 40 print("----河南專區----") 41 42 43 home() 44 login(america) # 須要驗證就調用 login,把須要驗證的功能 當作一個參數傳給login 45 46 # home() 47 # america() 48 login(henan)
你改變了調用方式呀, 想想,如今沒每一個須要認證的模塊,都必須調用你的login()方法,並把本身的函數名傳給你,人家以前可不是這麼調用 的, 試想,若是 有100個模塊須要認證,那這100個模塊都得更改調用方式,這麼多模塊確定不止是一我的寫的,讓每一個人再去修改調用方式 才能加上認證,你會被罵死的。
3):裝飾器之旅3
home()
america
=
login(america)
#你在這裏至關於把america這個函數替換了
henan
=
login(henan)
#那用戶調用時依然寫
america()
|
但問題在於,還不等用戶調用 ,你的america = login(america)就會先本身把america執行了呀。。。。,你應該等我用戶調用 的時候 再執行纔對呀,不信我試給你看。。。
老王:哈哈,你說的沒錯,這樣搞會出現這個問題? 但你想一想有沒有解決辦法 呢?
你:我擦,你指的思路呀,大哥。。。我哪知道 下一步怎麼走。。。
老王:算了,估計你也想不出來。。。 學過嵌套函數沒有?
你:yes,而後呢?
老王:想實現一開始你寫的america = login(america)不觸發你函數的執行,只須要在這個login裏面再定義一層函數,第一次調用america = login(america)只調用到外層login,這個login雖然會執行,但不會觸發認證了,由於認證的全部代碼被封裝在login裏層的新定義 的函數裏了,login只返回 裏層函數的函數名,這樣下次再執行america()時, 就會調用裏層函數啦。詳見代碼:
1 def login(func): #把要執行的模塊從這裏傳進來 2 3 def inner():#再定義一層函數 4 _username = "alex" #僞裝這是DB裏存的用戶信息 5 _password = "abc!23" #僞裝這是DB裏存的用戶信息 6 global user_status 7 8 if user_status == False: 9 username = input("user:") 10 password = input("pasword:") 11 12 if username == _username and password == _password: 13 print("welcome login....") 14 user_status = True 15 else: 16 print("wrong username or password!") 17 18 if user_status == True: 19 func() # 看這裏看這裏,只要驗證經過了,就調用相應功能 20 21 return inner #用戶調用login時,只會返回inner的內存地址,下次再調用時加上()纔會執行inner函數 22 23 24 25 還能夠更簡單 26 27 能夠把下面代碼去掉 28 29 america = login(america) #你在這裏至關於把america這個函數替換了 30 只在你要裝飾的函數上面加上下面代碼 31 32 @login 33 def america(): 34 #login() #執行前加上驗證 35 print("----歐美專區----") 36 37 def japan(): 38 print("----日韓專區----") 39 40 @login 41 def henan(): 42 #login() #執行前加上驗證 43 print("----河南專區----")
你:老王,老王,怎麼傳個參數就不行了呢?
老王:那必然呀,你調用henan時,實際上是至關於調用的login,你的henan第一次調用時henan = login(henan), login就返回了inner的內存地址,第2次用戶本身調用henan("3p"),實際上至關於調用的時inner,但你的inner定義時並無設置參數,但你給他傳了個參數,因此天然就報錯了呀
你:可是個人 版塊須要傳參數呀,你不讓我傳不行呀。。。
老王:沒說不讓你傳,稍作改動即可。
老王:你再試試就行了 。
你: 果真好使,大神就是大神呀。 。。 不過,若是有多個參數呢?
老王:。。。。老弟,你不要什麼都讓我教你吧,非固定參數你沒學過麼? *args,**kwargs...
你:噢 。。。還能這麼搞?,nb,我再試試。
徹底遵循開放-封閉原則,最終代碼以下 。
1 user_status = False #用戶登陸了就把這個改爲True 2 3 def login(func): #把要執行的模塊從這裏傳進來 4 5 def inner(*args,**kwargs):#再定義一層函數 6 _username = "alex" #僞裝這是DB裏存的用戶信息 7 _password = "abc!23" #僞裝這是DB裏存的用戶信息 8 global user_status 9 10 if user_status == False: 11 username = input("user:") 12 password = input("pasword:") 13 14 if username == _username and password == _password: 15 print("welcome login....") 16 user_status = True 17 else: 18 print("wrong username or password!") 19 20 if user_status == True: 21 func(*args,**kwargs) # 看這裏看這裏,只要驗證經過了,就調用相應功能 22 23 return inner #用戶調用login時,只會返回inner的內存地址,下次再調用時加上()纔會執行inner函數 24 25 26 def home(): 27 print("---首頁----") 28 29 @login 30 def america(): 31 #login() #執行前加上驗證 32 print("----歐美專區----") 33 34 def japan(): 35 print("----日韓專區----") 36 37 # @login 38 def henan(style): 39 ''' 40 :param style: 喜歡看什麼類型的,就傳進來 41 :return: 42 ''' 43 #login() #執行前加上驗證 44 print("----河南專區----") 45 46 home() 47 # america = login(america) #你在這裏至關於把america這個函數替換了 48 henan = login(henan) 49 50 # #那用戶調用時依然寫 51 america() 52 53 henan("3p")
要容許用戶選擇用qq\weibo\weixin認證,此時的你,已深諳裝飾器各類裝逼技巧,輕鬆的就實現了新的需求:
1 #_*_coding:utf-8_*_ 2 3 4 user_status = False #用戶登陸了就把這個改爲True 5 6 def login(auth_type): #把要執行的模塊從這裏傳進來 7 def auth(func): 8 def inner(*args,**kwargs):#再定義一層函數 9 if auth_type == "qq": 10 _username = "alex" #僞裝這是DB裏存的用戶信息 11 _password = "abc!23" #僞裝這是DB裏存的用戶信息 12 global user_status 13 14 if user_status == False: 15 username = input("user:") 16 password = input("pasword:") 17 18 if username == _username and password == _password: 19 print("welcome login....") 20 user_status = True 21 else: 22 print("wrong username or password!") 23 24 if user_status == True: 25 return func(*args,**kwargs) # 看這裏看這裏,只要驗證經過了,就調用相應功能 26 else: 27 print("only support qq ") 28 return inner #用戶調用login時,只會返回inner的內存地址,下次再調用時加上()纔會執行inner函數 29 30 return auth 31 32 def home(): 33 print("---首頁----") 34 35 @login('qq') 36 def america(): 37 #login() #執行前加上驗證 38 print("----歐美專區----") 39 40 def japan(): 41 print("----日韓專區----") 42 43 @login('weibo') 44 def henan(style): 45 ''' 46 :param style: 喜歡看什麼類型的,就傳進來 47 :return: 48 ''' 49 #login() #執行前加上驗證 50 print("----河南專區----") 51 52 home() 53 # america = login(america) #你在這裏至關於把america這個函數替換了 54 #henan = login(henan) 55 56 # #那用戶調用時依然寫 57 america() 58 59 # henan("3p") 60 61 帶參數的裝飾器
更多裝飾器黃段子,請訪問導師Alex:http://www.cnblogs.com/alex3714/articles/5765046.html
5.裝飾器的練習操做(重要)
1):無參裝飾器
import time def timmer(func): # func=index def wrapper(*args,**kwargs): start=time.time() res=func(*args,**kwargs) stop=time.time() print('run time is %s' %(stop - start)) return res return wrapper def outter(func): def inner(*args,**kwargs): res=func(*args,**kwargs) return res return inner @timmer #index=timmer(index) def index(): print('welcome to index page') time.sleep(3) @timmer #home=timmer(home) def home(name): print('welecom %s ' %name) time.sleep(2) return 123 index() # wrapper() res=home('egon') # res=wrapper('egon')
2):認證功能裝飾器實現
import time current_userinfo={'user':None} def outter(func): def wrapper(*args,**kwargs): if current_userinfo['user']: return func(*args,**kwargs) user=input('please input you username: ').strip() pwd=input('please input you password: ').strip() if user == 'egon' and pwd == '123': print('login successfull') # 保存登陸狀態 current_userinfo['user']=user res=func(*args,**kwargs) return res else: print('user or password err') return wrapper @outter # index=outter(index) def index(): print('welcome to index page') time.sleep(3) @outter #home=outter(home) def home(name): print('welecom %s ' %name) time.sleep(2) return 123 index() # wrapper() res=home('egon') # res=wrapper('egon')
3):一個函數添加多個裝飾器的生效順序
import time current_userinfo={'user':None} def timmer(func): #func=最原始的index指向的內存地址 def wrapper2(*args,**kwargs): print('wrapper2.....') start=time.time() res=func(*args,**kwargs) # func=最原始的index指向的內存地址 stop=time.time() print('run time is %s' %(stop - start)) return res return wrapper2 def outter(func): # func=wrapper2 def wrapper1(*args,**kwargs): print('wrapper1.....') if current_userinfo['user']: return func(*args,**kwargs) user=input('please input you username: ').strip() pwd=input('please input you password: ').strip() if user == 'egon' and pwd == '123': print('login successfull') # 保存登陸狀態 current_userinfo['user']=user res=func(*args,**kwargs) # func=wrapper2 return res else: print('user or password err') return wrapper1 # 解釋語法的時候應該自下而上 # 執行時則是自上而下 # 能夠連續寫多個裝飾器,處於最頂層的裝飾器先執行 @outter # index=outter(wrapper2) # index=wrapper1 @timmer # timmer(最原始的index指向的內存地址) ==>wrapper2 def index(): print('welcome to index page') time.sleep(3) index() #wrapper1()
4):有參裝飾器的使用
import time current_userinfo={'user':None} def auth(engine='file'): def outter(func): #func=最原始的index def wrapper(*args,**kwargs): if engine == 'file': if current_userinfo['user']: return func(*args,**kwargs) user=input('please input you username: ').strip() pwd=input('please input you password: ').strip() if user == 'egon' and pwd == '123': print('login successfull') # 保存登陸狀態 current_userinfo['user']=user res=func(*args,**kwargs) return res else: print('user or password err') elif engine == 'mysql': print('mysql 的認證機制') elif engine == 'ldap': print('ldap 的認證機制') else: print('不支持該engine') return wrapper return outter @auth(engine='mysql') #@outter # index=outter(最原始的index) # index= wrapper def index(): print('welcome to index page') time.sleep(3) @auth(engine='ldap') def home(name): print('welecom %s ' %name) time.sleep(2) return 123 index() #warpper() home('egon') #wrapper('egon')
5):wraps裝飾器使用
import time from functools import wraps def timmer(func): @wraps(func) def wrapper(*args,**kwargs): start=time.time() res=func(*args,**kwargs) stop=time.time() print('run time is %s' %(stop - start)) return res # wrapper.__doc__ = func.__doc__ # wrapper.__name__= func.__name__ return wrapper @timmer def index(): """ 這是一個index函數 :return: """ print('welcome to index page') time.sleep(1) return 123 print(help(index)) # index.__doc__ print(index.__name__) # res=index() # print(res)
*什麼是迭代器?
迭代是一個重複的過程,每一次重複都是基於上一次結果二進行的。
#單純的重複並非迭代:
while True:
print('=====>>>')
*爲何要用迭代器?
找到一種不依賴於索引的取值方式。
*怎麼用迭代器?
a.#可迭代對象?
在python中,但凡內置有__iter__()方法對象,都是可迭代對象。
例如:字符串str,列表list,元祖tuple,字典dict,集合set,文件open等有__iter__()方法的可迭代對象。
b.#迭代器對象?
執行可迭代對象下的__iter__()方法獲得的返回值就是一個迭代器對象。
迭代器對象中內置有__next__()方法可迭代取值!
因此:同時內置有 __next__()方法 和 __iter__()方法的才叫作迭代器對象!
例如:
d=[1,2,4]
iter_obj=d.__iter__()
print(iter_obj) #<list_iterator object at 0x000000000284A390>
print(iter_obj.__next__())#依次取出d列表中的值,當取完的時候會拋出一個StopIteration的結束信號!
dict={'a':1,'b':2,'c':3,'d':4} iter_obj=dict.__iter__() while True: try: print(iter_obj.__next__()) except StopIteration: break ''' output: a b c d '''
由上面例子可知:for 循環底層運行機制,for 循環能夠稱爲迭代器循環。
1.先調用in 後面對象的__iter__()方法,獲得該對象的迭代器對象。
2.執行迭代器對象的__next__()方法,將獲得的返回值賦值給in前面的變量,而後執行一次循環體代碼。
3.循環往復,直到取乾淨迭代器內的全部值,自動捕捉結束循環。
c.#迭代器的優缺點
優勢:
1.提供一種不依賴於索引的迭代取值方式
2.更加節省內存空間
缺點:
1.是一次性的,值去幹淨後沒法再次取值,除非從新獲得新的迭代器對象。
2.只能日後取值,不能往前取值,永遠沒法預測迭代器的長度。(不如索引的取值的方式靈活)
a.什麼是生成器?
在函數體內但凡是有yield關鍵字,再調用函數就不會執行函數體代碼,獲得返回值就是一個生成器對象.
生成器本質上就是迭代器!!
實例:
def foo(): print('first') yield 1 print('second') yield 2 print('three') g=foo() print(g) #<generator object foo at 0x00000000024958E0> print(g.__iter__()) #<generator object foo at 0x00000000024958E0> res1=next(g) #first res2=next(g) #second print(res1) #1 print(res2) #2 #等價於print(g.__next__()) 當res3=next(g)取不到值的時候會報錯,跟迭代器同樣。 #生成器本質上就是迭代器!
def foo(): print('first') yield 1 print('second') yield 2 print('three') g=foo() for item in g: print(item)
next(g)過程:
會觸生成器g 所對應的函數的執行,知道遇到yiled才停下來,而後吧yiled後面的返回值看成本次next操做的結果!
b.爲何要用生成器?
學習生成器是爲了掌握一種自定義迭代器的方式!
1.實例:用yield實現range()函數的方法,就叫作自定義迭代器。。。
#用yield 實現range函數的功能 def my_range(start,stop,step=1): print('開始運行') while start<stop: yield start start+=step print('結束運行') res=my_range(1,1000,2) print(res.__next__()) print(res.__next__()) print(res.__next__()) ''' 開始運行 1 3 5 ''' # for i in res: # print(i)
2.yiled表達式應用
實例1:yield的使用,給dog喂包子...
def dog(name): food_list=[] print('狗哥 %s 準備開吃'%name) while True: food=yield food_list #暫停 yield=None print('狗哥%s 開始吃了%s'%(name,food)) food_list.append(food) g=dog('alex') # next(g)#狗哥 alex 準備開吃 # next(g) #狗哥alex 開始吃了None # next(g)#狗哥alex 開始吃了None g.send(None)#等價於next(g),由於使用是第一次必須傳入None!! #狗哥 alex 準備開吃,返回值爲None res1=g.send('骨頭') #send先傳值,再next(g) 狗哥alex 開始吃了骨頭 print(res1) res2=g.send('泔水') print(res2) res3=g.send('米飯') print(res3) #g.close() #關閉鏈接
實例2:yield實現協程函數
#yield關鍵字的另一種使用形式:表達式形式的yield def eater(name): print('%s 準備開始吃飯啦' %name) food_list=[] while True: food=yield food_list print('%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('燒鹿尾')
實例3:用裝飾器來實現初始化協程函數(next(g) /g.send(None))
1 def init(func): 2 def wrapper(*args,**kwargs): 3 g=func(*args,**kwargs) 4 next(g) 5 return g 6 return wrapper 7 8 9 10 11 @init 12 def eater(name): 13 print('%s 準備開始吃飯了'%name) 14 food_list=[] 15 while True: 16 food=yield food_list 17 print('%s 吃了 %s '%(name,food)) 18 food_list.append(food) 19 20 21 22 g=eater('egon') 23 #g.send(None) #用@init能夠替代初始化傳入None值的操做 24 g.send('蒸羊羔') 25 ''' 26 egon 準備開始吃飯了 27 egon 吃了 蒸羊羔 28 '''
總結yield:
1.yield提供一種自定義迭代器的方式
2.與return 對比,都能返回多個值,都沒有類型限制,而return只能返回一次值,而yiled能夠返回屢次值(yield能夠幫我賣保存函數的執行狀態或結果,以便下次迭代使用)
生成器表達式的應用:
''' 用生成器實現range函數的功能 ''' #1.應用1 def my_range(start,stop,step=1): while start<stop: yield start start+=step for item in my_range(1,5,2): print(item) #2.應用2 def dog(name): print('狗哥:%s 準備開始'%name) food_list=[] while True: food=yield food_list print('狗哥%s 吃了:%s'%(name,food)) food_list.append(food) #print(food_list) dg=dog('egon') res0=next(dg) #初始化yield的時候必須給一個None值! print(res0) res1=dg.send('骨頭') print(res1) res2=dg.send('泔水') print(res2) res3=dg.send('饅頭') print(res3) #3.生成器表達式應用 #列表表達式 # l=[i**2 for i in range(1,11)] # print(l) names=['egon','alex','lxx'] g_name=(name.upper() for name in names if name!='egon') print(g_name) #<generator object <genexpr> at 0x0000000002455A40> print(next(g_name)) with open('a.txt','rt',encoding='utf-8') as f: # g_lines=(line for line in f) # print(next(g_lines)) # g_lines=(len(line) for line in f) # print(max(g_lines)) print(max(len(line) for line in f))
1.請參考內置函數官方文檔或者相關文檔
菜鳥教程:http://www.runoob.com/python3/python3-built-in-functions.html
2.部份內置函數實例練習
1):frozenset內置函數
2):hash函數的原理
3).部分其餘函數
1 #Author:http://www.cnblogs.com/Jame-mei 2 3 #1.匿名函數lanbda 方式1 4 #(lambda n:print(n)) (5) 5 6 #匿名函數lanbda 方式2,不能作循環或者迭代,只能進行簡單的三元運算! 7 calc=lambda n:n*2 8 #print(calc(10)) #輸出20 9 10 11 #2.filter() 函數用於過濾序列,過濾掉不符合條件的元素,返回由符合條件元素組成的新列表。 與匿名函數結合使用以下: 12 ''' 13 res = filter(lambda n:n>5,range(10)) 14 for i in res: 15 print (i) 16 17 輸出: 18 6 19 7 20 8 21 9 22 ''' 23 24 25 '''#3.map函數的使用:map() 會根據提供的函數對指定序列作映射。 26 res=map(lambda n:n*n,range(10)) #等價於res1=[i*2 for i in range(10)]和res2=[lambda i:i*2,range(10)] 27 for i in res: 28 print (i) 29 30 輸出: 31 0 32 1 33 4 34 9 35 16 36 25 37 36 38 49 39 64 40 81 41 ''' 42 43 '''#4.reduce 在python2.x中屬於內置函數,可是在python3.x中已經移動到了functools 44 import functools 45 res=functools.reduce(lambda x,y:x*y,range(1,5)) 46 print(res) 47 輸出: 48 24 49 ''' 50 51 52 '''#5.frozenset() 凍結 53 res1=([1,1,22,22,3,3]) 54 res2=frozenset([1,1,22,22,3,3]) 55 ''' 56 57 58 '''#5.globals() 函數會以字典類型返回當前位置的所有全局變量。''' 59 print (globals()) 60 61 62 '''#6.pow() 方法返回 xy(x的y次方) 的值 63 print (pow(2,3)) #輸出8''' 64 65 '''#7.repr() 函數將對象轉化爲供解釋器讀取的形式。''' 66 dict = {'runoob': 'runoob.com', 'google': 'google.com'} 67 print (repr(dict)) 68 69 70 '''#8.reversed 函數返回一個反轉的迭代器。''' 71 72 '''#9.round() 方法返回浮點數x的四捨五入值。''' 73 print (round(4.25)) 74 print (round(4.78)) 75 print (round(3.78)) 76 77 78 '''#10.sorted()排序....''' 79 sor={-5:3,6:2,1:10,9:5,7:4} 80 print (sor) 81 #print (sorted(sor.items())) 默認按照Key排序 82 print (sorted(sor.items(),key=lambda x:x[1])) #按照value來進行排序! 83 84 85 '''#11.type() 函數若是你只有第一個參數則返回對象的類型,三個參數返回新的類型對象。''' 86 87 88 '''#12.zip()函數''' 89 a=[1,2,3,4] 90 b=['a','b','c','d'] 91 for i in zip(a,b): 92 print (i) 93 94 95 '''#13.import decorator 96 __import__('decorator') 97 98 輸出: 99 in the test1............... 100 The func run time is 2.0011146068573 101 '''
1.參考 http://www.cnblogs.com/alex3714/articles/5161349.html
2.json序列化和反序列化操做實例
1):json序列化和反序列化
實例1:序列化和反序列化前奏
1 #Author:http://www.cnblogs.com/Jame-mei 2 3 ''' 4 #1.序列化,將內存的對象,變成字符串存起來 5 6 info={ 7 'name':'alex', 8 'age':24 9 10 } 11 f=open('json1.txt','w') 12 f.write(str(info)) 13 f.close() 14 ''' 15 16 #2.反序列化,將字符串取出來 17 f=open('json1.txt','r') 18 data=eval(f.read()) #直接r.read()是一個字符串類型,想要輸出以前的字典,須要用eval()函數。 19 print (data['name']) 20 f.close() 21 22 #以上轉換方法太low了,請看json02.py內容!
實例2:json實現序列化和反序列化
1 #Author:http://www.cnblogs.com/Jame-mei 2 import json 3 4 ''' 5 #1.序列化 6 info={ 7 'name':'alex', 8 'age':24 9 10 } 11 f=open('json2.txt','w') 12 # print (json.dumps(info)) #打印出來是字符串 13 f.write(json.dumps(info)) 14 f.close() 15 ''' 16 17 18 #2.反序列化 19 f=open('json2.txt','r') 20 #print (json.loads(f.read())) 21 data=json.loads(f.read()) 22 print (data['name']) 23 print (data['age'])
實例3:json只能處理簡單的數據類型
1 #Author:http://www.cnblogs.com/Jame-mei 2 import json 3 4 def sayhi(name): 5 print ('hello ',name) 6 7 8 info={ 9 'name':'alex', 10 'age':24, 11 'func':sayhi 12 } 13 14 f=open('json3.txt','w') 15 f.write(json.dumps(info)) 16 f.close() 17 18 19 '''總結:1.序列化def會報錯,TypeError: Object of type 'function' is not JSON serializable。 20 2.json只能處理簡單的數據類型,例如字典,列表,字符串等。 21 3.想要處理更加複雜的,須要用到pickle,用法跟json徹底同樣。 22 '''
實例4:json處理序列化和反序列化最好單個任務只進行一次
1 #Author:http://www.cnblogs.com/Jame-mei 2 import json 3 4 ''' 5 #1.dumps屢次,進行序列化,修改其中的元素再次dumps,發現存了2個字典進去。 6 def sayhi(name): 7 print ('hello ',name) 8 9 10 info={ 11 'name':'alex', 12 'age':24, 13 #'func':sayhi 14 } 15 16 f=open('json5.txt','w') 17 f.write(json.dumps(info)) 18 19 info['age']=26 #修改info的內容,再次進行dumps 20 f.write(json.dumps(info)) 21 22 f.close() 23 ''' 24 25 f=open('json5.txt','r') 26 data=json.loads(f.read()) 27 print (data) 28 #json.decoder.JSONDecodeError: Extra data: line 1 column 28 (char 27) 29 30 31 '''總結: 32 經過以上序列化和反序列化操做,dumps能夠屢次進行,可是loads後會報錯,能夠循環打印出來,可是不能打印出每次dumps的。 33 因此,最好每次dumps()和loads(),每次序列化和反序列化只進行一次,有新的內容再從新進行存取操做。 34 35 36 '''
3.pickle
1):實例1:pickle序列化和反序列化操做
1 import pickle 2 3 4 '''#1.序列化 5 def sayhi(name): 6 print ('hello ',name) 7 8 9 info={ 10 'name':'alex', 11 'age':24, 12 'func':sayhi 13 } 14 15 f=open('json3.txt','wb') #若是隻是w,會報TypeError: write() argument must be str, not bytes 16 f.write(pickle.dumps(info)) 17 f.close() 18 ''' 19 20 def sayhi(name): 21 print ('hello ',name) 22 23 f=open('json3.txt','rb') 24 data=pickle.loads(f.read()) 25 #AttributeError: Can't get attribute 'sayhi' on <module '__main__' from 'E:/pythonwork/s14/day04/pickle01.py'> 26 #sayhi隨着函數執行後,會從內存中消失,且func:sayhi也不該該這用,須要手動將def sayhi()函數copy過來。 27 print (data['name']) 28 print (data) 29 print (data['func']('caiyunhua')) #執行func對應的函數!
2):實例2:pickle精簡版序列化和反序列化
1 #Author:http://www.cnblogs.com/Jame-mei 2 import pickle 3 4 5 #1.精簡版pickle序列化 6 def sayhi(name): 7 print ('hello ',name) 8 9 10 info={ 11 'name':'alex', 12 'age':24, 13 'func':sayhi 14 } 15 16 f=open('json4.txt','wb') 17 pickle.dump(info,f) #這步至關於pickle01中的f.write(pickle.dumps(info))序列化操做! 18 f.close() 19 20 21 f1=open('json4.txt','rb') 22 data=pickle.load(f1) #這步至關於pickle02中的 data=pickle.loads(f.read()) 23 print (data['name']) 24 print (data) 25 print (data['func']('tom'))
1.三元表達式
語法:# res=條件成立時的返回值 if 條件 else 條件不成立時的返回值
例子:比較兩個大小:
1):用函數來實現: def max2(x,y): if x>y: return x else: return y res=max2(1,2) 2):用三元表達式實現 x=1 y=2 res=x if x>y else y print(res) res=True if x>y else False print(res)
2.列表生成式
語法:# [ 條件成立的列表 循環體 判斷條件]
例子: 過濾符合條件的數字列表,過濾添加符合條件的字符列表
1.生成一個0-10大於3的新列表 l1=[i for i in range(10) if i > 3] print(l1) 2.將一個列表過濾,生成一個新的列表 names=['alex_sb','egon','liuqingzheng_sb','wupeiqi_sb'] l2=[] for name in names: if name.endswith('sb'): l.append(name.upper()) print(l2) names=[name.upper() for name in names if name.endswith('sb')] print(names)
3.字典生成式
語法:#{i:i 循環體 過濾條件}
例子:從數字中快速生成一個字典,將一個列表格式快速生成字典
#1.生成一個純數字的字典 dic1={i:i for i in range(10) if i>3} print(dic1) #2.將一個列表快速生成爲一個字典 l1=[ ['name','egon'], ['age',18], ['sex','male'] ] dic2={} dic2={item[0]:item[1] for item in l1} print(dic2)
"設計項目目錄結構",就和"代碼編碼風格"同樣,屬於我的風格問題。對於這種風格上的規範,一直都存在兩種態度:
我是比較偏向於後者的,由於我是前一類同窗思想行爲下的直接受害者。我曾經維護過一個很是很差讀的項目,其實現的邏輯並不複雜,可是卻耗費了我很是長的時間去理解它想表達的意思。今後我我的對於提升項目可讀性、可維護性的要求就很高了。"項目目錄結構"其實也是屬於"可讀性和可維護性"的範疇,咱們設計一個層次清晰的目錄結構,就是爲了達到如下兩點:
因此,我認爲,保持一個層次清晰的目錄結構是有必要的。更況且組織一個良好的工程目錄,實際上是一件很簡單的事兒。
關於如何組織一個較好的Python工程目錄結構,已經有一些獲得了共識的目錄結構。在Stackoverflow的這個問題上,能看到你們對Python目錄結構的討論。
這裏面說的已經很好了,我也不打算從新造輪子列舉各類不一樣的方式,這裏面我說一下個人理解和體會。
假設你的項目名爲foo, 我比較建議的最方便快捷目錄結構這樣就足夠了:
Foo/ |-- bin/ | |-- foo |
|----com |
|-- foo/ | |-- tests/ | | |-- __init__.py | | |-- test_main.py | | | |-- __init__.py | |-- main.py | |-- docs/ | |-- conf.py | |-- abc.rst | |-- setup.py |-- requirements.txt |-- README
簡要解釋一下:
bin/
: 存放項目的一些可執行文件,固然你能夠起名script/
之類的也行。foo/
: 存放項目的全部源代碼。(1) 源代碼中的全部模塊、包都應該放在此目錄。不要置於頂層目錄。(2) 其子目錄tests/
存放單元測試代碼; (3) 程序的入口最好命名爲main.py
。docs/
: 存放一些文檔。setup.py
: 安裝、部署、打包的腳本。requirements.txt
: 存放軟件依賴的外部Python包列表。README
: 項目說明文件。
除此以外,有一些方案給出了更加多的內容。好比LICENSE.txt
,ChangeLog.txt
文件等,我沒有列在這裏,由於這些東西主要是項目開源的時候須要用到。若是你想寫一個開源軟件,目錄該如何組織,能夠參考這篇文章。
下面,再簡單講一下我對這些目錄的理解和我的要求吧。
這個我以爲是每一個項目都應該有的一個文件,目的是能簡要描述該項目的信息,讓讀者快速瞭解這個項目。
它須要說明如下幾個事項:
我以爲有以上幾點是比較好的一個README
。在軟件開發初期,因爲開發過程當中以上內容可能不明確或者發生變化,並非必定要在一開始就將全部信息都補全。可是在項目完結的時候,是須要撰寫這樣的一個文檔的。
能夠參考Redis源碼中Readme的寫法,這裏面簡潔可是清晰的描述了Redis功能和源碼結構。
通常來講,用setup.py
來管理代碼的打包、安裝、部署問題。業界標準的寫法是用Python流行的打包工具setuptools來管理這些事情。這種方式廣泛應用於開源項目中。不過這裏的核心思想不是用標準化的工具來解決這些問題,而是說,一個項目必定要有一個安裝部署工具,能快速便捷的在一臺新機器上將環境裝好、代碼部署好和將程序運行起來。
這個我是踩過坑的。
我剛開始接觸Python寫項目的時候,安裝環境、部署代碼、運行程序這個過程全是手動完成,遇到過如下問題:
setup.py
能夠將這些事情自動化起來,提升效率、減小出錯的機率。"複雜的東西自動化,能自動化的東西必定要自動化。"是一個很是好的習慣。
setuptools的文檔比較龐大,剛接觸的話,可能不太好找到切入點。學習技術的方式就是看他人是怎麼用的,能夠參考一下Python的一個Web框架,flask是如何寫的: setup.py
固然,簡單點本身寫個安裝腳本(deploy.sh
)替代setup.py
也何嘗不可。
這個文件存在的目的是:
setup.py
安裝依賴時漏掉軟件包。
這個文件的格式是每一行包含一個包依賴的說明,一般是flask>=0.10
這種格式,要求是這個格式能被pip
識別,這樣就能夠簡單的經過 pip install -r requirements.txt
來把全部Python包依賴都裝好了。具體格式說明: 點這裏。
conf.py
放在源碼目錄下,而是放在docs/
目錄下。
不少項目對配置文件的使用作法是:
import conf
這種形式來在代碼中使用配置。
這種作法我不太贊同:
conf.py
這個文件。
因此,我認爲配置的使用,更好的方式是,
可以佐證這個思想的是,用過nginx和mysql的同窗都知道,nginx、mysql這些程序均可以自由的指定用戶配置。
因此,不該當在代碼中直接import conf
來使用配置文件。上面目錄結構中的conf.py
,是給出的一個配置樣例,不是在寫死在程序中直接引用的配置文件。能夠經過給main.py
啓動參數指定配置路徑的方式來讓程序讀取配置內容。固然,這裏的conf.py
你能夠換個相似的名字,好比settings.py
。或者你也可使用其餘格式的內容來編寫配置文件,好比settings.yaml
之類的。
實例:文件結構和不一樣的文件目錄的相互調用!
main.py:
atm.py:其中...bin/atm.py調用...core/main.py內的函數。
做業需求:
模擬實現一個ATM + 購物商城程序
示例代碼 https://github.com/triaquae/py3_training/tree/master/atm
簡易流程圖:https://www.processon.com/view/link/589eb841e4b0999184934329
本人完成做業提交gitee.com
https://gitee.com/meijinmeng/Atm-Shopping
master 基礎版
1stage 升級版