Python面試知識點小結

一.Python基礎

  1.Python語言特性:

    動態型(運行期肯定類型,靜態型是編譯型肯定類型),強類型(不發生隱式轉換,弱類型,如PHP,JavaScript就會發生隱患式轉換html

  2.Python做爲後端語言的優缺點:

    優勢:前端

      膠水語言,輪子多,應用普遍;語言靈活,生產力高node

    缺點:python

      性能問題,代碼維護問題,python2/3不兼容問題mysql

  3.鴨子類型:

    「當一隻鳥走起來像鴨子,游泳像鴨子,叫起來像鴨子,那麼這隻鳥就能被稱爲鴨子」nginx

    關注點在對象的行爲,而不是類型;程序員

    如file,StringIO,socket都支持read/write方法(file類型)web

    定義了__iter__就能夠用for迭代redis

  4.monkey patch(猴子補丁):

    就是運行時替換算法

    4.1好比gevent須要修改內置的socket和select:

 

    4.2本身寫的猴子補丁:

      

    5.is和==:

    is是判斷是否爲同一個對象(id內存地址是否相同),==是判斷值是否相同

  6.列表,生成器,字典推導:

  7.Python之禪:

    Tim Peters編寫的關於Python編程的規則;

    import this查看,編程拿不許能夠查看:

                

優美勝於醜陋(Python 以編寫優美的代碼爲目標)

明瞭勝於晦澀(優美的代碼應當是明瞭的,命名規範,風格類似)

簡潔勝於複雜(優美的代碼應當是簡潔的,不要有複雜的內部實現)

複雜勝於凌亂(若是複雜不可避免,那代碼間也不能有難懂的關係,要保持接口簡潔)

扁平勝於嵌套(優美的代碼應當是扁平的,不能有太多的嵌套)

間隔勝於緊湊(優美的代碼有適當的間隔,不要奢望一行代碼解決問題)

可讀性很重要(優美的代碼是可讀的)

即使假借特例的實用性之名,也不可違背這些規則(這些規則至高無上)

不要包容全部錯誤,除非你肯定須要這樣作(精準地捕獲異常,不寫 except:pass 風格的代碼)

當存在多種可能,不要嘗試去猜想

而是儘可能找一種,最好是惟一一種明顯的解決方案(若是不肯定,就用窮舉法)

雖然這並不容易,由於你不是 Python 之父(這裏的 Dutch 是指 Guido )

作也許好過不作,但不假思索就動手還不如不作(動手以前要細思量)

若是你沒法向人描述你的方案,那確定不是一個好方案;反之亦然(方案測評標準)

命名空間是一種絕妙的理念,咱們應當多加利用(倡導與號召)

  8.Python2/3的差別:

    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等;

      性能優化等

    Python新增:

      yield from連接子生成器;

      asyncio內置庫,async/await原生協程支持異步編程;

      新的內置庫enum(枚舉),mock(單測),asyncio(異步),ipaddress(處理ip地址),concurent.futures等

     Python/2/3兼容工具:

      six模塊;2to3等工具轉換代碼;__future__

  9. Python函數常考題:

    Python如何傳遞參數:

      傳遞值仍是引用?都不是,惟一支持的參數傳遞是共享傳參;

      Call by Object(Call by Object Reference or Call by Sharing)

      共享傳參,函數形參得到實參種各個引用的副本      

     

ll指向[1,2,3],l也指向[1,2,3],後又指向[],因此ll仍爲[1,2,3],l爲[]

默認參數只計算一次

    Python可變,不可變對象:

      不可變對象:bool/int/float.str/frozenset;

      可變對象:list/set/dict

    Python *args,**kwargs:

      用來處理可變參數,*args會被打包成tuple,**kwargs會打包成dict

  10.Python異常處理機制:

    什麼是Python的異常:

      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)

  10.Python性能分析與優化,GIL常考題:

    GIL(Global Interpreter Lock):全局解釋器鎖

      Cpython解釋器的內存管理並非線程安全的;

      保護多線程狀況下對Python對象的訪問;

      Cpython使用簡單的鎖機制避免多個線程同時執行字節碼。

    GIL的影響:

      限制了程序的多核執行:

        同一個時間只能有一個線程執行字節碼;

        CPU密集程序難以利用多核優點;

        IO期間會釋放GIL,對IO密集程序影響不大

    如何規避GIL影響:

      CPU密集能夠用多進程+進程池;

      IO密集使用多線程/協程

      cython擴展

    Python中什麼操做纔是原子的?一步到位執行完:

      一個操做若是一個字節碼指令能夠完成就是原子的;

      原子的是能夠保證線程安全的;

      使用dis操做來分析字節碼。

    如何剖析程序性能:

      使用profile工具(內置或第三方)

        二八定律:大部分時間耗時在少許代碼上;

        內置的profile/cprofile等工具;

        使用pyflame(uber開源)的火焰圖工具

    服務端性能優化措施:

      web應用通常語言不會成爲瓶頸:

          數據結構與算法優化;

        數據庫層:索引優化,慢查詢消除,批量操做減小IO,NoSql

        網絡IO:批量操做,pipeline操做減小IO

        緩存:使用內存數據庫 redis。memcached

        異步:asyncio,celery

        併發:gevent/多線程

  11.生成器和協程:

    生成器:

      生成器就是能夠生成值的函數;

      當一個函數裏有了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支持原生協程

  12.Python單元測試:

    什麼是單元測試:

      針對程序模塊進行正確性檢驗;

      一個函數,一個類進行驗證;

      自底向上保證程序正確性

    爲何要寫單元測試:

      三無代碼不可取(無文檔,無註釋,無單測)

      保證代碼邏輯的正確性(甚至有些採用測試驅動開發(TDD))

      單測影響設計,易測的代碼每每是高類聚低耦合的;

      迴歸測試,防止一處修改整個服務不可用

    單元測試庫:

      nose/pytest較爲經常使用;

      mock模塊用來模擬替換網絡請求等

       coverage統計測試覆蓋率

    Python深拷貝和淺拷貝:

      深拷貝,包含對象裏面的自對象的拷貝,因此原始對象的改變不會形成深拷貝里任何子元素的改變。

      淺拷貝至關於拷貝了對象的引用(在python中,對象賦值其實是對象的引用。當建立一個對象,而後把它賦給另外一個變量的時候,python並無拷貝這個對象,而只是拷貝了這個對象的引用)

    Python建立二維數組:

      http://www.javashuo.com/article/p-gulqatsm-r.html

二.算法和數據結構:  

  1.經常使用的內置算法結構:

    sorted;

    dict/list/set/tuple.... 

    collections中經常使用模塊:

      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)

 

    2.算法常考題:

      排序,查找等。。。。

      排序的穩定性:

        相同大小的元素排序以後保持相對的位置不變,就是穩定的。穩定性對於排序一個複雜的結構,而且保持原有排序纔有意義。

      快排:(分治法)

        選擇基準分割數組爲兩個字數組,小於基準和大於基準的;

        對兩個子數組分別快排;

        合併結果。

 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]))
View Code

 

       歸併排序:

 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]))
View Code

 

      二分查找:

 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))
View Code

   3.數據結構:   

    常考的數據結構:

      數據結構鏈表,隊列,棧,二叉樹,堆

      使用內置結構實現數據結構,好比內置的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
View Code

 

      隊列是先進先出結構:

          如何實現隊列;實現隊列的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())
View Code

 

        Python的dict和set低層實現都是hash表:

        hash表的低層實現原理其實就是數組;

        根據哈希表能夠很快定位一個元素,平均查找爲O(1);

        不斷加入元素會引發hash表重新開闢新空間,拷貝以前的元素到新空間

      解決哈希衝突:

        連接法:

          元素key衝突以後使用一個鏈表填充相同key的元素;

        開放尋址法:

          衝突以後根據必定方式(二次探查)尋找下一個可用的槽   

      二叉樹:

         先序(根->左->右),中序(左->根->右),後序(左->右->根)    

      堆:

        最大堆:對於每一個非葉子節點V,V的值都比它的兩個孩子大;

        最大堆支持每次pop操做獲取最大的元素,最小堆獲取最小的元素;

        常見問題:用堆來完成topk問題,從海量數據中尋找最大的k個  

      堆解決topk的問題:

        獲取大量元素,固定內存:

          思路: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())
View Code

     鏈表:

      刪除節點:

 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
View Code

 

 

      合併鏈表:

 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
View Code  

     二叉樹:

      二叉樹的鏡像(反轉):

 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
View Code

 

        層序遍歷二叉樹(廣度優先):  

 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 []
View Code

     棧和隊列:

      棧實現隊列:

 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()
View Code

 

    堆:

      合併多個有序數組;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         
View Code     

    字符串:

      翻轉字符串: 

 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
View Code

 

      判斷一個數字是不是迴文數:

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
View Code

 三.面向對象編程和設計模式

  1.什麼是面向對象編程:

    把對象做爲基本單元,把對象抽象成類,包括成員和方法;

    數據封裝,繼承,多態;

    Python中使用類來實現。過程式編程(函數),OOP(類)

    2.組合和繼承:

    組合是使用其餘類的實例做爲本身的一個屬性;

    子類繼承父類的屬性和方法(Is a關係);

    優先使用組合保持代碼簡單。

  3.類變量和實例變量的區別:

    類變量是全部實例共享;

    實例變量由實例單獨享有,不一樣實例之間不影響;

    當咱們須要在一個類的不一樣實例之間共享變量的時候使用類變量。

 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()
View Code

 

  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
View Code

   5.元類:

    元類是建立類的類。(元類容許咱們控制類的生成,好比修改類的屬性等);

    使用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})
View Code

 

  元類的使用:

 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))
View Code

 

   6.裝飾器:

    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()
View Code

 

   使用類編寫裝飾器:

 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)
View Code

 

   使用類磚石砌比較方便實現裝飾器參數:

 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)
View Code

   7.設計模式:

    建立型:

      工廠模式:解決對象建立問題;

      構造模式:控制複雜對象的建立;

      原型模式:經過原型的克隆建立新的實例;

      單例模式:一個類只能建立同一個對象;

      對象池模式:預先分配同一個類型的一組實例;

      惰性計算模式:延遲計算

      

      工廠模式:

        解決對象建立問題;

        解決對象的建立和使用;

        包括工廠方法和抽象工廠

 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()
View Code

 

       構造模式:

        用來控制複雜對象的構造;

        建立和表示分離。好比你要買電腦,工廠模式直接給你須要的電腦,可是構造模式容許你本身定義電腦的配置,組裝完成後給你

      原型模式:

        經過克隆原型來建立新的實例;

        可使用相同的類型,經過修改部分屬性來建立新的實例;

        用途:對於一些建立實例開銷比較高的地方可使用原型模式 

      單例模式:

        一個類建立出來的對象都是同一個;

        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)
View Code

 

      外觀模式:

        簡化複雜對象的訪問問題;

      享元模式:

        經過對象複用(池)改善資源利用,好比鏈接池;

      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)
View Code

 

      觀察者模式:對象發生改變的時候,觀察者執行相應的操做

        發佈訂閱是一種最經常使用的實現方式;

        發佈訂閱用於解耦邏輯;

        能夠經過回調方式實現,當發生事件時,調用相應的回調函數

 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)
View Code

 

      策略模式:針對不一樣規模使用不一樣的策略

        根據不一樣的輸入採用不一樣的策略;

        好比買東西超過10個打八折,超過20個打七折;

        對外暴露統一的接口,內部採用不一樣的策略計算

  8.函數式編程(推薦使用生成式):

    把電腦運算視做數學上的函數計算(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))

 

   9.閉包:引用了外部自由變量的函數;自由變量:不在當前函數定義的變量;特性:自由變量會和閉包函數同時存在

    綁定了外部做用域的變量的函數;

    即便程序離開外部做用域,若是閉包仍然可見,綁定變量不會銷燬;

    每次運行外部函數都會從新組建閉包

 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)
View Code

   10.Linux常考命令:

    man:查看某個命令怎末用;

    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

  11.線程和進程的區別:

    進程是對運行程序的封裝,是系統資源調度和分配的基本單位;

    線程是進程的子任務,cou調度和分配的基本單位,實現進程內併發;

    一個進程能夠包含多個線程,線程依賴進程存在,並共享進程內存

  12.線程安全:

    Python中哪些是線程安全的:

      一個操做能夠在多線程環境中安全使用,獲取正確的結果;

      線程安全的操做比如線程是順序執行而不是併發執行的;

      通常若是涉及到寫操做須要考慮如何讓多個縣城安全訪問數據

    線程同步的方式:

      互斥量(鎖):經過互斥機制防止多個線程同時訪問公共資源;

      信號量:控制同一時刻多個線程同時訪問同一個資源的線程數

      事件(信號):經過通知的方式保持多個線程同步

    進程間通訊:

      管道/匿名管道/有名管道(pipe)

      信號:好比用戶ctrl+c產生SININT程序終止信號

      消息隊列

      共享內存;

      信號量;

      套接字:最經常使用的

  13.操做系統內存管理機制:

    分頁機制:操做系統爲了高效管理內存,減小碎片

    把邏輯地址和物理地址分離的內存分配管理方案:

         程序的邏輯地址劃分爲固定大小的頁;

         物理地址劃分爲一樣大小的幀;

         經過頁表對應邏輯地址和物理地址

    分段機制:分段是爲了知足代碼的一些邏輯需求

      數據共享,數據保護,動態連接等

      經過段表實現邏輯地址和物理地址的映射關係;

      每一個段內部是內存分配,段和段之間是離散分配的

    分頁和分段的區別:

      頁是出於內存利用率的角度提出的離散分配機制;

      段是出於用戶角度,用於數據保護,數據隔離等用途的管理機制;

      頁的大小是固定的,操做系統決定,;段的大小不肯定,用戶程序決定

    什麼是虛擬內存:

      經過把一部分暫時不用的內存信息放到硬盤上:

        局部性原理,程序運行時候只有部分必要的信息裝入內存;

        內存中暫時不須要的內容放到硬盤上;

        系統彷佛提供了比實際內存大得多的容量,稱之爲虛擬內存

    什麼是內存抖動(顛簸):

      頻繁的頁調度,進程中不斷產生缺頁中斷;

      置換一個頁,又不斷再次須要這個頁;

      運行程序太多了;頁面替換策略很差。終止進程或者增長物理內存;

    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),每隔必定時間對每一代實行標記回收

 四.網絡協議TCP/UDP/HTTP

  瀏覽器輸入一個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多路複用:操做系統提供的同時監聽多個socket的機制

    五種IO模型 

    常見的提高併發能力的方式:

      多線程模型;多進程模型; 

    IO多路複用:

      爲了實現高併發須要一種機制併發處理多個socket;

      Linux常見的是select/poll/epoll

      select/poll/epoll的區別:http://www.javashuo.com/article/p-txmndpyl-cm.html

  Python併發網絡庫:

    哪些併發網絡庫:

      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

六.Python web

   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()
View Code

 

效果

      封裝成類:

 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)
View Code

 

  經常使用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()
View Code

 

  

 

       直接拼接:若是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()
View Code

 

         如何防範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))
View Code

 

      十進制->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))
View Code

 

      (須要自增id,須要一個計數器Redis incr)

       request->redis incr index->token->mysql->url

    如何設計秒殺系統:

      什麼是秒殺系統?有沒有用過?

      如何根據三要素設計?

      秒殺系統涉及到哪些後端組件?

      https://www.jianshu.com/p/d789ea15d060      

相關文章
相關標籤/搜索