C語言基礎學習PYTHON——基礎學習D04
html
20180810內容綱要:python
1 內置函數git
2 裝飾器github
3 生成器redis
4 迭代器算法
5 軟件目錄結構規範express
6 小結json
1 內置函數flask
內置函數方法詳解:https://docs.python.org/3/library/functions.html?highlight=builtapi
1 #Author:ZhangKanghui 2 3 print(all([0,-5,4])) 4 print(all([1,-5,4])) 5 #all() return true if all elements of the iterable are true (or if the iterable is empty) 6 #any() 便是有一個真即爲真。 7 8 a =ascii([1,2]) 9 print(type(a),a) 10 print(type(a),[a]) 11 #ascii() 把數據對象變成可打印的字符串形式 12 13 #bin() #把數字十進制轉二進制 14 print(bool(0)) 15 print(bool(1)) 16 print(bool([])) 17 print(bool([1])) 18 19 a =bytes("abcde",encoding="utf-8") 20 print(a.capitalize(),a) #字符串不能夠被修改,所以字節格式的字符串也不能被修改 21 b =bytearray("abcde",encoding="utf-8") #bytearray()變成列表,這樣就能夠修改了 22 print(b[0]) #以ascii碼輸出第一個字節 23 b[1] = 100 24 print(b) 25 26 27 print(callable([])) #判斷是否可調用,就看能不能加()的方式調用 28 def a():pass 29 print(callable(a)) 30 31 print(chr(98)) 32 print(ord('b')) 33 34 35 print(divmod(5,3)) #輸出結果(商,餘數) 36 #eval() #把一個字符串變成字典 37 38 def var(): 39 local_var =333 40 print(locals()) 41 var() 42 print(globals()) #程序的全部變量以key,value字典形式輸出 43 print(globals().get('local_var')) #程序的全部變量以key,value字典形式輸出 44 45 46 print(hash('Kanghui')) 47 48 print(hex(15)) #轉十六進制 49 print(oct(1)) #轉八進制 50 print(pow(3,5)) #3的5次方 51 d ='<code object <module> at 0x03379230, file "", line 1>' 52 print(repr(d)) #轉成字符串 53 print(round(1.433,2)) #保留兩位小數 54 f =range(20) 55 #print(d[slice(2,5)]) 這是什麼鬼?說好的切片呢 56 57 a ={6:30,8:7,1:4,99:27,18:16} 58 #print(sorted(a)) #只是把字典中的key排序了,沒有value的值 59 #如何實現帶有value的key排序呢/ 60 #print(sorted(a.items())) #排序完成後變成一個列表,由於字典默認是無序的 61 #那麼按value排序怎麼辦呢? 62 print(sorted(a.items(),key =lambda x:x[1])) 63 print(a) 64 65 66 m =[1,2,3,4] 67 n =['a','b','c','d'] 68 print(zip(m,n)) 69 for i in zip(m,n): 70 print(i)
2 裝飾器
a 高階函數
b 嵌套函數
高階函數+嵌套函數》=裝飾器
定義:本質就是函數,爲其餘函數添加附加功能。
原則:
1.不能修改被裝飾函數的源代碼
2.不能修改被裝飾函數的調用方式
a 高階函數
高階函數1:貌似是一個裝飾器,car裝飾bar。雖然沒有改變bar的源代碼爲bar添加了附加功能,可是改變了調用方式
1 import time 2 3 def bar(): 4 time.sleep(3) 5 print('in the bar') 6 7 def car(func): 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 13 car(bar)
高階函數2:
1 import time 2 def bar(): 3 time.sleep(3) 4 print('in the bar') 5 def car(func): 6 print(func) 7 return func 8 9 10 # print(car(bar)) 11 #兩者的區別 12 # a=car(bar()) #將bar()的執行結果/返回值傳給func,這樣就不符合高階函數的定義 13 # b=car(bar) #將bar的內存地址傳給func, 14 # print(a) 15 # print(b) 16 t =car(bar) #將bar的內存地址傳給func先執行函數car裏面的print(bar的內存地址),而後返回執行函數bar 17 t() 18 #此時,把t賦給bar 19 bar=car(bar) 20 bar() 21 #如今好像不修改函數的調用方式也能爲其添加附加功能,可是給bar從新定義,將原來函數中的bar給覆蓋掉了
如何實現不改變源代碼和調用方式的狀況下實現裝飾器呢?
b 嵌套函數
嵌套函數:
1 #Author:ZhangKanghui 2 3 def foo(): 4 print('in the foo') 5 def bar(): #此處的函數bar即變量,局部變量。只能在嵌套函數內局部調用 6 print('in the bar') 7 8 #bar() 這裏不能調用函數bar能夠類比局部變量
接下來,把高階函數和嵌套函數結合起來有很麼用呢?
>>裝飾器:
1 #Author:ZhangKanghui 2 #裝飾器 3 4 import time 5 def timer(func): #高階函數,返回值中包含函數名 6 def dec(): #嵌套函數 7 start_time = time.time() 8 func() #run bar() 9 stop_time = time.time() #添加功能 10 print('the func run time is %s' % (stop_time - start_time)) 11 return dec #返回函數dec的內存地址 12 #@timer #bar=timer(bar) 13 def bar(): 14 time.sleep(3) 15 print('in the bar') 16 bar=timer(bar) 17 bar() #bar()=dec() 18 # 若是想要傳參數 19 @timer 20 def test2(name): 21 print("test2:",name) 22 test2()
1 #Author:ZhangKanghui 2 #模擬網站,一個函數就是一個網頁,部分網頁須要登陸,添加驗證功能 3 4 user,passwd = 'Kanghui','abc123' #先寫個特殊狀況下指定用戶帳號密碼 5 import time 6 def auth(func): 7 def wrapper(*args,**kwargs): 8 user_name =input("user_name:").strip() #去掉兩頭的空格和回車 9 password =input("passwd:").strip() 10 11 if user ==user_name and passwd ==password: 12 print("\033[32;1mUser has passed authentication\033[0m") 13 func(*args,**kwargs) 14 15 else: 16 exit("\031[32;1mInvalid password\033[0m") 17 return wrapper 18 19 def index(): 20 print("welcome to index page") 21 @auth 22 def bbs(): 23 print("welcome to bbs page") 24 25 index() 26 bbs() 27 #此時這個裝飾器的基本功能已經完成。雖然正常狀況下只需登陸一次便可。 28 #接下來有個問題:沒有改變函數源代碼和調用方式可是函數執行之後的返回值發生了變化 29 @auth 30 def home(): 31 print("welcome to home page") 32 return "from home" 33 print(home()) #調用home()至關於調用wrapper() 34 #如何獲取到home的返回值 35 # 將func() 換成return func(*args,**kwargs) #這個可使home()獲取到本身的返回值
1 #Author:ZhangKanghui 2 user,passwd = 'Kanghui','abc123' #先寫個特殊狀況下指定用戶帳號密碼 3 import time 4 def auth(auth_type): 5 print("auth_type:",auth_type) 6 def out_wrapper(func): 7 def wrapper(*args,**kwargs): 8 print("wrapper func args:",*args,**kwargs) 9 if auth_type =='local': 10 user_name =input("user_name:").strip() #去掉兩頭的空格和回車 11 password =input("passwd:").strip() 12 13 if user ==user_name and passwd ==password: 14 print("\033[32;1mUser has passed authentication\033[0m") 15 func(*args,**kwargs) 16 17 else: 18 exit("\031[32;1mInvalid password\033[0m") 19 elif auth_type=='ldap': 20 print("搞毛線") 21 22 return wrapper 23 return out_wrapper 24 def index(): 25 print("welcome to index page") 26 @auth(auth_type ='local') 27 def bbs(): 28 print("welcome to bbs page") 29 @auth(auth_type ='ldap') 30 def home(): 31 print("welcome to home page") 32 return "from home"
那麼裝飾器是如何實如今不改變源代碼和調用方式的狀況下爲函數添加附加功能的呢?能夠經過以上三個案例debug一下,看一下程序運行過程。
裝飾器的做用;
這裏還有一個額外擴展練習:
1 user_status = False #用戶登陸了就把這個改爲True 2 3 def login(auth_type): #把要執行的模塊從這裏傳進來 4 def auth(func): 5 def inner(*args,**kwargs):#再定義一層函數 6 if auth_type == "qq": 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 return func(*args,**kwargs) # 看這裏看這裏,只要驗證經過了,就調用相應功能 23 else: 24 print("only support qq ") 25 return inner #用戶調用login時,只會返回inner的內存地址,下次再調用時加上()纔會執行inner函數 26 27 return auth 28 29 def home(): 30 print("---首頁----") 31 32 @login('qq') 33 def america(): 34 #login() #執行前加上驗證 35 print("----歐美專區----") 36 37 def japan(): 38 print("----日韓專區----") 39 40 @login('weibo') 41 def henan(style): 42 ''' 43 :param style: 喜歡看什麼類型的,就傳進來 44 :return: 45 ''' 46 #login() #執行前加上驗證 47 print("----河南專區----") 48 49 home() 50 # america = login(america) #你在這裏至關於把america這個函數替換了 51 #henan = login(henan) 52 53 # #那用戶調用時依然寫 54 america() 55 56 # henan("3p")
3 生成器
>>生成器generator:
1.只有在調用時纔會生成相應的數據
2.只記錄當前位置
3.只有一個__next__()方法逐個調用生成器中的數據
先來作個對比:
列表生成式:
1 列表生成式: 2 >>>[i*2 for i in range(10)] 3 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] 4 5 a=[] 6 >>> for i in range(10): 7 ... a.append(i*2) 8 ... 9 >>> a 10 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] 11 12 >>> ( i*2 for i in range(10) ) 13 <generator object <genexpr> at 0x0155B030>
生成器方式:
1 經過生成器的方式 2 >>> b=( i*2 for i in range(10) ) 3 >>> for i in b: 4 ... print(i) 5 ... 6 0 7 2 8 4 9 6 10 8 11 10 12 12 13 14 14 16 15 18
[]和()的區別在於:[]這個在訪問以前,全部的數據都已經準備完畢。而()只是提供了一種算法數據暫時不存在,只有訪問的時候纔會存在
這樣可以節省內存空間。不信能夠試試這個
1 a=[for i*2 in range(100000000)] 2 3 a[1000] 4 5 b=(for i*2 in range(100000000)) 6 7 b[1000] 8 9 TypeError: 'generator' object is not subscriptable
那除了循環還有什麼方式能夠調用生成器中的數據呢?
1 >>> b=( i*2 for i in range(10) ) 2 3 >>> b.__next__() 4 5 0
generator很是強大,若是推算的算法比較複雜,用相似列表的生成式的for循環沒法實現的時候,還能夠用函數實現。
好比著名的斐波那契數列(Fibonacci)除第一個和第二個外,任一個數均可以由前兩個數相加獲得。
1,1,2,3,5,8,13,...
1 #Author:ZhangKanghui 2 3 #斐波那契數列的推算 4 ''' 5 def fib(max): 6 n,a,b =0,0,1 7 while n<max: 8 print(b) 9 a,b =b, a+b #注意賦值語句 10 #通常會理解成:a=0 b=1 a=b a=1 11 #b=a+b b=1+1=2 但實際上b=0+1=1。。。。下面有詳解 12 n=n+1 13 return 'done' 14 fib(10) 15 ''' 16 17 #注意賦值語句: a,b =b, a+b 18 #t =[a,a+b] #t是一個tumple 19 #a =t[0] 20 #b=t[1] 21 #但沒必要顯式寫出臨時變量t就能夠賦值。
#此時,離生成器只有一步之遙,fib函數只是定義了斐波那契數列推算的規則,這種邏輯很相似生成器
#此時,只須要吧print(b)改爲yeild b神奇的事情就會發生了
1 def fib(max): 2 n,a,b =0,0,1 3 while n<max: 4 yield b 5 a,b =b, a+b 6 n=n+1 7 return 'done' 8 print(fib(10)) 9 #<generator object fib at 0x003BCDE0> 10 b=fib(10) 11 print(b.__next__()) 12 print("i am your dad") 13 print(b.__next__()) 14 print("hello world") 15 print(b.__next__()) 16 #此時,咱們可以完成隨時在生成器訪問值的過程當中添加其餘操做。 17 ''' 18 print("---new strat---") 19 for i in b: 20 print(i) 21 ''' 22 #而且能夠隨時再次接着繼續訪問生成器中的值 23 #那麼最終會打印輸出 return "done"嗎? 24 #在for 循環中就不會打印。可是...一路next呢?
在一路next以後,出現異常該怎麼抓住異常呢?
1 def fib(max): 2 n,a,b =0,0,1 3 while n<max: 4 yield b 5 a,b =b, a+b 6 n=n+1 7 return 'done' 8 print(fib(10)) 9 #<generator object fib at 0x003BCDE0> 10 b=fib(10) 11 print(b.__next__()) 12 print("i am your dad") 13 print(b.__next__()) 14 print("hello world") 15 print(b.__next__()) 16 #此時,咱們可以完成隨時在生成器訪問值的過程當中添加其餘操做。 17 ''' 18 print("---new strat---") 19 for i in b: 20 print(i) 21 ''' 22 #而且能夠隨時再次接着繼續訪問生成器中的值 23 #那麼最終會打印輸出 return "done"嗎? 24 #在for 循環中就不會打印。可是...一路next呢? 25 print(b.__next__()) 26 print(b.__next__()) 27 print(b.__next__()) 28 print(b.__next__()) 29 print(b.__next__()) 30 print(b.__next__()) 31 print(b.__next__()) 32 print(b.__next__()) 33 print(b.__next__()) 34 #異常 :StopIteration: done 35 #那麼咱們如何抓住這個異常呢?下面是一段捕獲異常的代碼
1 g=fib(6) 2 while True: 3 try: 4 x =next(g) 5 print('g',x) 6 except StopIteration as e: 7 print("Generator return value:",e.value) 8 break
generator和函數的執行流程不同。函數是順序執行,遇到return
語句或者最後一行函數語句就返回。而變成generator的函數,
在每次調用next()
的時候執行,遇到yield
語句返回,再次執行時從上次返回的yield
語句處繼續執行。
補充一條:
1 def send(self,value) 2 '''Resume the generator and 「send」 a value that becomes the result of the current yield-expression.'''
還可經過yield實如今單線程的狀況下實現併發運算的效果:
1 #Author:ZhangKanghui 2 import time 3 def consumer(name): 4 print("%s is ready to eat baozi" %name) 5 while True: 6 baozi =yield #保存當前狀態 7 8 print("baozi %s is coming,%s miximixi" %(baozi,name)) 9 10 ''' 11 c =consumer('Kanghui') 12 c.__next__() 13 b1 ='beef' 14 c.send(b1) 15 c.__next__() #此時包子並無傳值進去,只是在調用yield,繼續執行yield以前保存的狀態 16 ''' 17 def producer(name): 18 c1 =consumer('A') #這樣只是把函數變成生成器還沒開始運行,因此下面要調用next、 19 c2 =consumer('B') 20 c1.__next__() 21 c2.__next__() 22 print("Your dad is ready to make baozi") 23 for i in range(10): 24 time.sleep(1) 25 print("Have made two") 26 c1.send(i) 27 c2.send(i) 28 29 producer('Kanghui') 30 #這樣就能實現交互式地
4 迭代器
可直接做用於for循環的數據類型:
1、集合數據類型:list、tuple、dict、set、str等
2、generat,包括生成器和帶yield的generator function
這些能夠直接做用於for循環的對象統稱爲可迭代對象:Iterable
可使用isinstance()判斷一個對象是一個什麼樣的類型。
1 >>> from collections import Iterable 2 >>> isinstance([],Iterable) 3 True
能夠被next()函數調用並不斷返回下一個值的對象成爲迭代器:Interator 。
1 >>> from collections import Iterator 2 >>> isinstance((x for x in range(10)),Iterator) 3 True 4 >>> isinstance([],Iterator) 5 False
雖然list、dict、str是Iterable 可是卻不是Iterator
把這些變成Iterator可使用iter()函數
1 >>> isinstance(iter([]),Iterator) 2 True
這是爲何呢?由於在Python中的Iterator對象表示的是一個數據流,Iterator對象能夠被next()函數調用並返回下個數據,直到沒有數據拋出
StopIteration錯誤。能夠把這個數據流看作是一個有序序列,但不知道長度,只能經過next()函數實現按需計算下一個數據,因此Iterator是
惰性的,只有在須要返回下一個數據時它纔會計算。
Iterator甚至能夠是一個無限大的數據流,例如全體天然數,而使用list是不可能存儲全體天然數的。
Python的for循環實際上就是經過不斷調用的next()函數實現的。
1 #Author:ZhangKanghui 2 3 for x in [1,2,3,4,5]: 4 pass 5 #實際上就等價於: 6 it = iter([1,2,3,4,5]) #迭代器 7 while True: 8 try: 9 x =next(it) 10 except StopIteration: 11 break
5 軟件目錄結構規範
目錄的組織方式:在Stackoverflow的上,能看到你們對Python目錄結構的討論。點擊這裏
大概都會是這個樣子。
若是你想寫一個開源軟件,目錄該如何組織,能夠參考這篇文章。
能夠參考Redis源碼中Readme的寫法,這裏面簡潔可是清晰的描述了Redis功能和源碼結構。
通常來講,用來管理代碼的打包、安裝、部署問題。業界標準的寫法是用Python流行的打包工具setuptools來管理這些事情。
setuptools的文檔比較龐大,剛接觸的話,可能不太好找到切入點。學習技術的方式就是看他人是怎麼用的,能夠參考一下Python的一個Web框架,flask是如何寫的: setup.py
https://jeffknupp.com/blog/2013/08/16/open-sourcing-a-python-project-the-right-way/
https://github.com/antirez/redis#what-is-redis
https://github.com/pallets/flask/blob/master/setup.py
https://pip.readthedocs.io/en/1.1/requirements.htmlsetup.py
6 小結
當你陷入絕望的時候,那就放棄吧!
1 #===============>star.py 2 import sys,os 3 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 4 sys.path.append(BASE_DIR) 5 6 from core import src 7 8 if __name__ == '__main__': 9 src.run() 10 #===============>settings.py 11 import os 12 13 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 14 DB_PATH=os.path.join(BASE_DIR,'db','db.json') 15 LOG_PATH=os.path.join(BASE_DIR,'log','access.log') 16 LOGIN_TIMEOUT=5 17 18 """ 19 logging配置 20 """ 21 # 定義三種日誌輸出格式 22 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \ 23 '[%(levelname)s][%(message)s]' #其中name爲getlogger指定的名字 24 simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' 25 id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s' 26 27 # log配置字典 28 LOGGING_DIC = { 29 'version': 1, 30 'disable_existing_loggers': False, 31 'formatters': { 32 'standard': { 33 'format': standard_format 34 }, 35 'simple': { 36 'format': simple_format 37 }, 38 }, 39 'filters': {}, 40 'handlers': { 41 #打印到終端的日誌 42 'console': { 43 'level': 'DEBUG', 44 'class': 'logging.StreamHandler', # 打印到屏幕 45 'formatter': 'simple' 46 }, 47 #打印到文件的日誌,收集info及以上的日誌 48 'default': { 49 'level': 'DEBUG', 50 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 51 'formatter': 'standard', 52 'filename': LOG_PATH, # 日誌文件 53 'maxBytes': 1024*1024*5, # 日誌大小 5M 54 'backupCount': 5, 55 'encoding': 'utf-8', # 日誌文件的編碼,不再用擔憂中文log亂碼了 56 }, 57 }, 58 'loggers': { 59 #logging.getLogger(__name__)拿到的logger配置 60 '': { 61 'handlers': ['default', 'console'], # 這裏把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕 62 'level': 'DEBUG', 63 'propagate': True, # 向上(更高level的logger)傳遞 64 }, 65 }, 66 } 67 68 69 #===============>src.py 70 from conf import settings 71 from lib import common 72 import time 73 74 logger=common.get_logger(__name__) 75 76 current_user={'user':None,'login_time':None,'timeout':int(settings.LOGIN_TIMEOUT)} 77 def auth(func): 78 def wrapper(*args,**kwargs): 79 if current_user['user']: 80 interval=time.time()-current_user['login_time'] 81 if interval < current_user['timeout']: 82 return func(*args,**kwargs) 83 name = input('name>>: ') 84 password = input('password>>: ') 85 db=common.conn_db() 86 if db.get(name): 87 if password == db.get(name).get('password'): 88 logger.info('登陸成功') 89 current_user['user']=name 90 current_user['login_time']=time.time() 91 return func(*args,**kwargs) 92 else: 93 logger.error('用戶名不存在') 94 95 return wrapper 96 97 @auth 98 def buy(): 99 print('buy...') 100 101 @auth 102 def run(): 103 104 print(''' 105 1 購物 106 2 查看餘額 107 3 轉帳 108 ''') 109 while True: 110 choice = input('>>: ').strip() 111 if not choice:continue 112 if choice == '1': 113 buy() 114 115 116 117 #===============>db.json 118 {"egon": {"password": "123", "money": 3000}, "alex": {"password": "alex3714", "money": 30000}, "wsb": {"password": "3714", "money": 20000}} 119 120 #===============>common.py 121 from conf import settings 122 import logging 123 import logging.config 124 import json 125 126 def get_logger(name): 127 logging.config.dictConfig(settings.LOGGING_DIC) # 導入上面定義的logging配置 128 logger = logging.getLogger(name) # 生成一個log實例 129 return logger 130 131 132 def conn_db(): 133 db_path=settings.DB_PATH 134 dic=json.load(open(db_path,'r',encoding='utf-8')) 135 return dic 136 137 138 #===============>access.log 139 [2017-10-21 19:08:20,285][MainThread:10900][task_id:core.src][src.py:19][INFO][登陸成功] 140 [2017-10-21 19:08:32,206][MainThread:10900][task_id:core.src][src.py:19][INFO][登陸成功] 141 [2017-10-21 19:08:37,166][MainThread:10900][task_id:core.src][src.py:24][ERROR][用戶名不存在] 142 [2017-10-21 19:08:39,535][MainThread:10900][task_id:core.src][src.py:24][ERROR][用戶名不存在] 143 [2017-10-21 19:08:40,797][MainThread:10900][task_id:core.src][src.py:24][ERROR][用戶名不存在] 144 [2017-10-21 19:08:47,093][MainThread:10900][task_id:core.src][src.py:24][ERROR][用戶名不存在] 145 [2017-10-21 19:09:01,997][MainThread:10900][task_id:core.src][src.py:19][INFO][登陸成功] 146 [2017-10-21 19:09:05,781][MainThread:10900][task_id:core.src][src.py:24][ERROR][用戶名不存在] 147 [2017-10-21 19:09:29,878][MainThread:8812][task_id:core.src][src.py:19][INFO][登陸成功] 148 [2017-10-21 19:09:54,117][MainThread:9884][task_id:core.src][src.py:19][INFO][登陸成功]