動態型(運行期肯定類型,靜態型是編譯型肯定類型),強類型(不發生隱式轉換,弱類型,如PHP,JavaScript就會發生隱患式轉換)html
優勢:前端
膠水語言,輪子多,應用普遍;語言靈活,生產力高node
缺點:python
性能問題,代碼維護問題,python2/3不兼容問題mysql
「當一隻鳥走起來像鴨子,游泳像鴨子,叫起來像鴨子,那麼這隻鳥就能被稱爲鴨子」nginx
關注點在對象的行爲,而不是類型;程序員
如file,StringIO,socket都支持read/write方法(file類型)web
定義了__iter__就能夠用for迭代redis
就是運行時替換算法
is是判斷是否爲同一個對象(id內存地址是否相同),==是判斷值是否相同
Tim Peters編寫的關於Python編程的規則;
import this查看,編程拿不許能夠查看:
優美勝於醜陋(Python 以編寫優美的代碼爲目標)
明瞭勝於晦澀(優美的代碼應當是明瞭的,命名規範,風格類似)
簡潔勝於複雜(優美的代碼應當是簡潔的,不要有複雜的內部實現)
複雜勝於凌亂(若是複雜不可避免,那代碼間也不能有難懂的關係,要保持接口簡潔)
扁平勝於嵌套(優美的代碼應當是扁平的,不能有太多的嵌套)
間隔勝於緊湊(優美的代碼有適當的間隔,不要奢望一行代碼解決問題)
可讀性很重要(優美的代碼是可讀的)
即使假借特例的實用性之名,也不可違背這些規則(這些規則至高無上)
不要包容全部錯誤,除非你肯定須要這樣作(精準地捕獲異常,不寫 except:pass 風格的代碼)
當存在多種可能,不要嘗試去猜想
而是儘可能找一種,最好是惟一一種明顯的解決方案(若是不肯定,就用窮舉法)
雖然這並不容易,由於你不是 Python 之父(這裏的 Dutch 是指 Guido )
作也許好過不作,但不假思索就動手還不如不作(動手以前要細思量)
若是你沒法向人描述你的方案,那確定不是一個好方案;反之亦然(方案測評標準)
命名空間是一種絕妙的理念,咱們應當多加利用(倡導與號召)
Python3改進:
print改成函數,能夠指定sep參數;
編碼問題:Python3再也不有Unicode對象,默認str就是Unicode
除法變化,Python3除號(/)返回浮點數,Python2向下取整,Python3向下取整爲(//)
類型註解(type hint)。幫助IDE實現類型檢查;
主要是提示做用,檢查能夠用mypy包
優化的super()函數方便直接調用父類函數;
高級解包操做:a,b,*rest=range(10)
限定關鍵字參數(Keyword only arguments),防止把數據搞混
Chained exceptions。Python3從新拋出異常不會丟失棧信息(有利於排錯),raise from保留棧信息(raise NotImplementedError("haha") from OSError)
一切返回迭代器range,zip,map,dict.values,etc(節省內存)
生成的pyc文件統一放到__pyache__;
一些內置庫的修改,如urllib,selector等;
性能優化等
yield from連接子生成器;
asyncio內置庫,async/await原生協程支持異步編程;
新的內置庫enum(枚舉),mock(單測),asyncio(異步),ipaddress(處理ip地址),concurent.futures等
six模塊;2to3等工具轉換代碼;__future__
傳遞值仍是引用?都不是,惟一支持的參數傳遞是共享傳參;
Call by Object(Call by Object Reference or Call by Sharing)
共享傳參,函數形參得到實參種各個引用的副本
ll指向[1,2,3],l也指向[1,2,3],後又指向[],因此ll仍爲[1,2,3],l爲[]
默認參數只計算一次
不可變對象:bool/int/float.str/frozenset;
可變對象:list/set/dict
用來處理可變參數,*args會被打包成tuple,**kwargs會打包成dict
Python使用異常處理錯誤(有些語言使用錯誤碼,如C)
BaseException(最低層異常)
SystemExit,Timeout(超時),KeyboradInterrupt(ctrl+c),GeneratorExit(生成器退出異常),繼承Exception(繼承BaseException)
網絡超時(超時,資源錯誤);
資源問題(權限問題,資源不存在)
代碼邏輯(越界訪問,KeyError等)
如何處理:
try: #fun #可能拋出異常的代碼塊 except (Exception1,Exception2) as e: #能夠捕獲多個異常並處理 #異常處理的代碼 else: #pass #異常沒有發生的時候代碼邏輯 finally: pass #不管異常有沒有發生都會執行的代碼,通常處理資源的關閉和釋放
經過繼承Exception實現定義異常(爲何不是BaseException,ctrl+c不能結束,BaseException是全部異常的基類,KeyboradInterrupt和SystemExit,GeneratorExit,Exception都繼承於它,若是繼承BaseException,可能捕獲不到一些異常。)
給異常加一些附加信息;
處理一些業務相關的特定異常(raise MyException)
Cpython解釋器的內存管理並非線程安全的;
保護多線程狀況下對Python對象的訪問;
Cpython使用簡單的鎖機制避免多個線程同時執行字節碼。
同一個時間只能有一個線程執行字節碼;
CPU密集程序難以利用多核優點;
IO期間會釋放GIL,對IO密集程序影響不大
CPU密集能夠用多進程+進程池;
IO密集使用多線程/協程
cython擴展
一個操做若是一個字節碼指令能夠完成就是原子的;
原子的是能夠保證線程安全的;
使用dis操做來分析字節碼。
使用profile工具(內置或第三方)
二八定律:大部分時間耗時在少許代碼上;
內置的profile/cprofile等工具;
使用pyflame(uber開源)的火焰圖工具
web應用通常語言不會成爲瓶頸:
數據結構與算法優化;
數據庫層:索引優化,慢查詢消除,批量操做減小IO,NoSql
網絡IO:批量操做,pipeline操做減小IO
緩存:使用內存數據庫 redis。memcached
異步:asyncio,celery
併發:gevent/多線程
生成器:
生成器就是能夠生成值的函數;
當一個函數裏有了yield關鍵字就成了生成器;
生成器能夠掛起並保持當前執行狀態
基於生成器的協程:
Python3以前沒有原生的協程,只有基於生成器的協程
pep342加強生成器功能;
生成器能夠經過yield暫停執行和產出數據;
同時支持end()向生成器發送數據和throw()向生成器拋異常
協程使用send(None)或者next(coroutine)來預激(prime)才能啓動;
在yield出協程會暫停執行;
單獨的yield value會產出值給調用方;
能夠經過coroutine.send(value)來給協程發送值,發送的值會賦值給yield表達式左邊的變量value=yield
協程執行完成後(沒有遇到下一個yield語句)會拋出StopIteration異常
避免每次都使用send預激它(from functool import wraps)@wraps裝飾器向前執行一個yield表達式,預激
Python3.5引入async/await支持原生協程
針對程序模塊進行正確性檢驗;
一個函數,一個類進行驗證;
自底向上保證程序正確性
三無代碼不可取(無文檔,無註釋,無單測)
保證代碼邏輯的正確性(甚至有些採用測試驅動開發(TDD))
單測影響設計,易測的代碼每每是高類聚低耦合的;
迴歸測試,防止一處修改整個服務不可用
nose/pytest較爲經常使用;
mock模塊用來模擬替換網絡請求等
coverage統計測試覆蓋率
深拷貝,包含對象裏面的自對象的拷貝,因此原始對象的改變不會形成深拷貝里任何子元素的改變。
淺拷貝至關於拷貝了對象的引用(在python中,對象賦值其實是對象的引用。當建立一個對象,而後把它賦給另外一個變量的時候,python並無拷貝這個對象,而只是拷貝了這個對象的引用)
namedtuple:讓tuple屬性可讀(賦予名字)
deque:雙端隊列(能夠在隊列先後操做,方便實現queue/statck)
Counter:實現計數的功能
OrderDict:能夠記住key第一次進入的位置(有序的)
defaultdict():鍵不存在時不會報錯(帶有默認值的字典)
dict低層使用的hash表:
爲了支持快速查找使用了哈希表做爲低層結構;
哈希表平均查找複雜度爲O(1);
CPython解釋器使用二次探查解決哈希衝突問題(如何解決哈希衝突,哈希擴容)
list vs tuple:
都是線性結構,支持下標訪問;
list是可變對象,tuple是保存的引用不可變(指的是無法替換掉這個對象,可是若是對像自己是一個可變對象,是能夠修改這個引用指向的可變對象的);
list無法做爲字典的key,tuple能夠(可變對象不可hash)
LRUCache:
Least-Recently-Used替換最少使用的對象
緩存剔除策略,當緩存空間不夠用的時候須要一種方式剔除key
常見的有LRU,LFU
LRU經過使用一個循環雙端隊列不斷把最新訪問的key放到表頭實現
實現:
字典利用緩存,循環雙端鏈表用來記錄訪問順序:
利用Python內置的dict+collections.OrderdDict實現;
dict用來看成k/v鍵值對的緩存;
OrderedDict用來實現最近訪問的key
實現代碼:
1 from collections import OrderedDict 2 3 class LRUCathe: 4 def __init__(self,capacity=120): 5 self.od=OrderedDict() 6 self.capacity=capacity 7 8 def get(self,key): 9 if key in self.od: 10 val=self.od[key] 11 self.od.move_to_end(key) 12 return val 13 else: 14 return -1 15 # 更新k/v 16 def put(self,key,value): 17 if key in self.od: 18 del self.od[key] 19 #更新key到表頭 20 self.od[key]=value 21 else: 22 self.od[key]=value 23 #判斷當前容量是否滿了 24 if len(self.od)>self.capacity: 25 self.od.popitem(last=False)
排序,查找等。。。。
排序的穩定性:
相同大小的元素排序以後保持相對的位置不變,就是穩定的。穩定性對於排序一個複雜的結構,而且保持原有排序纔有意義。
快排:(分治法)
選擇基準分割數組爲兩個字數組,小於基準和大於基準的;
對兩個子數組分別快排;
合併結果。
1 '''快速排序: 2 時間平均複雜度:O(nlog2n) 3 最好狀況:O(nlog2n) 4 最壞:O(n^2) 5 空間複雜度:O(nlog2n) 6 排序方式:內部排序 7 穩定性:不穩定 8 ''' 9 def quicksort(arr): 10 if len(arr)<2: 11 return arr 12 13 pivot_index=0 14 pivot=arr[pivot_index] 15 less_pivot=[i for i in arr[pivot_index+1:] if i <=pivot] 16 great_pivot=[i for i in arr[pivot_index+1:] if i > pivot] 17 return quicksort(less_pivot)+[pivot]+quicksort(great_pivot) 18 print(quicksort([3,4,5,6]))
歸併排序:
1 '''歸併排序: 2 時間平均複雜度:O(nlog2n) 3 最好狀況:O(nlog2n) 4 最壞:O(nlog2n) 5 空間複雜度:O(n) 6 排序方式:外部排序 7 穩定性:穩定 8 ''' 9 10 11 def mergeSort(arr): 12 import math 13 if len(arr)<2: 14 return arr 15 middle=math.floor(len(arr)/2) 16 left,right=arr[:middle],arr[middle:] 17 return merge(mergeSort(left),mergeSort(right)) 18 19 def merge(left,right): 20 result=[] 21 while left and right: 22 if left[0]<=right[0]: 23 result.append(left.pop(0)) 24 else: 25 result.append(right.pop(0)) 26 while left: 27 result.append(left.pop(0)) 28 while right: 29 result.append(right.pop(0)) 30 return result 31 if __name__=='__main__': 32 print(mergeSort([3,2,1,5,6,8,10,44,51,31,0,49]))
二分查找:
1 def binary_search(arr,val): 2 if not arr: 3 return -1 4 beg=0 5 end=len(arr)-1 6 while beg<=end: 7 mid=int((beg+end)/2) 8 if arr[mid]==val: 9 return mid 10 elif arr[mid]>val: 11 end=mid-1 12 else: 13 beg=mid+1 14 return -1 15 print(binary_search([1,3,5,7,0,12],12))
數據結構鏈表,隊列,棧,二叉樹,堆
使用內置結構實現數據結構,好比內置的list/deque實現棧
leetcode或者《劍指offer》常見題
鏈表有單鏈表,雙鏈表,循環雙端鏈表:
如何使用Python來表示鏈表結構;
實現鏈表常見操做,好比插入節點,反轉鏈表,合併多個鏈表;
Leetcode練習常見鏈表題目
反轉鏈表:(合併鏈表,求鏈表公共值)
1 # Definition for singly-linked list. 2 # class ListNode(object): 3 # def __init__(self, x): 4 # self.val = x 5 # self.next = None 6 7 class Solution(object): 8 def reverseList(self, head): 9 """ 10 :type head: ListNode 11 :rtype: ListNode 12 """ 13 pre=None 14 cur=head 15 while cur: 16 nextnode=cur.next 17 cur.next=pre 18 pre=cur 19 cur=nextnode 20 return pre
如何實現隊列;實現隊列的append和pop操做,如何作到先進先出;使用Python的list或者collections.deque實現隊列
1 from collections import deque 2 3 class Stack(object): 4 def __init__(self): 5 self.item=deque() 6 7 def push(self,value): 8 self.item.append(value) 9 10 def pop(self): 11 return self.item.pop() 12 13 s=Stack() 14 s.push(1) 15 s.push(2) 16 print(s.pop()) 17 print(s.pop())
Python的dict和set低層實現都是hash表:
hash表的低層實現原理其實就是數組;
根據哈希表能夠很快定位一個元素,平均查找爲O(1);
不斷加入元素會引發hash表重新開闢新空間,拷貝以前的元素到新空間
解決哈希衝突:
連接法:
元素key衝突以後使用一個鏈表填充相同key的元素;
開放尋址法:
衝突以後根據必定方式(二次探查)尋找下一個可用的槽
二叉樹:
先序(根->左->右),中序(左->根->右),後序(左->右->根)
獲取大量元素,固定內存:
思路:1.先放入元素前k個創建一個最小堆;2.迭代剩餘元素:若是當前元素小於棧頂元素,跳過該元素(確定不是前k大),不然替換棧頂元素爲當前元素,並重新調整堆
1 import heapq 2 3 class TopK: 4 def __init__(self,iterable,k): 5 self.minheap=[] 6 self.capacity=k 7 self.iterable=iterable 8 9 def push(self,val): 10 if len(self.minheap)>=self.capacity: 11 min_val=self.minheap[0] 12 if val<min_val: 13 pass 14 else: 15 #返回並替換pop堆頂最小值,插入新的值並調整堆 16 heapq.heapreplace(self.minheap,val) 17 else: 18 #前面k個元素直接放入minheap 19 heapq.heappush(self.minheap,val) 20 def get_topk(self): 21 for val in self.iterable: 22 self.push(val) 23 return self.minheap 24 25 tk=TopK([1,3,5,6,0,3,44,11,42,0,1,9999],4) 26 print(tk.get_topk())
刪除節點:
1 # Definition for singly-linked list. 2 # class ListNode(object): 3 # def __init__(self, x): 4 # self.val = x 5 # self.next = None 6 7 class Solution(object): 8 def deleteNode(self, node): 9 """ 10 :type node: ListNode 11 :rtype: void Do not return anything, modify node in-place instead. 12 """ 13 nextnode=node.next 14 after_node=node.next.next 15 node.val=nextnode.val 16 node.next=after_node
合併鏈表:
1 # Definition for singly-linked list. 2 # class ListNode(object): 3 # def __init__(self, x): 4 # self.val = x 5 # self.next = None 6 7 class Solution(object): 8 def mergeTwoLists(self, l1, l2): 9 """ 10 :type l1: ListNode 11 :type l2: ListNode 12 :rtype: ListNode 13 """ 14 root=ListNode(None) 15 cur=root 16 while l1 and l2: 17 if l1.val<l2.val: 18 node=ListNode(l1.val) 19 l1=l1.next 20 else: 21 node=ListNode(l2.val) 22 l2=l2.next 23 cur.next=node 24 cur=node 25 #l1和l2可能還有剩餘值 26 # if l1 is None: 27 # cur.next=l2 28 # else: 29 # cur.next=l1 30 cur.next = l1 or l2 31 return root.next
二叉樹:
二叉樹的鏡像(反轉):
1 # Definition for a binary tree node. 2 # class TreeNode: 3 # def __init__(self, x): 4 # self.val = x 5 # self.left = None 6 # self.right = None 7 8 class Solution: 9 def invertTree(self, root: TreeNode) -> TreeNode: 10 if root==None: 11 return root 12 else: 13 self.invertTree(root.left) 14 self.invertTree(root.right) 15 root.left,root.right = root.right,root.left 16 return root
層序遍歷二叉樹(廣度優先):
1 # Definition for a binary tree node. 2 # class TreeNode: 3 # def __init__(self, x): 4 # self.val = x 5 # self.left = None 6 # self.right = None 7 8 class Solution: 9 def levelOrder(self, root: TreeNode) -> List[List[int]]: 10 if root: 11 res=[] 12 cur_nodes=[root] 13 next_nodes=[] 14 res.append([i.val for i in cur_nodes]) 15 while cur_nodes or next_nodes: 16 for node in cur_nodes: 17 if node.left: 18 next_nodes.append(node.left) 19 if node.right: 20 next_nodes.append(node.right) 21 if next_nodes: 22 res.append([i.val for i in next_nodes]) 23 cur_nodes=next_nodes 24 next_nodes=[] 25 return res 26 else: 27 return []
棧實現隊列:
1 from collections import deque 2 class Stack(object): 3 def __init__(self): 4 self.item=deque() 5 6 def pop(self): 7 return self.item.pop() 8 def push(self,val): 9 self.item.append(val) 10 def empty(self): 11 return len(self.item)==0 12 13 def top(self): 14 return self.item[-1] 15 16 class MyQueue: 17 def __init__(self): 18 """ 19 Initialize your data structure here. 20 """ 21 self.s1=Stack() 22 self.s2=Stack() 23 24 def push(self, x: int) -> None: 25 """ 26 Push element x to the back of queue. 27 """ 28 self.s1.push(x) 29 30 def pop(self) -> int: 31 """ 32 Removes the element from in front of queue and returns that element. 33 """ 34 if not self.s2.empty(): 35 return self.s2.pop() 36 while not self.s1.empty(): 37 val=self.s1.pop() 38 self.s2.push(val) 39 return self.s2.pop() 40 41 def peek(self) -> int: 42 """ 43 Get the front element. 44 """ 45 if not self.s2.empty(): 46 return self.s2.top() 47 while not self.s1.empty(): 48 val = self.s1.pop() 49 self.s2.push(val) 50 return self.s2.top() 51 52 def empty(self) -> bool: 53 """ 54 :return: 55 """ 56 return self.s1.empty() and self.s2.empty()
堆:
合併多個有序數組;TopK問題。
堆是徹底二叉樹,有最大堆和最小堆。
使用heapq實現堆的操做;
合併k個有序鏈表
1 # Definition for singly-linked list. 2 # class ListNode: 3 # def __init__(self, x): 4 # self.val = x 5 # self.next = None 6 from heapq import heapify,heappop 7 8 class Solution: 9 def mergeKLists(self, lists: List[ListNode]) -> ListNode: 10 h=[] 11 for node in lists: 12 while node: 13 h.append(node.val) 14 node=node.next 15 if not h: 16 return None 17 #構建一個最小堆 18 heapify(h) 19 #構建鏈表 20 root=ListNode(heappop(h)) 21 curnode=root 22 while h: 23 nextnode=ListNode(heappop(h)) 24 curnode.next=nextnode 25 curnode=nextnode 26 return root 27 28 29
字符串:
翻轉字符串:
1 class Solution: 2 def reverseString(self, s: List[str]) -> None: 3 """ 4 Do not return anything, modify s in-place instead. 5 """ 6 beg=0 7 end=len(s)-1 8 while beg<end: 9 s[beg],s[end]=s[end],s[beg] 10 beg+=1
判斷一個數字是不是迴文數:
class Solution: def isPalindrome(self, x: int) -> bool: if x<0: return False s=str(x) beg,end=0,len(s)-1 while beg<end: if s[beg]==s[end]: beg+=1 end-=1 else: return False return True
把對象做爲基本單元,把對象抽象成類,包括成員和方法;
數據封裝,繼承,多態;
Python中使用類來實現。過程式編程(函數),OOP(類)
組合是使用其餘類的實例做爲本身的一個屬性;
子類繼承父類的屬性和方法(Is a關係);
優先使用組合保持代碼簡單。
類變量是全部實例共享;
實例變量由實例單獨享有,不一樣實例之間不影響;
當咱們須要在一個類的不一樣實例之間共享變量的時候使用類變量。
1 class Person: 2 Coutry='China' 3 def __init__(self,name): 4 self.name=name 5 def print_name(self): 6 print(self.name) 7 p=Person('laoliu') 8 p2=Person('laowang') 9 p.print_name() 10 p2.print_name()
4.classmethod和staicmethod的區別:
均可以經過Class.method()的方式使用;
classmethod第一個參數是cls,能夠引用類變量
staticmethod使用起來和普通函數同樣,只不過放在類裏去組織
注:classmethod是爲了使用類變量,staticmethod是代碼組織的須要,徹底能夠放在類以外
1 class Person: 2 Coutry='China' 3 def __init__(self,name): 4 self.name=name 5 @classmethod 6 def print_country(cls): 7 print(cls.Coutry) 8 @staticmethod 9 def join_name(first_name,last_name): 10 return last_name+first_name
元類是建立類的類。(元類容許咱們控制類的生成,好比修改類的屬性等);
使用type來定義類;
元類最多見的一個使用場景就是ORM框架
type:
1 #參數:類名,基類,屬性 2 class Base(): 3 pass 4 class Child(Base): 5 pass 6 #等價定義,注意是tuple 7 SameChild=type('Child',(Base,),{}) 8 #加上方法 9 class ChildWithMethod(Base): 10 bar=True 11 12 def hello(self): 13 print('hello') 14 #等價 15 ChiildWithMethod=type('ChiildWithMethod',(Base,),{'bar':True,'hello':hello})
元類的使用:
1 #元類繼承於type 2 class LowercaseMeta(type): 3 ''' 4 修改類的屬性名稱爲小寫的元類 5 ''' 6 def __new__(mcs,name,bases,attrs): 7 lower_attrs={} 8 for k,v in attrs.items(): 9 if not k.startswith('__'): 10 lower_attrs[k.lower()]=v 11 else: 12 lower_attrs[k]=v 13 return type.__new__(mcs,name,bases,lower_attrs) 14 class LowerCaseClass(metaclass=LowercaseMeta): 15 BAR=True 16 def HELLO(self): 17 print('hello') 18 print(dir(LowerCaseClass))
Python中一切皆對象,函數也能夠看成參數來傳遞;
裝飾器是接收函數做爲參數,添加功能後返回一個新函數的函數(類);
Python中經過@使用裝飾器(@是語法糖)
編寫一個記錄函數耗時的裝飾器
1 #編寫一個記錄函數耗時的裝飾器 2 import time 3 def log_time(func): 4 def _log(*args,**kwargs): 5 beg=time.time() 6 res=func(*args,**kwargs) 7 print('use time:{}'.format(time.time()-beg)) 8 return res 9 return _log 10 @log_time 11 def mysleep(): 12 time.sleep(1) 13 #相同 14 #mysleep=log_time(mysleep) 15 mysleep()
使用類編寫裝飾器:
1 class LogTime: 2 def __call__(self, func): 3 def _log(*args,**kwargs): 4 beg=time.time() 5 res=func(*args,**kwargs) 6 print('use time:{}'.format(time.time()-beg)) 7 return res 8 return _log 9 @LogTime() 10 def mysleep2(): 11 time.sleep(1)
使用類磚石砌比較方便實現裝飾器參數:
1 class LogTime: 2 def __init__(self,user_int=False): 3 self.user_int=user_int 4 def __call__(self, func): 5 def _log(*args,**kwargs): 6 beg=time.time() 7 res=func(*args,**kwargs) 8 if self.user_int: 9 print('use time:{}'.format(time.time()-beg)) 10 else: 11 print('use time:{}'.format(int(time.time() - beg))) 12 return res 13 return _log 14 @LogTime(True) 15 def mysleep2(): 16 time.sleep(1)
建立型:
工廠模式:解決對象建立問題;
構造模式:控制複雜對象的建立;
原型模式:經過原型的克隆建立新的實例;
單例模式:一個類只能建立同一個對象;
對象池模式:預先分配同一個類型的一組實例;
惰性計算模式:延遲計算
工廠模式:
解決對象建立問題;
解決對象的建立和使用;
包括工廠方法和抽象工廠
1 class DogToy: 2 def speak(self): 3 print('wang wang') 4 5 class CatToy: 6 def speak(self): 7 print('miao miao') 8 def toy_factory(toy_type): 9 if toy_type=='dog': 10 return DogToy() 11 elif toy_type=='cat': 12 return CatToy()
構造模式:
用來控制複雜對象的構造;
建立和表示分離。好比你要買電腦,工廠模式直接給你須要的電腦,可是構造模式容許你本身定義電腦的配置,組裝完成後給你
原型模式:
經過克隆原型來建立新的實例;
可使用相同的類型,經過修改部分屬性來建立新的實例;
用途:對於一些建立實例開銷比較高的地方可使用原型模式
單例模式:
一個類建立出來的對象都是同一個;
Python'的模塊其實就是單例的,只會導入一次;
使用共享同一個實例的方式來建立單例模式
構建型:
橋代理組合適配器,享元回家裝飾外觀。
裝飾器模式:
無需子類化就能夠擴展對象功能;
代理模式:
把一個對象的操做代理到另外一個對象;
前面實現的Stack/Queue,把操做代理到deque;
has-a組合關係;
還有就是實現一些安全相關的東西,好比一個比較裸露的接口,在校驗層能夠作一些校驗。
適配器模式:
經過一個間接層適配統一接口;
至關於多功能衝電頭,能夠給多個不一樣手機衝電;
1 from abc import abstractmethod, ABCMeta 2 3 4 class Payment(metaclass=ABCMeta): 5 @abstractmethod 6 def pay(self, money): 7 raise NotImplementedError 8 9 10 class Alipay(Payment): 11 def pay(self, money): 12 print("支付寶支付%s元"%money) 13 14 15 class ApplePay(Payment): 16 def pay(self, money): 17 print("蘋果支付%s元"%money) 18 19 #------待適配的類----- 20 class WeChatPay: 21 def fuqian(self,money): 22 print('微信支付%s元'%money) 23 24 #------類適配器------ 25 class RealWeChatPay(Payment,WeChatPay): 26 def pay(self, money): 27 return self.fuqian(money) 28 29 #-----對象適配器----- 30 class PayAdapter(Payment): 31 def __init__(self,payment): 32 self.payment=payment 33 def pay(self, money): 34 return self.payment.fuqian(money) 35 36 #RealWeChatPay().pay(100) 37 38 p=PayAdapter(WeChatPay()) 39 p.pay(200)
外觀模式:
簡化複雜對象的訪問問題;
享元模式:
經過對象複用(池)改善資源利用,好比鏈接池;
MVC模式:
解耦展現邏輯和業務邏輯;
行爲型:
訪問者寫好策略備忘錄,觀察模板迭代狀態,命令中介解釋責任鏈。
迭代器模式:經過統一的接口迭代對象
Python內置對迭代器模式的支持;
好比咱們能夠用for遍歷各類Iterable的數據類型;
實現__next__和__iter__實現迭代器
1 class LinkedList: 2 class Node: 3 def __init__(self,item=None): 4 self.item=item 5 self.next=None 6 class LinkedListIterator: 7 def __init__(self,node): 8 self.node = node 9 #實現next方法,返回下一個元素 10 def __next__(self): 11 if self.node: 12 cur_node = self.node 13 self.node = cur_node.next 14 return cur_node.item 15 16 def __iter__(self): 17 return self 18 def __init__(self,iterable=None): 19 self.head = LinkedList.Node(0) 20 self.tail = self.head 21 self.extend(iterable) 22 23 #鏈表尾部追加元素 24 def append(self,obj): 25 s = LinkedList.Node(obj) 26 self.tail.next = s 27 self.tail = s 28 #鏈表自動增長長度 29 def extend(self,iterable): 30 for obj in iterable: 31 self.append(obj) 32 self.head.item += len(iterable) 33 34 def __iter__(self): 35 return self.LinkedListIterator(self.head.next) 36 37 def __len__(self): 38 return self.head.item 39 40 def __str__(self): 41 return '<<'+', '.join(map(str,self)) + '>>' 42 43 li = [i for i in range(100)] 44 lk = LinkedList(li) 45 print(lk)
觀察者模式:對象發生改變的時候,觀察者執行相應的操做
發佈訂閱是一種最經常使用的實現方式;
發佈訂閱用於解耦邏輯;
能夠經過回調方式實現,當發生事件時,調用相應的回調函數
1 from abc import ABCMeta, abstractmethod 2 3 #抽象主題 4 class Oberserver(metaclass=ABCMeta): 5 @abstractmethod 6 def update(self): 7 pass 8 9 #具體主題 10 class Notice: 11 def __init__(self): 12 self.observers = [] 13 14 def attach(self,obs): 15 self.observers.append(obs) 16 17 def detach(self,obs): 18 self.observers.remove(obs) 19 20 def notify(self): 21 for obj in self.observers: 22 obj.update(self) 23 24 #抽象觀察者 25 class ManagerNotice(Notice): 26 def __init__(self,company_info=None): 27 super().__init__() 28 self.__company_info = company_info 29 30 @property 31 def company_info(self): 32 return self.__company_info 33 34 @company_info.setter 35 def company_info(self,info): 36 self.__company_info = info 37 self.notify() 38 39 #具體觀察者 40 class Manager(Oberserver): 41 def __init__(self): 42 self.company_info = None 43 def update(self,noti): 44 self.company_info = noti.company_info 45 46 #消息訂閱-發送 47 notice = ManagerNotice() 48 49 alex=Manager() 50 tony=Manager() 51 52 notice.attach(alex) 53 notice.attach(tony) 54 notice.company_info="公司運行良好" 55 print(alex.company_info) 56 print(tony.company_info) 57 58 notice.company_info="公司將要上市" 59 print(alex.company_info) 60 print(tony.company_info) 61 62 notice.detach(tony) 63 notice.company_info="公司要破產了,趕快跑路" 64 print(alex.company_info) 65 print(tony.company_info)
策略模式:針對不一樣規模使用不一樣的策略
根據不一樣的輸入採用不一樣的策略;
好比買東西超過10個打八折,超過20個打七折;
對外暴露統一的接口,內部採用不一樣的策略計算
把電腦運算視做數學上的函數計算(lambda計算)
高階函數:map/reduce/filter;
無反作用,相同的參數調用始終產生一樣的結果
print(list(map(lambda x:x*2,range(10)))) #求1到9和 reduce(lambda x,y:x+y,range(10)) #求10內偶數 filter(lambda x:x%2==0,range(10))
綁定了外部做用域的變量的函數;
即便程序離開外部做用域,若是閉包仍然可見,綁定變量不會銷燬;
每次運行外部函數都會從新組建閉包
1 from functools import wraps 2 3 def catche(func): #裝飾器 4 store={} 5 @wraps(func) 6 def _(n): #閉包函數 7 if n in store: 8 return store[n] 9 else: 10 res=func(n) 11 srore[n]=res 12 return res 13 return _ 14 15 @catche 16 def f(n): 17 if n<=1: 18 return 1 19 else: 20 return f(n-1)+f(n)
tldr:更簡潔(pip install tldr)
文件:chown/chmod/chgrp
ls/rm/mv/touch/rename/ln(軟硬鏈接)
locate/find/grep
編輯器 vi/nano
cat/head/tail查看文件
more/less交互式查看文件
ps查看進程,kill殺死,top/htop監控進程
free查看可用內存;
瞭解每一列含義,排查泄露
ipconfig/lsof/netstat/ssh/scp。tcpdump
useradd/usermod/grepadd/grepmod
進程是對運行程序的封裝,是系統資源調度和分配的基本單位;
線程是進程的子任務,cou調度和分配的基本單位,實現進程內併發;
一個進程能夠包含多個線程,線程依賴進程存在,並共享進程內存
Python中哪些是線程安全的:
一個操做能夠在多線程環境中安全使用,獲取正確的結果;
線程安全的操做比如線程是順序執行而不是併發執行的;
通常若是涉及到寫操做須要考慮如何讓多個縣城安全訪問數據
線程同步的方式:
互斥量(鎖):經過互斥機制防止多個線程同時訪問公共資源;
信號量:控制同一時刻多個線程同時訪問同一個資源的線程數
事件(信號):經過通知的方式保持多個線程同步
進程間通訊:
管道/匿名管道/有名管道(pipe)
信號:好比用戶ctrl+c產生SININT程序終止信號
消息隊列
共享內存;
信號量;
套接字:最經常使用的
分頁機制:操做系統爲了高效管理內存,減小碎片
把邏輯地址和物理地址分離的內存分配管理方案:
程序的邏輯地址劃分爲固定大小的頁;
物理地址劃分爲一樣大小的幀;
經過頁表對應邏輯地址和物理地址
分段機制:分段是爲了知足代碼的一些邏輯需求
數據共享,數據保護,動態連接等
經過段表實現邏輯地址和物理地址的映射關係;
每一個段內部是內存分配,段和段之間是離散分配的
分頁和分段的區別:
頁是出於內存利用率的角度提出的離散分配機制;
段是出於用戶角度,用於數據保護,數據隔離等用途的管理機制;
頁的大小是固定的,操做系統決定,;段的大小不肯定,用戶程序決定
什麼是虛擬內存:
經過把一部分暫時不用的內存信息放到硬盤上:
局部性原理,程序運行時候只有部分必要的信息裝入內存;
內存中暫時不須要的內容放到硬盤上;
系統彷佛提供了比實際內存大得多的容量,稱之爲虛擬內存
什麼是內存抖動(顛簸):
頻繁的頁調度,進程中不斷產生缺頁中斷;
置換一個頁,又不斷再次須要這個頁;
運行程序太多了;頁面替換策略很差。終止進程或者增長物理內存;
Python垃圾回收機制:https://blog.csdn.net/csucsgoat/article/details/81510313
引用計數爲主(缺點:循環引用沒法解決【兩個對象互相引用以後引用計數沒法歸零】,del減小引用計數);
引用標記清除和分代回收解決引用計數的問題;
引用計數爲主+標記清除和分代回收爲輔
何時引用計數增長:
對象建立 a=1;
對象被引用:b=a;
對象做爲參數傳遞func(a);
對象存儲在容器中 l=[a];
何時引用計數會減小:
顯示使用del a;
引用指向了別的對象 b=None;
離開了對象的做用域(好比函數執行結束);
從一個容器移除對象或者銷燬容器
#a和b的引用計數都爲1 a=[1] b=[1] #a和b的引用計數都爲2 a.append(b) b.append(a) #a和b都銷燬 可是仍是1 del a del b
標記清除:
從根訪問,若是不可達的點(獨立的)就標記
分三代(0 1 2),每隔必定時間對每一代實行標記回收
瀏覽器輸入一個url中間經歷的過程:
DNS查詢->TCP握手->HTTP請求->反向代理nginx->uwsgi/gunicom->wep app響應->TCP揮手
TCP三次握手,四次揮手,狀態轉換:http://www.javashuo.com/article/p-ulpwsqrg-ek.html
TCP和UDP的區別:http://www.javashuo.com/article/p-rwajqagu-w.html
TCP是面向鏈接的,可靠的,基於字節流的;
UDP是無鏈接,不可靠,面向報文的;
HTTP協議:
HTTP請求的組成:
狀態行;
請求頭;
消息主體(可能爲空)
HTTP響應組成:
狀態行;響應頭;嚮應正文
HTTP請求頭:https://www.cnblogs.com/honghong87/articles/6941436.html
HTTP常見狀態碼:https://blog.csdn.net/laishaohe/article/details/79052085
HTTP GET/POST的區別:http://www.javashuo.com/article/p-oxfvvfbb-cp.html
Restful語義上一個是獲取,一個是建立;
Get是冪等的,POST是不冪等的;
GET請求參數放到url(明文),長度限制;POST放在請求體,更安全
什麼是冪等的(指屢次請求結果和請求一次結果一致):https://blog.csdn.net/qq_15037231/article/details/78051806
冪等方法是不管調用多少次獲得相同結果的HTTP方法;
例如:a=4是冪等的,a+=4就是非冪等的;
冪等的方法客戶端能夠安全的從發請求
冪等:OPTIONS/GET/HEAD?PUT?DELETE;非冪等:POST/PATCH
安全的(是否會修改數據):OPTIONS/HEAD/GET;不安全的:PUT/POST/DELETE/PATCH
HTTP長鏈接:http://www.javashuo.com/article/p-xbufbtql-ct.html
短鏈接:創建鏈接。。。數據傳輸。。關閉鏈接(鏈接的創建和關閉開銷大)
長鏈接:Connection:Keep-alive。保持TCp鏈接不斷開;
如何區分不一樣的HTTP請求:Content-Length(長度)|Transfer-Encoding(分段發送)
cookie和session的區別:
Session通常是服務器生成後交給客戶端(經過url參數或cookie);
Cookie是實現session的一種機制,經過HTTP cookie字段實現;
Session經過服務器保存sessionid識別用戶,cookie存儲在客戶端
TCP/UDP socket編程;HTTP編程:http://www.javashuo.com/article/p-vwskbwch-w.html
使用socket發送HTTP請求:
。。。。
五種IO模型
常見的提高併發能力的方式:
多線程模型;多進程模型;
IO多路複用:
爲了實現高併發須要一種機制併發處理多個socket;
Linux常見的是select/poll/epoll
select/poll/epoll的區別:http://www.javashuo.com/article/p-txmndpyl-cm.html
Tornado vs Gevent ns Asyncio:
Tornado併發網絡庫和同時也是一個web微框架;
Gevent綠色線程(greenlet)實現併發,猴子補丁修改內置socket;
Asyncio Python3內置的併發網絡庫,基於原生協程
Tornado:
適用於微服務,實現restful接口:
低層基於Linux多路複用;
能夠經過協程或者回調實現異步編程;
不過生態不夠完善,相應的異步框架好比ORM不完善
Gevent:
基於輕量級綠色線程實現併發;
須要注意monkey patch,gevent修改了內置的socket改成非阻塞;
配合gunicorn和gevent部署做爲wsgi server
Asyncio:
基於協程實現的內置併發網絡庫:
Python3引入內置庫,協程加事件循環;
生態不夠完善,沒有大規模生產環境檢驗;
目前應用不普遍,基於Aiohttp能夠實現一些小的服務
Mysql基礎考點:
事務的原理,特性,事務併發控制;
經常使用的字段,含義的區別;
經常使用的數據庫引擎之間的區別;
什麼是事務:
事務是數據庫併發控制的基本單位;
事務能夠看做是一些列SQL語句的集合;
事務必需要麼所有執行成功,要麼所有執行失敗(回滾),如轉帳操做。。。
事務的四個特性(ACID):
原子性:一個事務中的全部操做要不所有完成成功,要不所有失敗;
一致性:事務開始和結束後數據完整性沒有被破壞;
隔離性:容許多個事務同時對數據庫修改和讀寫;
持久性:事務結束後,修改是永不丟失的
事務的併發控制:
若是不對事務進行併發控制,可能產生四種異常狀況:
幻讀:一個事務第二次查出現第一次沒有結果;
非重複讀:一個事務重複讀兩次獲得的結果不一樣;
髒讀:一個事務讀取到另外一個事務沒有提交的修改;
丟失修改:併發寫入形成其中一些修改丟失
爲了解決併發控制異常,定義了4種事務隔離級別:
讀未提交:別的事務能夠讀取到未提交改變;
讀已提交:只能讀已經提交的數據;
可重複讀:同一個事務前後查詢的結果同樣(默認);
串行化:事務徹底串行化串行,隔離級別最高,執行效率最低
如何解決高併發場景下,寫辱數據庫有數據重複問題:
使用數據庫惟一索引;
使用隊列異步寫入;
使用redis等實現分佈式鎖
樂觀鎖和悲觀鎖:
悲觀鎖是先獲取鎖再進行操做。一鎖二查三更新 select for update;
樂觀鎖先修改,更新的時候發現數據已經變了就回滾 check and set;
使須要根據響應速度,衝突頻率,重試代價來判斷使用哪種
數據類型:。。。。int(10),表示最小顯示十個字符寬
Innodb和MyISAM區別:
MyISAM不支持事務u,InnoDB支持事務;
MyISAM不支持外鍵,InnoDB支持外鍵;
MyISAM只支持表鎖,InnoDB支持行鎖和表鎖;
MyISAM支持全文索引,InnoDB不支持;
。。。
Mysql索引原理及優化:
索引的原理,類型,結構;
建立索引的注意事項,使用原則;
如何排查和消除慢查詢
什麼是索引:
索引是數據表種一個或者多個列進行排序的數據結構,索引可以大幅提高檢索速度(查找結構:線性,二分,二叉樹。。。。);
建立,更新索引自己也會耗費空間和時間;
什麼是B-Tree:
查找結構進化史:
線性查找:一個個找;實現簡單;太慢;
二分查找:有序;簡單;要求有序,插入特別慢;
HASH:查詢塊;佔用空間;不太適合存儲大規模數據;
二叉查找樹:插入和查詢很快(logn);沒法存儲大規模數據,複雜度退化(單邊增加,成線性);
平衡樹:解決bst退化的問題,樹是平衡的;節點很是多的時候,依然樹高很高;
多路查找樹:一個父親多個孩子節點(度);節點過多樹高不會特別深;
多路平衡查找樹:B-Tree(沒法使用範圍查找)
B-Tree:多路平衡查找樹(每一個節點最多m(m>=2)個孩子,稱未m階或者度);
葉節點具備相同的深度;
節點種的數據key從左到右是遞增的;
B+Tree:
B+Tree是B-Tree的變形:
Mysql實際使用的B+Tree做爲索引的數據結構;
只在葉子節點帶有記錄的指針(爲何?能夠增長樹的度)
葉子節點經過指針相連。爲何?實現範圍查詢
階(度/孩子)越多越好嗎?
操做系統內存分頁,硬盤分塊;
以磁盤塊的大小去肯定階的大小,這樣操做系統去讀取和緩存數據就很是友好。
索引類型:
普通索引(create index);
惟一索引,索引列的值必循惟一(create unique index);
多列索引
主鍵索引:一個表只能有一個
全文索引:InnoDB不支持,倒排索引
何時建立索引:
常常用做查詢條件的字段(WHERE條件);
進場用作錶鏈接的字段;
常常出如今order by,group by以後的字段
建立索引須要注意的:
最佳實踐:
非空字段 NOT NULL,Mysql很難對空值做查詢優化;
區分度高,離散度大,做爲索引的字段值儘可能不要大量相同值;
索引的長度不要太長(比較耗費時間)
索引何時失效:
記憶口訣:模糊匹配,類型隱轉,最左匹配;
以%開頭的LIKE語句;
出現隱式類型轉換(在Python這種動態語言查詢中須要注意),沒法匹配;
沒有知足最左前綴匹配(爲何是最左前綴) (a,b,c) (1,2,3) (1,2,4),至關於元組,從左往右比較,如(a,b,c),(a,b),(a)能比較,可是(a,b,c)和(b,c)不能比較,第一個都不相同
彙集索引和非彙集索引:
彙集仍是非彙集指得是B+Tree葉節點存的是指針仍是數據記錄;
MyISAM索引和數據分離,使用的是非彙集索引;
InnoDB數據文件就是索引文件,主鍵索引就是彙集索引
如何排查慢查詢:
慢查詢缺乏索引,索引不合理或者業務代碼實現致使:
slow_query_log_file開始而且查詢慢查詢日誌;
經過explain排查索引問題;
調整數據修改索引;業務代碼層限制不合理訪問。
Mysql語句:
經常使用鏈接:
內鏈接:兩個表都存在匹配時,纔會返回匹配行;
外鏈接:返回一個表的行,即便另外一個沒有匹配;
全鏈接:只要某一個表存在匹配就返回
內鏈接:
將左表和右表可以關聯的數據鏈接返回;
相似於求兩個表的「交集」;
select * from A inner join B on a.id=b.id
外鏈接:
左鏈接:返回左表的全部記錄,即便右表中沒有任何知足匹配的記錄;
右鏈接:返回右表的全部記錄,即便左表中沒有任何知足匹配的記錄;
全鏈接:
只要某一個表存在匹配,就返回行;
相似於求兩個表的「並集」;
到時Mysql不支持,能夠用left join 和union,right join聯合使用模擬
緩存及redis:
緩存的使用場景,爲何使用:
內存緩存(常見的有Redis,Memcached):
緩解關係數據庫(常見的是Mysql)併發訪問的壓力:熱點數據;
減小響應時間:內存IO速度比磁盤快;
提高吞吐量:Redis等內存數據庫單機就能夠支撐很大併發
redis和memcached的區別:
http://www.javashuo.com/article/p-bqdmgulx-bt.html
resis經常使用數據類型及使用場景:
String(字符串):用來實現簡單的KV鍵值對存儲,好比計數器;
List(鏈表):實現雙向鏈表,好比用戶的關注,粉絲列表;
Hash(哈希表):用來存儲彼此相關信息的鍵值對;
Set(集合):存儲不重複元素,好比用戶的關注者;
Sorted Set(有序集合):實時信息排行榜
Redis內置實現:使用C語言實現;
String:整數或者sds(Simple Dynamic String);
List:ziplist(壓縮鏈表,經過一個連續的內存塊實現list結構,其中的每一個entry節點頭部保存先後節點長度信息,實現雙鏈表功能)或者double linked list;
Hash:ziplist或者hashtable(能夠本身設置,大於某個值使用什麼);
Set:intset或者hashtable;
SortedSet:skiplist跳躍表
時間空間複雜度。。。。
Sorted Set爲了簡化實現,使用skiplist而不是平衡樹實現:
Redis有哪些持久化的方式:
快照方式:把數據快照放在磁盤二進制文件中,dump.rdb;
AOF(Append Only File):每個寫命令追加到appendonly.aof中;
能夠修改Redis配置實現使用哪一種方式
Redis的事務:
將多個請求打包,一次性,按序執行多個命令的機制;
Redis經過MULTI,EXEC,WATCH等命令實現事務功能;
Python redis-py,pipeline=conn.pipeline(transaction=True)
Redis實現分佈式鎖:
使用setnx實現加鎖,能夠同時經過expire添加超時時間;
鎖的value值可使用一個隨機的uuid或者特定的命名;
釋放鎖的時候,經過uuid判斷是不是該鎖,是則執行delete釋放鎖
使用緩存的模式:
Cache Aside:同時更新緩存和數據庫;
Read/Write Througr:先更新緩存,緩存負責同步更新數據庫;
Write behind Caching:先更新緩存,緩存按期異步更新數據庫。
(先更新數據庫後更新緩存,併發寫操做可能致使緩存讀取的是髒數據,通常先更新數據庫而後刪除緩存)
緩存穿透的問題:
大量在緩存查詢不到的數據的請求落到後端數據庫,數據庫壓力增大
因爲大量緩存查不到就去數據庫取,數據庫也沒有要查的數據(爬蟲id遍歷,有可能大量的id沒有)
解決:對於沒查到的返回爲None的數據也緩存;
插入數據的時候刪除相應緩存,或者設置較短的超時時間
緩存擊穿的問題:
某些很是熱點的數據key過時,大量請求達到後端數據庫;
熱點數據key失效致使大量請求達到數據庫增長數據庫壓力;
分佈式鎖:獲取鎖的線程從數據庫拿數據更新緩存,其餘線程等待
異步後臺更新:後臺任務針對過時的key自動刷新
緩存雪崩:
大量緩存不可用或者大量的緩存key同時失效(或重啓緩存服務器),大量的請求直接打到數據庫
解決:多級緩存:不一樣級別的key設置不一樣的超時時間;
隨機超時:key的超時時間隨機設置,防止同時超時;
架構層:提高系統的可用性。監控,報警完善
Mysql問題:https://blog.csdn.net/HeatDeath/article/details/79833462
Redis實現分佈式鎖:https://baijiahao.baidu.com/s?id=1623086259657780069&wfr=spider&for=pc
WSGI;常見Web框架:
什麼是WSGI?爲何須要它?
Python Web Server Gateway Interface(pep3333)
解決Python Web Server亂象mod_python,CGI,FastCGI等
描述了Web Server(Gunicorn/uWSGI)如何與web框架(Flask/Django)交互,Web框架如何處理請求
def application(environ,start_response):
application就是WSGI app,一個可調用的對象
參數:
environ:一個包含WSGI環境信息的字典,由WSGI服務器提供,常見的key有PATH_INFO,QUERY_STRING等
start_response:生成WSGI響應的回調函數,接收兩個參數,status和headers
函數返回響應體的迭代器
編寫兼容WSGI的小應用(使用Python內置的WSGI server):
函數:
1 def myapp(environ,start_response): 2 status="200 OK" 3 headers=[('Content-Type','text/html;charset=utf8')] 4 start_response(status,headers) 5 #可迭代對象,字節 6 return [b'<h1>Hello World</h1>'] 7 8 if __name__=="__main__": 9 from wsgiref.simple_server import make_server 10 httpd=make_server('127.0.0.1',8888,myapp) 11 httpd.serve_forever()
效果
封裝成類:
1 class Application(object): 2 3 def __init__(self,routers,**kwargs): 4 self.routers=routers 5 6 def __call__(self, environ, start_response): 7 try: 8 request=Request(environ) 9 callback,args=routers.match(request.path) 10 response=callback(request,*args) 11 except FileNotFoundError: 12 response=Response("<h1>Not Found</h1>",status=404) 13 start_response(response.status,response.headers.items()) 14 return iter(response)
經常使用Python Web框架對比:
Django VS Flask VS Tarnado
Django大而全,內置ORM,Admin等組件,第三方插件較多
Flask:微框架,插件機制,比較靈活
Tornado:異步支持的微框加和異步網絡庫
什麼是MVC:Model(模型),View(視圖),控制器(Controller)【解耦數據,展現和操做】
Model:負責業務對象和數據庫的交互(ORM)
View:負責與用戶的交互展現
Controller:接收請求參數調用模型和視圖完成請求
什麼是ORM(對象關係映射):提高開發效率和維護性
用於實現業務對象與數據表的字段映射
優點:代碼更加面對對象,代碼量更少,靈活性高,提高開發效率;
缺點:拼接對象比較耗時,有必定性能影響
一個Web框架的組成:
中間件:用於請求以前和請求以後作一些處理(好比記錄日誌等)
路由,表單驗證,權限認證,ORM,視圖函數,模板渲染,序列化
第三方插件:Redis鏈接,RESTful支持等
什麼是Gunicorn:gunicorn -w 4 myapp:app(部署4個進程)
純Python編寫的高性能WSGI Server
pre-fork預先分配多個worker進程處理請求(master-slave)
多種worker支持:Sync/Async(Gevent)/Tornado/AsyncIO
Web安全:
SQL注入:
經過構造特殊的參數傳入Web應用,致使後端執行了惡意的SQL
一般因爲程序員未對輸入進行過濾,直接動態拼接SQL產生
可使用開源工具sqlmap,SQLninja檢測
建立表並插入幾條數據:
1 from hashlib import md5 2 def ha_md(s): 3 m=md5() 4 m.update('haha'.encode('utf-8')) 5 return m.hexdigest() 6 7 sql=""" 8 create table users( 9 id int PRIMARY KEY not null auto_increment, 10 name varchar(50) null, 11 email varchar(50) null, 12 password varchar(50) null 13 ); 14 insert into users(name,email,password) values('LYQ','lyq@haha.com','{0}'); 15 insert into users(name,email,password) values('LYQ2','lyq2@haha.com','{1}'); 16 insert into users(name,email,password) values('LYQ3','lyq3@haha.com','{2}'); 17 """.format(ha_md('123411'),ha_md('adade'),ha_md('467241')) 18 19 #sql注入演示代碼 20 import os 21 import MySQLdb 22 db=MySQLdb.connect(host='localhost', 23 user='root', 24 passwd='112358', 25 db='test', 26 charset='utf8') 27 28 cur=db.cursor() 29 cur.execute(sql) 30 cur.close() 31 db.commit() 32 db.close()
直接拼接:若是password輸入--(表示註釋),只要name對了就能找到,修改成sql="select * from name=%s AND password=md5(%s)"就不會了
1 #sql注入演示代碼 2 import MySQLdb 3 db=MySQLdb.connect(host='localhost', 4 user='root', 5 passwd='112358', 6 db='test', 7 charset='utf8') 8 9 cur=db.cursor() 10 name=input("請輸入姓名:") 11 print("您1輸入的姓名是:{0}".format(name)) 12 password=input("請輸入密碼:") 13 print("您輸入的密碼是:{0}".format(password)) 14 #直接拼接sql 15 sql="select * from name='"+name+"'"+" AND password=md5('"+password+"')" 16 cur.execute(sql) 17 cur.close() 18 db.commit() 19 db.close()
如何防範SQL注入:
web安全一大法則:永遠不要相信用戶的任何輸入
對輸入參數作好檢查(類型和範圍);過濾和轉義特殊字符;
不要直接拼接sql,使用ORM能夠大大下降sql注入的風險;
數據庫層:作好權限管理配置i;不要明文存儲敏感信息
XSS攻擊:跨站腳本攻擊
惡意用戶將代碼植入到提供給其餘用戶使用的頁面中,未經轉義的惡意代碼輸出到其餘用戶的瀏覽器被執行;
用戶瀏覽頁面的時候嵌入頁面中的腳本(js)會被執行,攻擊用戶;
主要分爲兩類:反射型(非持久型),存儲型(持久型)
如:博客評論:<script>alert("haha");</script>,若是沒有轉義js代碼,訪問會彈出haha;(存儲型)
還有的能夠手機用戶的document.cookie發送到指定的服務器上
反射型如把腳本放在url參數裏面,誘導用戶去點擊
xss危害:
盜用cookie,獲取敏感信息;
利用用戶私人帳號執行一些違法行爲,好比盜取我的或者商業資料,執行一些隱私操做;
甚至能夠在一些訪問量很大的網站上實現DDos攻擊
如何訪問xss攻擊:
過濾(輸入和參數)。對敏感標籤<script><img> <a>等進行過濾。;
轉義。對常見符號(「&」,「<」,「and」)轉義(python3有個html.escape轉義);
設置HttpOnly禁止瀏覽器訪問和操做Document.cookie
CSRF:跨站請求僞造
利用網站對已認證用戶的權限去執行未受權的命令的一種惡意攻擊;
攻擊者會盜用你的登陸信息,以你的身份模擬發送請求;
web身份認證機制只能識別一個請求是否來自某個用戶的瀏覽器,可是沒法保證請求是用戶本身或者批准發的
csrf:http://www.javashuo.com/article/p-cfrdrxkv-kt.html
csrf攻擊具有的兩個條件:
受害者已經登陸到了目標網站而且沒有退出(保持登陸狀態);
受害者訪問攻擊者連接或者吧表單
兩者缺一不可
如何防範csrf:不要再任何GET請求中有任何數據修改操做
令牌操做(STP):再用戶請求的表單中嵌入一個隱藏的csrf_token,服務端驗證其是否與cookie中的一致(基於同源策略其餘網站是沒法獲取cookie中的csrf_token)
黑客是拿不到你cookie中的csrftoken值的,前提是網站自己沒有xss漏洞;
若是是js提交須要先從cookie獲取csrf_token做爲X-CSRFToken請求頭提交
其餘:檢測來源HTTP Referer(容易被僞造);驗證碼方式(安全可是繁瑣)
先後端分離和Restful:
什麼是先後端分離:
後端只負責數據接口,再也不渲染模板,前端獲取數據並呈現
先後端分離的意義和方式:
先後端解耦,接口複用(前端和客戶端公用接口),減小開發量;
各司其職,先後端同步開發,提高工做效率。定義好接口規範 ;
更有利於調試,測試和運維部署
什麼是RESTfulL:
表現層狀態轉移,由HTTP協議的主要設計者Roy Fielding提出;
資源(使用URL指向的一個實體),表現層(資源的表現形式,好比圖片,HTML文本等),狀態轉化(GET,POST,PUT,DELETE等動詞來操做資源,實現資源狀態的改變);
是一種資源爲中心的web軟件架構風格,能夠用Ajax和RESTful web服務構建應用
設計的概念和準則:
全部事物抽象爲資源,資源對應惟一的標識;
資源經過接口進行操做實現狀態轉移,操做自己是無狀態的
什麼是RESTful API:
RESTful風格的API接口:
經過HTTP GET/POST/DELETE 獲取/新建/更新/刪除資源
通常經過JSON格式返回數據;
通常web框架都有相應的插件支持RESTful API
如何設計RESTful API
GET http://[hostname]/api/users 檢索用戶列表.....
什麼是https。。。http和https的區別。。。你瞭解對稱加密和非對稱加密碼。。。
HTTPS的通訊過程?
什麼是系統設計:
系統設計是一個定義系統架構,模塊,接口和數據知足特定需求的過程;
好比設計一個短網址服務,評論服務,Feed流服務,搶紅包系統;
微服務架構不少系統被按照業務拆分,須要單獨設計一個系統服務
系統設計難點:中高級工程師必經之路
須要具有相關領域,算法經驗,有必定的架構設計能力;
熟悉後端技術組件,好比消息隊列,緩存,數據庫,框架;
具有文檔撰寫,流程圖繪製,架構設計,編碼實現等綜合能力
系統設計的要素:
使用場景和限制條件;
數據存儲設計;
算法模塊設計
按照三個元素來答:
什麼場景和條件下使用;(好比短網址系統提供站內各類服務生成短網址;限制條件:用戶估計有多少?至少要能支撐多少用戶(服務)?估算併發qps:峯值qps是多少?平均qps是多少)
設計存儲模塊;
設計算法模塊
數據存儲設計:
短網址系統的存儲設計?須要存儲哪些字段?
如何設計短網址系統?
按照設計數據庫,須要哪些字段,使用類型》數據增加規模;
數據庫選型:是否須要持久化?使用關係型仍是NoSQL;
如何優化?如何設計索引?是否可使用緩存?
算法模塊設計:
須要哪些接口?幾口如何設計?
使用什麼算法或者模型?
不一樣實現方式之間的優劣對比?如何取捨?
擴展:用戶多了,qps高了如何處理?
數據存儲多了不夠存瞭如何處理?
故障如何處理?單點失敗?多點失敗,雪崩問題
短網址系統的設計和實現:
什麼是短網址系統,包含哪些功能?
把一個長網址轉成短網址的服務。
好比https://bitly.com。
轉換以後網址的後綴不超過7位(字符或數字)
使用場景:
一個長網址轉成短網址並存儲;根據短網址還原長url;
要求短網址的後綴不超過7位(大小寫字母和數字);
預估峯值插入請求數量級:數百;查詢請求數量級:數千。
數據存儲設計:
根據需求設計數據存儲方式:
使用Mysql便可知足;
須要的字段有哪些:
id token(索引設計) url(原網址) created_time
短網址生成算法有哪些?對比優缺點
兩個API:long2short_url,short2long_url
md5摘要算法:獲得的是32位,能夠階段,取前7個字符,可是衝突/
相似於62進制的數字。二進制:0,1;十六進制0-9,a-f;十進制->62進制
十進制->二進制轉換:
1 #不斷取餘,倒序輸出 2 def mybin(num): 3 if num==0: 4 return 0 5 result=[] 6 while num: 7 num,rem=divmod(num,2) 8 result.append(str(rem)) 9 return ''.join(reversed(result)) 10 11 print(mybin(10))
十進制->16進制:遞增序列算法
1 CHARS='abcdefghijklmnopqrstuvwxwzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' 2 def encode(num): 3 if num==0: 4 return CHARS[0] 5 res=[] 6 while num: 7 num,rem=divmod(num,len(CHARS)) 8 res.append(CHARS[rem]) 9 return ''.join(reversed(res)) 10 print(encode(61))
(須要自增id,須要一個計數器Redis incr)
request->redis incr index->token->mysql->url
如何設計秒殺系統:
什麼是秒殺系統?有沒有用過?
如何根據三要素設計?
秒殺系統涉及到哪些後端組件?