python3學習筆記:零碎知識點

  • break語句可以在循環過程中直接退出循環,而continue語句可以提前結束本輪循環,並直接開始下一輪循環。這兩個語句通常都必須配合if語句使用。
  • list:Python內置的一種數據類型叫列表。list是一種有序的集合,可以隨時添加和刪除其中的元素。格式:[ ]
    • 要刪除指定位置的元素,用pop(i)方法,其中i是索引位置:
    • 可以獲取倒數第2個、倒數第3個L[-2]、L[-3]
  • tuple:另一種有序列表叫元組。tuple和list非常類似,但是tuple一旦初始化就不能修改。格式:( )
    • tuple所謂的“不變”是說,tuple的每個元素,指向永遠不變。即指向’a’,就不能改成指向’b’,指向一個list,就不能改成指向其他對象,但指向的這個list本身是可變的!
    • Python規定,只有1個元素的tuple定義時必須加一個逗號,,來消除歧義: t = (1,)
  • dict:字典,dict全稱dictionary,在其他語言中也稱爲map,使用鍵-值(key-value)存儲,具有極快的查找速度。格式:{ }
    • dict的存儲不是按照list的方式順序排列,迭代出的結果順序很可能不一樣
    • 和list比較,dict有以下幾個特點:
      • 查找和插入的速度極快,不會隨着key的增加而變慢;
      • 需要佔用大量的內存,內存浪費多。
    • 而list相反:
      • 查找和插入的時間隨着元素的增加而增加;
      • 佔用空間小,浪費內存很少。
    • 所以,dict是用空間來換取時間的一種方法。
  • set:set和dict類似,也是一組key的集合,但不存儲value。格式{ }
    • 要創建一個set,需要提供一個list作爲輸入集合:s = set([1, 2, 3])
    • 易錯點:
      • a = ‘abc’
      • b = a.replace(‘a’, ‘A’)
      • 我們調用a.replace(‘a’,’A’)時,實際上調用方法replace是作用在字符串對象’abc’上的,而這個方法雖然名字叫replace,但卻沒有改變字符串’abc’的內容。相反,replace方法創建了一個新字符串’Abc’並返回,如果我們用變量b指向該新字符串,就容易理解了,變量a仍指向原有的字符串’abc’,但變量b卻指向新字符串’Abc’了
      • 所以,對於不變對象來說,調用對象自身的任意方法,也不會改變該對象自身的內容。相反,這些方法會創建新的對象並返回,這樣,就保證了不可變對象本身永遠是不可變的。
  • 函數多樣化的參數:
    • 默認參數一定要用不可變對象,如果是可變對象,程序運行時會有邏輯錯誤!
    • 要注意定義可變參數和關鍵字參數的語法:
      • *args是可變參數,args接收的是一個tuple;
      • **kw是關鍵字參數,kw接收的是一個dict。
    • 以及調用函數時如何傳入可變參數和關鍵字參數的語法:
      • 可變參數既可以直接傳入:func(1,2,3),又可以先組裝list或tuple,再通過* args傳入:func(*(1, 2, 3));
      • 關鍵字參數既可以直接傳入:func(a=1,b=2),又可以先組裝dict,再通過* kw傳入:func(*{‘a’: 1, ‘b’: 2})。
    • 使用* args和** kw是Python的習慣寫法,當然也可以用其他參數名,但最好使用習慣用法。
    • 命名的關鍵字參數是爲了限制調用者可以傳入的參數名,同時可以提供默認值。
    • 定義命名的關鍵字參數在沒有可變參數的情況下不要忘了寫分隔符*,否則定義的將是位置參數。
  • 切片:
    • L[a:b:c]:在list L中[a,b)的元素中每隔c個元素取一個組成新的list並返回
      • a不寫默認是list首,b不寫默認是list尾,a、b均不寫,代表整個list
    • 倒數切片L[-a:-b:c]:在list L中[-a,-b]的元素中每隔c(步長)個元素取一個組成新的list並返回,注意:倒數第一個是-1
    • 步長c不能爲0,但是可以爲負數,即從右到左提取元素。使用一個負數作爲步長時,必須讓開始點(開始索引)大於結束點。
    • eg:numbers = [1,2,3,4,5,6,7,8,9,10]
    • print(numbers[6:3:-1]) 輸出:[7, 6, 5]
    • tuple( )和string‘ ’也支持slice切片操作,切片後返回的是tuple( )和string‘ ’
  • 迭代iterable:
  • 如何判斷一個對象是可迭代對象呢?方法是通過collections模塊的Iterable類型判斷
    • from collections import Iterable
    • isinstance(‘abc’,Iterable) #str是否可以迭代
  • 對list實現類似Java那樣的下標循環怎麼辦?
    • Python內置的enumerate函數可以把一個list變成索引-元素對,這樣就可以在for循環中同時迭代索引和元素本身:
    • for i,j in enumerate([1,2,3]):
    • print(i,j)
  • 生成器generator:
  • 在Python中,這種一邊循環一邊計算的機制,稱爲生成器:generator
  • 只要把一個列表生成式的[]改成(),就創建了一個generator
  • 可以直接打印出list的每一個元素,但generator函數的“調用”實際返回一個generator對象,要打印出generator的每一個元素:
    • 通過next()函數獲得generator的下一個返回值
    • 使用for循環,generator可以迭代
  • 如果一個函數定義中包含yield關鍵字,那麼這個函數就不再是一個普通函數,而是一個generator
  • eg:打印楊輝三角
  • def triangles:
    • L = [1]
    • while True:
      • yield L
      • L.append(0)
      • L = [L[i-1] + L[i] for i in range(len(L))]
  • 代器Iterator:
  • Python的Iterator對象表示的是一個數據流,Iterator對象可以被next()函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。
  • 可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個數據,所以Iterator的計算是惰性的,只有在需要返回下一個數據時它纔會計算。
  • Iterator甚至可以表示一個無限大的數據流,例如全體自然數。
  • 凡是可作用於for循環的對象都是Iterable類型;
  • 凡是可作用於next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列
  • 集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。
  • 迭代器和生成器的區別:
  • 迭代器有兩個基本的方法
    • next方法:返回迭代器的下一個元素
    • iter方法:返回迭代器對象本身
  • 生成器使用yield方法保持迭代器的高效
  • 函數式編程
  • 函數式編程的一個特點就是,允許把函數本身作爲參數傳入另一個函數,還允許返回一個函數!
  • Python對函數式編程提供部分支持。由於Python允許使用變量,因此,Python不是純函數式編程語言。
  • 把函數作爲參數傳入,這樣的函數稱爲高階函數,函數式編程就是指這種高度抽象的編程範式。
  • map/reduce
  • map()函數接收兩個參數,一個是函數,一個是Iterable,map將傳入的函數依次作用到序列的每個元素,並把結果作爲新的Iterator返回。
    • eg: list = [map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])]
  • reduce把一個函數作用在一個序列[x1, x2, x3, …]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
    • eg: reduce(add, [1, 3, 5, 7, 9]) 累加
  • filter()函數用於過濾序列
  • filter()也接收一個函數和一個序列,把傳入的函數依次作用於每個元素,然後根據返回值是True還是False決定保留還是丟棄該元素。
  • filter()函數返回的是一個Iterator,也就是一個惰性序列,所以要強迫filter()完成計算結果,需要用list()函數獲得所有結果並返回list。
  • eg: list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
  • sorted
  • sorted(List,key=,reverse=False)函數是一個高階函數,接收一個key函數來實現自定義的排序,reverse默認爲False,即默認升序
  • key指定的函數將作用於list的每一個元素上,並根據key函數返回的結果進行排序。
  • eg:sorted([‘bob’, ‘about’, ‘Zoo’, ‘Credit’], key=str.lower, reverse=True)
  • 輸出:[‘Zoo’, ‘Credit’, ‘bob’, ‘about’] 反向排序
  • 閉包(詳見博客)
  • 裝飾器(詳見博客)
  • 偏函數
    • Python的functools模塊提供了很多有用的功能,其中一個就是偏函數(Partial function)。
    • 當函數的參數個數太多,需要簡化時,使用functools.partial可以創建一個新的函數,這個新函數可以固定住原函數的部分參數,從而在調用時更簡單。
    • 創建偏函數時,實際上可以接收函數對象、*args和**kw這3個參數:
      • eg:int2 = functools.partial(int, base=2)
      • int2(‘10010’)
      • 相當於:
      • kw = { ‘base’: 2 }
      • int(‘10010’, **kw)
      • eg:max2 = functools.partial(max, 10)
      • max2(5, 6, 7)
      • 相當於:
      • args = (10, 5, 6, 7)
      • max(*args)
  • 面向對象
  • 如果要讓內部屬性不被外部訪問,可以把屬性的名稱前加上兩個下劃線,在Python中,實例的變量名如果以開頭,就變成了一個私有變量(private),只有內部可以訪問,外部不能訪問
  • 在Python中,變量名類似xxx的,也就是以雙下劃線開頭,並且以雙下劃線結尾的,是特殊變量,特殊變量是可以直接訪問的,不是private變量
  • 有些時候,你會看到以一個下劃線開頭的實例變量名,比如_name,這樣的實例變量外部是可以訪問的,但是,按照約定俗成的規定,當你看到這樣的變量時,意思就是,“雖然我可以被訪問,但是,請把我視爲私有變量,不要隨意訪問”。
  • python中可以這樣寫:1<=score<=100,這在其他很多語言中都是不允許的,入java
  • 雙下劃線開頭並以雙下劃線結尾的變量是特殊變量,不是private的,可以直接訪問
  • 單個下劃線開頭的變量雖然可以直接訪問,但是它隱含了“不要隨便訪問我”的意思
  • __name雖然是private的變量,但是依然可以在外部以_Student_name的形式訪問到
  • bart.__name = ‘Chen’ 在外部也可以正常執行,但是和想象中的不是一回事,這時候其實是又定義了新的變量__name
  • python(動態語言)的“鴨子”類型特點:
  • C/C++這樣的靜態語言定義函數的時候是需要顯式地申明參數類型,既然限定了參數類型自然也限定了參數在函數內的行爲,因爲錯誤的參數類型在傳入的當下就已經被拒絕接受了。
  • 然而python這樣的動態語言函數的參數從來都沒有明確申明,因此你傳入任何類型,python函數都能接住,不過接住以後函數調用的時候發現該參數並沒有某行爲的話纔會拋出異常,因此只要函數內部調用的行爲只要傳入參數具備就行。
  • 簡而言之,靜態語言傳參錯誤,是不會進入函數調用階段;而動態語言是接收後使用參數的過程中發現錯誤拋出異常。
  • 屬性與方法
  • 判斷基本類型的對象類型,使用type()函數
  • type()函數既可以返回一個對象的類型,又可以創建出新的類型
    • def fn(self, name=’world’): # 先定義函數
    • print(‘Hello, %s.’ % name)
    • Hello = type(‘Hello’, (object,), dict(hello=fn)) # 創建Hello class
    • type()函數依次傳入3個參數:
    • class的名稱;
    • 繼承的父類集合,注意Python支持多重繼承,如果只有一個父類,別忘了tuple的單元素寫法;
    • class的方法名稱與函數綁定,這裏把函數fn綁定到方法名hello上。
  • 判斷class的類型,可以使用isinstance()函數,isinstance()判斷的是一個對象是否是該類型本身,或者位於該類型的父繼承鏈上。
  • 如果要獲得一個對象的所有屬性和方法,可以使用dir()函數,它返回一個包含字符串的list
  • 通過callable()函數,可以判斷一個對象是否是“可調用”對象
  • _slots_
  • slot是限制的是實例對象的屬性的添加,而不是類的屬性
  • 類屬性是公共屬性,所有實例都可以引用的,前提是實例自身沒有同名的屬性,因此類屬性不能隨意更改(別的實例可能在引用類屬性的值),就是說不能隨便用a.set_age()更改age的值(因爲調用此方法更改的是類屬性age的值,不是實例a自身的age屬性值)
  • 使用slots要注意,slots定義的屬性僅對當前類實例起作用,對繼承的子類是不起作用的
  • 給實例綁定一個方法
    • from types import MethodType
    • s.set_age = MethodType(set_age, s) # 給實例綁定一個方法
  • 給所有實例都綁定方法,即給class綁定方法:
    • def set_score(self, score):
      • self.score = score
    • Student.set_score = set_score # 給class綁定方法
  • @property 詳見博客
  • Python允許使用多重繼承,採用MIxIn設計模式(類名後加上MixIn標記)
    • eg:ForkingMixIn(多進程模型)、ThreadingMixIn(多線程模型)
  • metaclass(元類)改變類創建時的行爲,一般用於創建API,詳見博客
  • 調試與錯誤
  • raise語句如果不帶參數,就會把當前錯誤原樣拋出(向上拋)
  • pdb調試:python -m pdb err.py
    • l(l:list) 顯示整個.py文件
    • n 單步執行
    • p + 變量名 查看變量名
    • c 繼續執行程序
    • q 退出pdb
  • python -m unittest mydict_test 在命令行通過參數-m unittest直接運行單元測試
  • Python內置的“文檔測試”(doctest)模塊可以直接提取註釋中的代碼並執行測試。
    • doctest嚴格按照Python交互式命令行的輸入和輸出來判斷測試結果是否正確。
    • 只有測試異常的時候,可以用…表示中間一大段煩人的輸出。
    • 當模塊正常導入時,doctest不會被執行。
    • 只有在命令行直接運行且自定義的doctest部分有問題,才執行doctest。
    • 所以,不必擔心doctest會在非測試環境下執行。
  • 文件讀寫
  • r是原始字符串操作符,寫在字符串的前邊,表示字符串內的所有字符都按原始意思解釋。
    • 如果這裏不加r,地址當中的這個符號“\”必須寫成轉義字符的形式,也就是’\’
    • 總結下就是:’C:\’ 和 r’C:\’ 輸出是一樣的
  • 在Linux和OS X中,文件路徑地址是“/”;在Windows系統中,文件路徑要使用反斜槓“\”
  • 操作文件和目錄
  • os.name 操作系統類型:windows下是‘nt’;Linux、unix等是‘posix’
  • os.environ 所有環境變量
    • 獲取某個具體的環境變量:os.environ.get(‘path’)
  • os.path.abspath(‘.’) 當前目錄的絕對路徑
  • 把兩個路徑合成一個,通過os.path.join()函數,這樣可以正確處理不同操作系統的路徑分隔符
  • 拆分路徑時,要通過os.path.split()函數,把一個路徑拆分爲兩部分,後一部分總是最後級別的目錄或文件名
  • os.path.splitext()可以直接讓你得到文件擴展名(第二部分是文件擴展名)
  • 序列化與反序列化
  • 將python對象序列化爲json對象
    • json.dumps()方法返回一個str,內容就是標準的JSON。
      • 例如:
      • import json
      • d = dict(name=’Bob’, age=20, score=88)
      • json.dumps(d)
      • 輸出:’{“age”: 20, “score”: 88, “name”: “Bob”}’
      • JSON語法規定key(age、score、name)必須要用雙引號
    • json.dump()方法可以直接把JSON寫入一個file-like Object。
  • 將json對象反序列化爲python對象
    • 最常用:json.loads()把JSON的字符串反序列化爲python對象
      • 例如:
      • json_str = ‘{“age”: 20, “score”: 88, “name”: “Bob”}’
      • json.loads(json_str)
      • 輸出:{‘age’: 20, ‘score’: 88, ‘name’: ‘Bob’}
    • json.load(),從file-like Object中讀取字符串並反序列化爲python對象
  • json.dumps(s, default=lambda obj: obj.dict)將任意對象序列化爲json對象
  • 進程和線程
  • 多進程multiprocessing
    • 多進程通信 Pipe、Queue
    • process.start()、process.join()、process.terminate()
    • 獲取cpu核數:queue.put()、queue.get()
    • multiprocessing.cpu_count()
  • 多線程threading
    • Python的threading模塊有個current_thread()函數,它返回當前線程的實例
    • 多線程不加鎖同時修改一個全局變量,會由於線程中斷出現錯誤
    • 解決方案:但加鎖後,多線程相當於轉變成單線程,而且由於加鎖操作使效率上:多線程<單線程
    • 通過threading.Lock()創建一個鎖;
    • 執行前獲取鎖:lock.acquire();
    • 執行完,釋放鎖lock.release()
    • Python的線程雖然是真正的線程,但解釋器執行代碼時,有一個GIL鎖:Global Interpreter Lock,任何Python線程執行前,必須先獲得GIL鎖,然後,每執行100條字節碼,解釋器就自動釋放GIL鎖,讓別的線程有機會執行。這個GIL全局鎖實際上把所有線程的執行代碼都給上了鎖,所以,多線程在Python中只能交替執行,即使100個線程跑在100核CPU上,也只能用到1個核。
    • Python解釋器由於設計時有GIL全局鎖,導致了多線程無法利用多核。多線程的併發在Python中就是一個美麗的夢。
    • IO操作不鎖定GIL,這時其他線程可以正常執行
    • Python雖然不能利用多線程實現多核任務,但可以通過多進程實現多核任務。多個Python進程有各自獨立的GIL鎖,互不影響。
  • ThreadLocal
    • 一個ThreadLocal變量雖然是全局變量,但每個線程都只能讀寫自己線程的獨立副本,互不干擾。
    • 把threadlocal看成全局dict,每個線程用自己作爲key訪問value,所以互不干擾
  • 進程VS線程
    • 多進程下,windows下創建進程代價大(linux、unix下有fork,windows沒有)
    • 多線程下,任意一個線程掛掉,會導致整個進程崩潰(所有線程共享進程的內存),即穩定性不夠
    • 計算密集型任務VSIO密集型任務
    • 計算密集型任務的特點是要進行大量的計算,消耗CPU資源。這種計算密集型任務雖然也可以用多任務完成,但是任務越多,花在任務切換的時間就越多,CPU執行任務的效率就越低,所以,要最高效地利用CPU,計算密集型任務同時進行的數量應當等於CPU的核心數。
    • IO密集型,涉及到網絡、磁盤IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因爲IO的速度遠遠低於CPU和內存的速度)。對於IO密集型任務,任務越多,CPU效率越高。
    • 異步IO
  • 分佈式進程(詳見博客)

    • 用multiprocessing.managers裏的BaseManager來通過網絡進行分佈式通信
  • 正則表達式:re模塊

  • \d 匹配一個數字
    • eg:’00\d’可以匹配’007’
  • \w 匹配一個數字或字母
    • eg:’\w\w\d’可以匹配’py3’
  • . 匹配任意單個字符
    • eg:’py.’可以匹配’pyc’、’pyo’、’py!’等等
  • *匹配任意個字符 (包括0個)
  • +匹配至少一個字符
  • ?匹配0個或1個字符
  • {n} 匹配n個字符;{n,m} 匹配n-m個字符
  • \s 匹配一個空格(也包括Tab等空白符)
    • eg:\s+ 匹配至少一個空格
  • []表示範圍
    • eg:[0-9a-zA-Z_]可以匹配一個數字、字母或者下劃線
  • ^表示行的開頭,^\d表示必須以數字開頭
  • \d 表示必須以數字結束
  • re模塊
  • 使用Python的r前綴,就不用考慮轉義(即字符串中有特殊意義的字符:\n、\t等)
  • re.match()方法判斷是否匹配,匹配成功,返回一個Match對象,否則返回None
  • 可以使用正則表達式切分字符串
    • eg:re.split(r’\s+’, ‘a b c’)
    • [‘a’, ‘b’, ‘c’]
  • 正則表達式還有提取子串的強大功能:用()表示的就是要提取的分組(Group)
  • 注意:group(0)永遠是原始字符串,group(1)、group(2)……表示第1、2、……個子串
    • eg:匹配區號和號碼 m = re.match(r’^(\d{3})-(\d{3,8})$’, ‘010-12345’)
  • 正則匹配默認是貪婪匹配,也就是匹配儘可能多的字符(加上?改爲非貪婪匹配)
    • eg: re.match(r’^(\d+)(0*)$’, ‘102300’).groups()
    • (‘102300’, ”)
    • \d+採用貪婪匹配,直接把後面的0全部匹配了,結果0*只能匹配空字符串了
    • 讓\d+採用非貪婪匹配(也就是儘可能少匹配),才能把後面的0匹配出來,加個?即可
    • re.match(r’^(\d+?)(0*)$’, ‘102300’).groups()
    • (‘1023’, ‘00’)
  • 預編譯正則表達式
  • re.compile(reg_expression) 調用對應的方法時不用給出正則字符串
  • 網絡編程
  • TCP(可靠連接)
  • socket.SOCK_STREAM面向TCP
  • 服務器端(多線程處理多個客戶端連接)+socket+客戶端
  • 服務器端:主要用到 socket.bind、socket.listen、socket.accept、socket.send、socket.recv、socket.close
  • 客戶端:主要用到socket.connect、socket.send、socket.recv
  • UDP(無連接)
  • socket.SOCK_DGRAM面向UDP
  • 服務器端+socket+客戶端+無連接,類似於TCP
  • 訪問數據庫
  • SQLite
  • sqlite驅動:sqlite3,Python已經封裝好了,直接引用即可
  • 主要用到連接sqlite3.connect和遊標conn.cursor
  • 通過遊標cursor執行sql語句:cursor.execute()
  • 執行insert等操作後要調用commit()提交事務
  • 佔位符是?
  • 用完記得關閉遊標cursor和連接conn
  • MySQL
  • mysql驅動:pymysql
  • eg:pymysql.connect(host=’127.0.0.1’, port=3306, user=’root’, passwd=”, db=’tkq1’, charset=’utf8’)
  • 佔位符是%s
  • mysql連接connect時需要用戶名、密碼連接,其他操作同上
  • SQLAlchemy(暫不深究)
  • ORM技術:Object-Relational Mapping,把關係數據庫的表結構映射到對象上
  • 在Python中,最有名的ORM框架是SQLAlchemy
  • HTTP
  • 當遇到連續兩個\r\n時,Header部分結束,後面的數據全部是Body
  • WSGI:Web Server Gateway Interface
  • 寫一個WSGI的處理函數,針對每個HTTP請求進行響應
  • eg: def application(environ,start_response):
    • start_response(‘200 OK’,[(‘Content-Type’,’text/html’)])
    • body = ‘< h1>hello,%s!< /h1>’ % (environ[‘PATH_INFO’][1:] or ‘web’)
    • return [body.encode(‘utf-8’)]
  • 導入wsgiref模塊裏的make_server,並監聽HTTP請求:serve_forever()
  • web框架Flask
  • 前端模板jinja2
  • 在Jinja2模板中,我們用{{ name }}表示一個需要替換的變量。
  • 循環、條件判斷等指令語句,在Jinja2中,用{% … %}表示指令。
  • 使用flask中的render_template調用模板,實現MVC模式
  • 異步IO
  • 協程:異步併發,不同於多線程,在一個線程裏,不需要切換線程;不同於多進程,不需要鎖;一般與多進程配合實現多cpu+高效率
  • 包文件:asyncio
  • asyncio提供的@asyncio.coroutine可以把一個generator標記爲coroutine類型,然後在coroutine內部用yield from調用另一個coroutine實現異步操作
  • yield from VS yield
    • yield from + 協程任務
    • 將協程任務的值包含到當前協程任務的返回值裏,即可以連接兩個協程任務的值
  • 核心方法:asyncio.get_event_loop()、asyncio.run_until_complete()、asyncio.close()、
  • asyncio.wait()(將多個任務進程封裝成一個新的協程任務並返回)
  • async/await
  • python3.5 加入了async、await來幫助asyncio簡化異步過程
  • 即async替代@asyncio.coroutine;await替代yield from即可
  • aiohttp
  • asyncio實現了TCP、UDP、SSL等協議,aiohttp則是基於asyncio實現的HTTP框架
  • 15天項目實戰
  • day1:搭建開發環境&&同步到github倉庫
  • day2:利用aiohttp搭建web骨架
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章