<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      TOP

      Python 開(kāi)發(fā)面試梳理

      整體知識(shí)框架

      后端工程師的整理工作流程以一次web請(qǐng)求為例

      這期間的每個(gè)流程需要進(jìn)行掌握其中涉及的知識(shí)點(diǎn)以及相關(guān)技術(shù)棧

      •  瀏覽器這里的前端相關(guān)
      •  負(fù)載均衡一般有哪些方式, 比如 nginx 之類的,
      •  web 框架可以選的 django 或者 flask 
      •  業(yè)務(wù)邏輯相關(guān)的具體實(shí)現(xiàn)涉及到編程范式, 設(shè)計(jì)模式等
      •  數(shù)據(jù)庫(kù)相關(guān)sql或者nosql, 緩存的 redis 之類  

      技術(shù)棧大綱

      ▓ Python 預(yù)言基礎(chǔ)   -  語(yǔ)言特點(diǎn) / 語(yǔ)法基礎(chǔ) / 高級(jí)特性

      ▓ Python函數(shù)常考 / 參數(shù)傳遞 / 不可變對(duì)象 / 可變參數(shù)

      ▓ 算法和數(shù)據(jù)結(jié)構(gòu) -   時(shí)間空間復(fù)雜度 / 實(shí)現(xiàn)常見(jiàn)的數(shù)據(jù)結(jié)構(gòu)和算法

      ▓ 編程范式  -  面向?qū)ο缶幊?/ 常用設(shè)計(jì)模式 / 函數(shù)式編程

      ▓ 操作系統(tǒng)  -  常用的 Linux 命令 / 進(jìn)程  / 線程 / 內(nèi)存管理

      ▓ 網(wǎng)絡(luò)編程 - 常用協(xié)議 TCP, IP, HTTP /  Socket 編程基礎(chǔ) / Python 并發(fā)庫(kù)

      ▓ 數(shù)據(jù)庫(kù)  -  Mysql 索引優(yōu)化 / 關(guān)系型和NoSQL 的使用場(chǎng)景

      ▓ Web 框架  -  常見(jiàn)的框架對(duì)比 / RESTful  / WSGI 原理 / Web 安全問(wèn)題

      ▓ 系統(tǒng)設(shè)計(jì)  -  設(shè)計(jì)原則, 如何分析 / 后端常用的組件 (緩存, 數(shù)據(jù)庫(kù), 消息隊(duì)列) / 技術(shù)選型和實(shí)現(xiàn) (短網(wǎng)址服務(wù) , Feed 流系統(tǒng))

      ▓ 軟實(shí)力  -  學(xué)習(xí)能力 / 業(yè)務(wù)理解能力 / 溝通交流能力 / 心態(tài)

      python 基礎(chǔ)問(wèn)題

      python 是靜態(tài)還是動(dòng)態(tài) , 強(qiáng)類型還是弱類型 

      動(dòng)態(tài)強(qiáng)類型 

      ps:

        動(dòng)態(tài) or 靜態(tài) ? 編譯期還是運(yùn)行期間確定類型

        強(qiáng)類型指不會(huì)發(fā)生隱式類型轉(zhuǎn)換

      python 作為后端預(yù)言的優(yōu)缺點(diǎn)

      優(yōu)點(diǎn) : 膠水預(yù)言, 輪子多, 應(yīng)用廣泛 ,預(yù)言靈活, 生產(chǎn)力高

      缺點(diǎn) : 性能問(wèn)題, 代碼維護(hù)問(wèn)題, python 2 / 3 兼容問(wèn)題

      什么是鴨子類型

      關(guān)注點(diǎn)在對(duì)象的行為, 而不是類型

      比如 file, StringIO, socket 對(duì)象都支持 read / wirte 方法 

      再比如定義了 __iter__ 魔術(shù)方法的對(duì)象可以用 for 迭代 

      什么是 monkey patch, 哪里用到了, 如何自己實(shí)現(xiàn)

      所謂猴子補(bǔ)丁就是運(yùn)行時(shí)的替換, 比如 gevent 庫(kù)需要修改內(nèi)置的 socket 

      簡(jiǎn)單的實(shí)現(xiàn)樣例

       什么是自省

      Introspection 

       運(yùn)行時(shí)判斷一個(gè)對(duì)象的類型的能力

       python 一切皆對(duì)象, 用 type, id, isinstance 獲取對(duì)象的信息 

       Inspect 模塊提供了更多獲取對(duì)象信息的函數(shù)

      ps:

        id 獲取其內(nèi)存地址

          "is" 和 "==" 的區(qū)別?

           is 相當(dāng)于調(diào)用 id 判斷兩個(gè)的內(nèi)存地址是否一樣

          == 則是判斷值是否一致

       什么是列表和字段推導(dǎo) (語(yǔ)法糖)

      比如 

       一種快速生成 list / dict / set  的方式, 用來(lái)代替 map 或者 filter 等

      ps:

        換成 () 也可以返回生成器, 從而節(jié)省內(nèi)存  

        

      什么是深拷貝, 什么是淺拷貝

      淺拷貝共享地址, 比如引用

      深拷貝不同享內(nèi)存地址, 彼此獨(dú)立. 互不影響

       盡管深拷貝已經(jīng)獨(dú)立出來(lái), 但是只能獨(dú)立一層, 對(duì)于多層的可變對(duì)象的內(nèi)部依舊是引用

      可以使用 deepcopy 進(jìn)行解決

      ps:

        如何正確初始化一個(gè) 3 * 3 的二維數(shù)組

         \

      什么是python 之禪

      終端內(nèi) 進(jìn)行 this 的導(dǎo)入即可看到

      Python 2/3 的區(qū)別

      print

      print 成為函數(shù)

        

      ps:

        py3 的 print 函數(shù)可以支持多個(gè)參數(shù)比 py2 的要更靈活的去控制 

          

      字符編碼

      Py3 不在有 Unicode 對(duì)象, 默認(rèn)的 str 就是 Unicode

      除法變化

      Py3 返回的是浮點(diǎn)數(shù), Py2 會(huì)直接截?cái)? 返回整數(shù)

       

       ps:

         想要 在py3 中實(shí)現(xiàn) py2 的效果可以使用 //   

        

      Python 3 的改進(jìn)

      類型注解

       type , hint 幫助 IDE 實(shí)現(xiàn)類型檢查

       類型注解只能進(jìn)行提示無(wú)法做到校驗(yàn)

      但是可以配合類似 mypy 之類的包進(jìn)行真正的校驗(yàn)

      優(yōu)化 super() 

      方便直接調(diào)用父類函數(shù)

       py2 中需要傳入?yún)?shù) 自己的類名以及self, 但是py3 中就不需要傳入?yún)?shù)了

      會(huì)方便很多, 是語(yǔ)法糖的一個(gè)簡(jiǎn)化操作

      高級(jí)解包操作

       常規(guī)操作在23中都可以進(jìn)行

      但是 py3 中有了更強(qiáng)的操作

       可以用 * 帶進(jìn)行類似 *args 這樣的操作進(jìn)行便攜獲取

      限定關(guān)鍵字參數(shù)

      存在多個(gè)不定參數(shù)的時(shí)候, 不想讓順序搞亂

      可以使用關(guān)鍵字參數(shù)跳過(guò)順序進(jìn)行傳參

      關(guān)鍵字參數(shù)具備高于順序位置傳參的優(yōu)先級(jí)

      重新拋出異常不會(huì)丟失棧信息

      py2 中如果在異常中再次拋出異常, 則之前的異常就會(huì)丟失

      py3 中支持 raise from 保留之前異常, 這樣有利于去排錯(cuò)

      一切返回迭代器

      py2 中很多內(nèi)置函數(shù)返回的就是實(shí)打?qū)嵉牧斜?(range, zip, map. dict.values, etc,are all)

      如果數(shù)量比較大就比較麻煩, 但是 py3 返回的都是迭代器

      因?yàn)槭菓屑虞d. 所以你不用他就不會(huì)生成列表去占用內(nèi)存

      這個(gè)問(wèn)題在 py 的解決方式是用 xrange 

      因此這個(gè)問(wèn)題在 py3 中不會(huì)出現(xiàn)之后. xrange 在 py3 中也被刪除

      如果需要變成列表則需要 list 強(qiáng)轉(zhuǎn)一下

      生成的pyc 文件統(tǒng)一放在 __pychche__ 中

      一些內(nèi)置庫(kù)的修改

      urlib , selector 等

      性能優(yōu)化等

      Python3 新增

      yield from

      連接子生成器

      asyncio 內(nèi)置庫(kù)

      async / await 原生協(xié)程支持異步編程

      新的內(nèi)置庫(kù)

      enum, mock, asynic, ipaddress, concurrent.futures 等

      Python 2 / 3 工具

      • six 模塊
      • 2to3 等工具
      • __future__

      Python函數(shù)常考 / 參數(shù)傳遞 / 不可變對(duì)象 / 可變參數(shù)

      可變參數(shù)作為參數(shù) , 不可變參數(shù)作為參數(shù)的區(qū)別

      Python 如何傳遞參數(shù)

      python 傳參并非 值傳遞 or 引用, 唯一支持的是 共享傳參  (Call by Object / Call by sharing)

      函數(shù)形參獲得實(shí)參中各個(gè)引用的副本

      可變的對(duì)象直接在原來(lái)的基礎(chǔ)上進(jìn)行修改

       不可變無(wú)法修改, 只能重新創(chuàng)建新的進(jìn)行修改

       ps: 

        

       ps:

        類似題  

        

      Python 可變參數(shù)作為默認(rèn)參數(shù)

      默認(rèn)參數(shù)只會(huì)計(jì)算一次

      Python  *args, **kwargs

      用來(lái)處理可變參數(shù)

      *args 會(huì)打包成 tuple

       

      *kargs 會(huì)打包成 dict

       

       混用

      配合解包傳參

       

       Python 異常機(jī)制常考題

      • BaseException 所有的異常都繼承此異常
      • SystemExit / KeyboardInterrupt / GeneratorExit 控制系統(tǒng)相關(guān)的異常
      • Esception  其他的常見(jiàn)異常都繼承此異常

      使用異常的常見(jiàn)場(chǎng)景 

      • 網(wǎng)絡(luò)請(qǐng)求, 超時(shí), 連接錯(cuò)誤
      • 資源訪問(wèn), 權(quán)限, 資源不存在
      • 代碼邏輯, 越界訪問(wèn), KeyError 等

      如何處理異常

       如何自定義異常

      繼承 Exception 實(shí)現(xiàn)自定義異常, 并且加上一些附加信息, 用來(lái)處理一些業(yè)務(wù)相關(guān)的特定異常 (rause MyException)

      ps : 

        如果使用 BaseException 的話結(jié)束程序都是個(gè)問(wèn)題, 因?yàn)榭刂?Ctrl + c 的 KeyboardInterrupt  異常也被捕獲

        從而導(dǎo)致無(wú)法結(jié)束程序

      Python 性能分析與優(yōu)化, GIL 常考題

      什么是 ,Cpytjon  GIL 

      •   Global Interpreter Lock 
      •   Cpython 解釋器的內(nèi)存管理不是線程安全的
      •   為了保護(hù)多線程下對(duì) Python對(duì)象 的安全訪問(wèn)
      •   Cpython 使用簡(jiǎn)單的鎖機(jī)制避免多線程直接執(zhí)行

      GIL 的影響

      • 限制了程序的多核執(zhí)行
      • 同一個(gè)時(shí)間只能有一個(gè)線程執(zhí)行字節(jié)碼
      • CPU 密集程序難以利用多核心優(yōu)勢(shì)
      • IO 期間會(huì)釋放GIL , 對(duì)于 IO 密集型程序影響不大

      如何規(guī)避 CIL 的影響

      • CPU密集可以使用多進(jìn)程 + 進(jìn)程池
      • IO 密集使用多線程 / 協(xié)程
      • cython 擴(kuò)展 

      GIL 的實(shí)現(xiàn)

       GIL 試題

       預(yù)測(cè)一下輸出情況, 按照道理來(lái)說(shuō)執(zhí)行 5k 次, 每次 + 2 結(jié)果應(yīng)該是 10000

      實(shí)際上多次執(zhí)行可以看到偶爾會(huì)出現(xiàn)結(jié)果未保存就被覆蓋的情況與預(yù)期不符

      為什么有了GIL 還要關(guān)注線程安全 

      python 中的原子操作

      •   一個(gè)字節(jié)碼指令就可以完成的就是原子操作
      •   原子操作是可以保證線程安全的
      •   使用 dis 操作可以來(lái)分析字節(jié)碼

      dis 的使用

       

       加鎖解決線程安全問(wèn)題

       加鎖會(huì)導(dǎo)致線程的性能下降, 但是保證了安全

      如何剖析程序性能

      使用各種 profile 工具 (內(nèi)置或者第三方)

      • 二八定律, 大部分時(shí)間都耗時(shí)在少量的代碼上
      • 內(nèi)置的 profile / cprofile 等工具
      • 使用 pyflame (uber開(kāi)源) 的火焰圖工具

      服務(wù)端性能優(yōu)化措施

      • web 應(yīng)用一般語(yǔ)言不會(huì)成為瓶頸
      • 數(shù)據(jù)結(jié)構(gòu)與算法優(yōu)化
      • 數(shù)據(jù)庫(kù)層: 索引優(yōu)化, 慢查詢優(yōu)化, 批量操作減少 IO, NoSQL
      • 網(wǎng)絡(luò) IO: 批量操作, pipeline 操作減少IO
      • 緩存: 使用內(nèi)存數(shù)據(jù)庫(kù) redis / memcached 
      • 異步:  asyncio / celery
      • 并發(fā): gevent / 多線程

      Python 生成器 與 協(xié)程 

      Generator 

      • 生成器就是可以生成值的函數(shù)
      • 當(dāng)一個(gè)函數(shù)里有了 yield關(guān)鍵字就成了生成器
      • 生成器可以掛起執(zhí)行兵器保持當(dāng)前執(zhí)行的狀態(tài)

      基于生成器的協(xié)程

      py3 之前是沒(méi)有原生協(xié)程的

      py2 是基于生成器來(lái)實(shí)現(xiàn)的協(xié)程

      生成器可以通過(guò) yield 暫停執(zhí)行和產(chǎn)出數(shù)據(jù)

      同時(shí)支持 send() 向生成器發(fā)送數(shù)據(jù)和 throw() 向生成器拋異常

      協(xié)程注意點(diǎn)

      協(xié)程需要使用  send(None)  或者  next(coroutine)  來(lái)預(yù)激才能啟動(dòng)

      在  yield  處協(xié)程會(huì)暫停執(zhí)行

      單獨(dú)的  yield value  會(huì)產(chǎn)出值給調(diào)用方

      可以通過(guò)  coroutine.send(value)  來(lái)給協(xié)程發(fā)送值

      發(fā)送的值會(huì)賦值給 yield 表達(dá)式左邊的變量  value = yield  

      協(xié)程完成后(沒(méi)有遇到下一個(gè) yield語(yǔ)句) 會(huì)拋出  StopIteration  異常

      協(xié)程裝飾器

       每次預(yù)激很麻煩, 裝飾器在每次調(diào)用的時(shí)候先自動(dòng)執(zhí)行一個(gè)  next  方法從而進(jìn)行預(yù)激

      Python3 原生協(xié)程

      在 3.5 版本的時(shí)候引入的 async / await 支持原生協(xié)程

      Python 單元測(cè)試

      針對(duì)程序模塊進(jìn)行正確性的驗(yàn)證, 一個(gè)函數(shù), 一個(gè)類進(jìn)行驗(yàn)證

      保證代碼邏輯的正確性

      單測(cè)影響設(shè)計(jì), 易測(cè)的代碼往往是高內(nèi)聚低耦合的

      回歸測(cè)試, 防止改一處導(dǎo)致整個(gè)服務(wù)不可用

      單元測(cè)試相關(guān)庫(kù)

      nose/pytest 較為常用

      mock 模塊用來(lái)模擬替換網(wǎng)絡(luò)請(qǐng)求

      coverage 統(tǒng)計(jì)測(cè)試覆蓋率

      測(cè)試實(shí)例

      安裝 

      pip install pytest

      待測(cè)函數(shù)

      一個(gè)二分查找, 找到值返回值的位置, 找不到返回 -1 

       測(cè)試用例編寫

       自動(dòng)執(zhí)行測(cè)試用例

      pytest xxx.py 

      正確執(zhí)行時(shí)

      存在錯(cuò)誤時(shí)

       

      Python 內(nèi)置的數(shù)據(jù)結(jié)構(gòu)算法常考

      collections 模塊相關(guān)的數(shù)據(jù)結(jié)構(gòu)擴(kuò)展

       命名元祖 - 方便可讀性

       雙端隊(duì)列 - 方便前后存取

       deque 可以很方便的實(shí)現(xiàn) queue / stack 

      Counter - 計(jì)數(shù)器

       OrderedDict  - 有序字典

       

       OrderedDict 的 key 順序是第一次插入的順序, 可以用來(lái)實(shí)現(xiàn) LRUCache 

      DefaultDict  - 默認(rèn)字典

      帶默認(rèn)值的字段

      Pytnon dict 底層結(jié)構(gòu)

      dict 底層使用的是哈希表

      哈希表的平均查找事件復(fù)雜度是 O(1)

      CPython 解釋器使用二次探查解決哈希沖突問(wèn)題

      常問(wèn)的問(wèn)題 哈希沖突和擴(kuò)容

      Python list / tuple 區(qū)別

      都是線性結(jié)構(gòu), 支持下標(biāo)訪問(wèn)

      list 不能作為字典的 key, tuple 可以 (可變對(duì)象不可hash)

      list 可變對(duì)象, tuple 保存的引用不可變 

      什么是 LRUCache 

      原理

      Least-Recently-Used 替換掉最近最少使用的對(duì)象

      緩存剔除策略, 當(dāng)緩存空間不足的時(shí)候需要確定一種方式進(jìn)行剔除key

      常見(jiàn)的有 LRU , LFU 等

      LRU 通過(guò)循環(huán)雙端隊(duì)列不斷把最新訪問(wèn)的 key 放在表頭實(shí)現(xiàn)

      實(shí)現(xiàn)

      # -*- coding: utf-8 -*-
      from collections import OrderedDict
      
      
      class LRUCache:
          
          def __init__(self, max_num=128):
              self.od = OrderedDict()
              self.max_num = max_num
          
          def get(self, k):  # 每次訪問(wèn)更新使用的 key
              if k in self.od:
                  v = self.od[k]
                  # 放在最尾部 (最右邊), 表示最新使用過(guò)
                  self.od.move_to_end(k)
                  return v
              else:
                  return -1
          
          def put(self, k, v):  # 更新 k/v
              if k in self.od:
                  del self.od[k]
                  self.od[k] = v  # 更新 key 到表頭
              else:  # 插入
                  self.od[k] = v
                  # 判斷容量
                  if len(self.od) > self.max_num:
                      # 滿了刪除最早的 key (最沒(méi)人用的)
                      self.od.popitem(last=False)

      數(shù)據(jù)結(jié)構(gòu)常考題

      常見(jiàn)的數(shù)據(jù)結(jié)構(gòu) 鏈表, 隊(duì)列, 棧, 二叉樹(shù), 堆

      使用內(nèi)置的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)高級(jí)的數(shù)據(jù)結(jié)構(gòu), 比如內(nèi)置的 list / deque 實(shí)現(xiàn)棧

      Leetcode 或者 劍指offer 上的常見(jiàn)題

      鏈表

      • 如何使用 Python 來(lái)表示鏈表結(jié)構(gòu)
      • 實(shí)現(xiàn)鏈表的常見(jiàn)操作, 比如插入節(jié)點(diǎn), 反轉(zhuǎn)鏈表, 合并多個(gè)鏈表等
      • Leetcode 常見(jiàn)的鏈表題目

      刪除連表的節(jié)點(diǎn)

      這道題要求的輸入是被刪除的節(jié)點(diǎn)而沒(méi)有傳入頭結(jié)點(diǎn), 因此按照正常來(lái)書

      比如刪除 5 我就找到 4  然后讓 4 指向 要被刪除的 5 的next 的1 就可以了

      但是沒(méi)有頭結(jié)點(diǎn)只有 5 , 單鏈表沒(méi)法往前找. 所以是沒(méi)辦法找到 4 

      換個(gè)角度想 知道 5 那 1 是 5 的 next, 9 是 5 的next 的next, 即 5 往后的節(jié)點(diǎn)都是拿得到

      因此可以考慮直接讓 5 換成 1 然后跳過(guò) 1, 直接指向 9, 這樣形式上就相當(dāng)于刪除了 5 

      如何反轉(zhuǎn)鏈表

      原理來(lái)說(shuō)就是由 一個(gè) cur 定義當(dāng)前節(jié)點(diǎn), 然后 pre 進(jìn)行往前指向

      第一個(gè)節(jié)點(diǎn)的指向?yàn)榭? 依次往后移動(dòng)

      把操作節(jié)點(diǎn)的后節(jié)點(diǎn)保存, 然后后指向?qū)傩愿某汕爸赶?/p>

      再將當(dāng)前操作節(jié)點(diǎn)保存為下一個(gè)操作節(jié)點(diǎn)的前指向之后進(jìn)行后一個(gè)節(jié)點(diǎn)的重復(fù)操作

      終止條件是當(dāng)前節(jié)點(diǎn)的后指向不存在時(shí)到達(dá)結(jié)尾結(jié)束

      # -*- coding: utf-8 -*-
      
      
      class ListNode:
          def __init__(self, x):
              self.val = x
              self.next = None
      
      
      def reverse_list(head):
          # 定義一個(gè)前指向的屬性
          pre = None  # 第一個(gè)進(jìn)來(lái)讓往前直到 None
          cur = head
          # cur 會(huì)從 1 -> 2 -> 3 -> 4 -> None 的順序移動(dòng)
          while cur:
              nextnode = cur.next  # 保存下來(lái)后指向的 next 值
              cur.next = pre  # 將 pre 覆蓋 next
              pre = cur  # 下一個(gè)的前指向?yàn)楫?dāng)前操作節(jié)點(diǎn)
              cur = nextnode  # 后移進(jìn)行下一個(gè)的操作
          return pre
      
      
      if __name__ == '__main__':
          # 1 - 2 - 3 - 4
          ln = ListNode(1)
          ln.next = ListNode(2)
          ln.next.next = ListNode(3)
          ln.next.next.next = ListNode(4)
          ln = reverse_list(ln)
          print(ln.val, ln.next.val, ln.next.next.val, ln.next.next.next.val)  # 4 3 2 1

      合并兩個(gè)有序列表

      有兩種方法可以實(shí)現(xiàn), 一種是將 l1 插入到 l2 中

      還有一種是創(chuàng)建一個(gè) l3, 然后將 l1 l2 分別插入到 l3 中

      以下為第二種方式, l1 和 l2 之間進(jìn)行彼此比較從而決定誰(shuí)來(lái)追加進(jìn)入 l3, 每次被加入進(jìn)去的指針往后移

      同時(shí) l3 每次追加后也要將指針后移, 最后將第一個(gè)節(jié)點(diǎn)之后的輸出既可

      # -*- coding: utf-8 -*-
      
      
      class ListNode:
          def __init__(self, x):
              self.val = x
              self.next = None
      
      
      def extend_list(l1, l2):
          if not l1 and not l2:
              return
          if not l1:
              return l2
          if not l2:
              return l1
          l3 = ListNode(0)
          # 頭結(jié)點(diǎn)保存下來(lái)
          l3_head_node = l3
          while l1 and l2:
              if l1.val <= l2.val:
                  l3.next = l1
                  l1 = l1.next  # l1 后移
              else:
                  l3.next = l2
                  l2 = l2.next  # l2 后移
              l3 = l3.next  # l3 后移
          
          while l1 or l2:
              l3.next = l1 or l2
              if l1:
                  l1 = l1.next
              if l2:
                  l2 = l2.next
              l3 = l3.next
          
          return l3_head_node.next
      
      
      if __name__ == '__main__':
          # 1 - 2 - 3 - 4
          ln1 = ListNode(1)
          ln1.next = ListNode(2)
          ln1.next.next = ListNode(3)
          ln1.next.next.next = ListNode(4)
          # 2 - 3 - 4 - 5
          ln2 = ListNode(2)
          ln2.next = ListNode(3)
          ln2.next.next = ListNode(4)
          ln2.next.next.next = ListNode(5)
      
          ret = extend_list(ln1, ln2)
          ln_list = []
          while ret:
              ln_list.append(ret.val)
              ret = ret.next
          print(ln_list)  # [1, 2, 2, 3, 3, 4, 4, 5]

      隊(duì)列

      隊(duì)列是先進(jìn)先出的隊(duì)列結(jié)構(gòu)

        如何使用  Python 實(shí)現(xiàn)隊(duì)列

        實(shí)現(xiàn)隊(duì)列的 append 和 pop 操作,  如何做到先進(jìn)先出

        使用 Python 的 list 或者 collection.deque 實(shí)現(xiàn)隊(duì)列

      代碼實(shí)現(xiàn)

       

      棧 ( stack ) 是后進(jìn)先出的結(jié)構(gòu)

        如何使用 Python 實(shí)現(xiàn)棧

        實(shí)現(xiàn)棧的 push 和 pop 操作, 如何做到后進(jìn)先出

        同樣可以用 Python list 或者 collection.deque 實(shí)現(xiàn)棧

      代碼實(shí)現(xiàn)

       ps:

        如何用兩個(gè)棧實(shí)現(xiàn)一個(gè)隊(duì)列?

      from collections import deque
      
      
      class Stack:
          def __init__(self):
              self.items = deque()
          
          def push(self, v):
              return self.items.append(v)
          
          def pop(self):
              return self.items.pop()
          
          def len(self):
              return len(self.items)
      
      
      class Queue:
          def __init__(self, q1, q2):
              self.q1 = q1
              self.q2 = q2
          
          def append(self, v):
              return self.q1.push(v)
          
          def pop(self):
              while self.q1.len():
                  self.q2.push(self.q1.pop())
              return self.q2.pop()
      
      
      if __name__ == '__main__':
          q = Queue(Stack(), Stack())
          q.append(1)
          q.append(2)
          q.append(3)
          print(q.pop()) # 1
          print(q.pop()) # 2
          print(q.pop()) # 3
      View Code

        實(shí)現(xiàn)獲取最小值的棧 MinStack 

      class MinStack(object):
          def __init__(self):
              self.stack = []
              self.min_stack = []
          
          def push(self, x):
              self.stack.append(x)
              if len(self.min_stack) == 0 or x <= self.min_stack[-1]:
                  self.min_stack.append(x)
          
          def pop(self):
              if not self.is_empty():
                  if self.stack.pop() == self.min_stack[-1]:
                      self.min_stack.pop()
              return
          
          def get_min(self):
              if not self.is_empty():
                  return self.min_stack[-1]
          
          def is_empty(self):
              return len(self.stack) < 1
      
      
      if __name__ == "__main__":
          minstack = MinStack()
          minstack.push(2)
          minstack.push(0)
          minstack.push(3)
          minstack.push(0)
          print(minstack.get_min())
          minstack.pop()
          print(minstack.get_min())
          minstack.pop()
          print(minstack.get_min())
          minstack.pop()
          print(minstack.get_min())
          
          """
          0
          0
          0
          2
          """
      View Code

       字典與集合

      Python dict / set 底層都是哈希表

        哈希表的實(shí)現(xiàn)原理, 底層就是一個(gè)數(shù)組

        根據(jù)哈希函數(shù)快速定位一個(gè)元素, 平均查找 O(1) , 非常快

        不斷加入新的元素會(huì)引起哈希表重新開(kāi)辟空間, 拷貝之前元素到新數(shù)組

      哈希表如何解決沖突 

        元素 key 沖突之后使用一個(gè)鏈表填充相同 key 的元素 , 比如這里 5 沖突了. 就往外再次連接槽進(jìn)行格外的存儲(chǔ) 

              ( 正常一個(gè)元素只能存放一個(gè)元素, 存放鏈表既可填充多個(gè)沖突元素 )

        開(kāi)放尋址法是沖突之后根據(jù)某種方式 (二次探查) 尋找下一個(gè)可用的槽

          ( 二次是指使用的是一元二次方程進(jìn)行計(jì)算 )

      二叉樹(shù)

      二叉樹(shù)很多操作都可以用遞歸非方式解決

      常考題: 二叉樹(shù)的鏡像,  層序遍歷二叉樹(shù) (廣度優(yōu)先)

      二叉樹(shù)的鏡像

      其實(shí)就是左右孩子的交換, 從根節(jié)點(diǎn)開(kāi)始往下遍歷

      先序, 中序, 后序遍歷的實(shí)現(xiàn)

      • 先序: 先處理根, 之后左子樹(shù)嗎然后右子樹(shù)
      • 中序: 先處理左子樹(shù), 之后根, 然后右子樹(shù)
      • 后序: 先處理左子樹(shù), 然后右子樹(shù), 最后根

      代碼實(shí)現(xiàn) 

      層序遍歷二叉樹(shù) (廣度優(yōu)先)

      定義兩個(gè) cur 當(dāng)前層級(jí)節(jié)點(diǎn) 和 next 下一層節(jié)點(diǎn) 然后通過(guò) 移動(dòng) cur 來(lái)定義 next 再用 next 替換 cur 從而一層一層的往下延伸

      結(jié)束條件 cur 或者 next 為空的時(shí)候就可以停止了, 表示當(dāng)前沒(méi)有節(jié)點(diǎn)或者當(dāng)前層沒(méi)有下一層節(jié)點(diǎn)了

       

       

       

       堆

       堆其實(shí)就是完全二叉樹(shù), 有最小堆和最大堆

      •   最大堆: 對(duì)于每個(gè)非葉子節(jié)點(diǎn)V, V 的值都比他的兩個(gè)孩子大
      •   最小堆: 對(duì)于每個(gè)非葉子節(jié)點(diǎn)V, V 的值逗比他的兩個(gè)孩子小
      •   最大堆支持每次 pop 操作獲取最大的元素, 最小堆獲取最小元素 ( 堆頂 )

      利用 堆來(lái)完成 topK 問(wèn)題, 從海量數(shù)字中獲取最大的 k 個(gè)元素

      通常的場(chǎng)景是說(shuō)有海量的數(shù)字, 但是很少的內(nèi)存, 想要獲取里面最大的10個(gè)元素

      獲取最大元素那就使用最小堆來(lái)處理, 取數(shù)據(jù)的前10個(gè)建立最小堆

      然后遍歷剩下的數(shù)字和堆頂進(jìn)行比較, 如果小于堆頂那必然不是 top10

      如果大于堆頂. 則調(diào)整堆納入新數(shù)字作為堆頂重新調(diào)整堆

      相關(guān)代碼

      import heapq
      
      
      class TopK:
          def __init__(self, items, k):
              self.min_hq = []
              self.max_num = k
              self.items = items
          
          def push(self, v):
              if len(self.min_hq) >= self.max_num:
                  min_v = self.min_hq[0]
                  if v < min_v:
                      pass
                  else:
                      heapq.heapreplace(self.min_hq, v)
              else:
                  heapq.heappush(self.min_hq, v)
          
          def get_topk(self):
              for v in self.items:
                  self.push(v)
              return self.min_hq
      
      
      if __name__ == '__main__':
          import random
          i = list(range(1000))
          random.shuffle(i)
          _ = TopK(i, 10)
          print(_.get_topk())
          # [990, 991, 992, 993, 994, 999, 995, 997, 996, 998]

       如何合并 k 個(gè)有序鏈表 

       

       

       輸入的是一個(gè)列表, 里面存放的是每個(gè)序列的頭結(jié)點(diǎn)

       之前做過(guò)兩個(gè)鏈表的合并, 這里當(dāng)然也可以使用此方法, 兩個(gè)鏈表合并之后再依次和后面的每個(gè)合并

      但是這里使用堆會(huì)更加簡(jiǎn)單, 將所有的數(shù)據(jù)放進(jìn)最小堆, 然后利用最小堆每次彈出堆頂在組成鏈表

      即可實(shí)現(xiàn)一個(gè)從小到大排列的有序鏈表

      利用 python 的 headq 就可以簡(jiǎn)單的實(shí)現(xiàn)

      字符串

      翻轉(zhuǎn)一個(gè)字符串

      有個(gè)特殊要求, 不能使用額外的空間 

      輸入的是 [""] , 這里使用 reversed 卻無(wú)法修改這個(gè)數(shù)據(jù)本身, 做不到原地修改

      當(dāng)然 str 本身也有 reverse 方法而且是原地修改, 但是這樣就沒(méi)意思了

      這里采用兩個(gè)指針?lè)謩e從兩頭彼此進(jìn)行互換即可

      判斷一個(gè)數(shù)字是否是回文數(shù)

      首先負(fù)數(shù)肯定不會(huì)是回文數(shù), 解題思路如上題類似, 也是雙指針進(jìn)行對(duì)比

      判斷是否一樣即可, 不一樣直接返回 False 即可

      Python 面向?qū)ο笙嚓P(guān)考題

      什么是面向?qū)ο缶幊?/h2>

      把對(duì)象作為基本單元, 把對(duì)象抽象成類 (Class), 包含成員和方法

      數(shù)據(jù)封裝, 繼承, 多態(tài) 

      Python 中使用類來(lái)實(shí)現(xiàn), 過(guò)程式編程( 函數(shù) ), OPP (類)

      優(yōu)先使用組合而非繼承

      組合是使用其他的類實(shí)例作為自己的一個(gè)屬性 (Has-a 關(guān)系)

      子類繼承父類的屬性和方法 (Is a 關(guān)系)

      優(yōu)先使用組合保持代碼簡(jiǎn)單, 如下示例

      使用 deque 作為自己ide一個(gè)屬性, 從而利用此屬性的內(nèi)部的方法進(jìn)行操作

      而非直接繼承 deque 來(lái)使用 deque 的方法. 

      類變量和實(shí)例變量的區(qū)別

      類變量由所有的實(shí)例共享 (類內(nèi)的聲明變量)

      實(shí)例變量由實(shí)例單獨(dú)享有, 不同實(shí)例之間不影響 (實(shí)例初始化聲明的屬性)

      當(dāng)我們需要在一個(gè)類的不同實(shí)例之間同享變量的時(shí)候可以使用類變量

      classmethod / staticmethod 的區(qū)別

      都可以使用 Class.method() 的方式使用

      classmethod 第一個(gè)參數(shù)是 cls, 可以引用類變量

      staticmethod 使用起來(lái)和普通的函數(shù)一樣, 只不過(guò)放在類里面去組織

      staticmethod 裝飾的函數(shù)的第一個(gè) self 參數(shù)可以忽略

      就目的而言: 

        classmethod  是為了不實(shí)例化直接使用類變量

        staticmethod  是為了組織美化代碼強(qiáng)制面向?qū)ο?/p>

      示例

      什么是元類, 使用場(chǎng)景

      元類 (Meta Class) 是創(chuàng)建類的類

      元類允許我們控制類的生成, 比如修改類的屬性等

      元類最常見(jiàn)的使用場(chǎng)景 ORM 框架

      使用 type 來(lái)定義元類

       

      帶繼承的示例

      帶方法的實(shí)例

      自我實(shí)現(xiàn)實(shí)例

      Python 設(shè)計(jì)模式常考

      設(shè)計(jì)模式分類

      行為型, 創(chuàng)建型, 結(jié)構(gòu)型 三大類

      創(chuàng)建型

        工廠模式: 解決對(duì)象創(chuàng)建問(wèn)題

        構(gòu)造模式: 控制復(fù)雜對(duì)象的創(chuàng)建, 通常用于比較復(fù)雜的對(duì)象分布進(jìn)行創(chuàng)建

        原型模式: 通過(guò)原型的克隆創(chuàng)建新的實(shí)例, 對(duì)于一些創(chuàng)建實(shí)例開(kāi)銷較高的地方來(lái)使用

        單例模式: 一個(gè)類只能創(chuàng)建同一個(gè)對(duì)象, 面試中較多問(wèn)到, python的導(dǎo)入就是單例, 使用共享同一個(gè)實(shí)例的方式來(lái)創(chuàng)建

        對(duì)象池模式: 預(yù)先分配同一類型的一組實(shí)例

        惰性計(jì)算模式: 延遲計(jì)算 ( Python 的 property )

      工廠模式 demo

      構(gòu)造模式 demo

       

       

       

      單例模式 demo

      # 單例模式
      class SingLeton:
          def __new__(cls, *args, **kwargs):
              if not hasattr(cls, "_instance"):
                  _instance = super().__new__(cls, *args, **kwargs)
                  cls._instance = _instance
              return cls._instance
      
      
      class Myclass(SingLeton):
          pass
      
      
      if __name__ == '__main__':
          a = Myclass()
          b = Myclass()
          print(a is b)  # True
          print(id(a), id(b))  # 2350011243712 2350011243712

      結(jié)構(gòu)型

        裝飾器模式: 無(wú)需子類化擴(kuò)展對(duì)象功能

        代理模式: 吧一個(gè)對(duì)象的操作代理到另一個(gè)對(duì)象, 

        適配器模式: 通過(guò)一個(gè)間接層適配統(tǒng)一接口

        外觀模式: 簡(jiǎn)化復(fù)雜對(duì)象的訪問(wèn)問(wèn)題

        享元模式: 通過(guò)對(duì)象復(fù)用(池) 改善資源利用, 比如連接池

        MVC模式: 解耦展示邏輯和業(yè)務(wù)邏輯

       適配器 demo

       

       這里使用 make_noise 方法就可以統(tǒng)一的使用了不同類的不同方法了. 

      代理模式 demo

      行為型

        迭代器模式: 通過(guò)統(tǒng)一的接口迭代對(duì)象, python 內(nèi)置迭代器  for  可以進(jìn)行遍歷, 可以用  __next__ ,  __iter__  實(shí)現(xiàn)迭代器

        觀察者模式: 對(duì)象發(fā)生改變的時(shí)候, 觀察者執(zhí)行相應(yīng)的動(dòng)作, 比如發(fā)布訂閱, 可以通過(guò)回調(diào)等方式實(shí)現(xiàn)

        策略模式: 針對(duì)不同的規(guī)模的輸入使用不同的策略, 一般可以用于打折優(yōu)惠券之類的邏輯

       迭代器 demo 

       

       觀察者 demo

      發(fā)布訂閱系統(tǒng)

       

       

      這里的 data 函數(shù)增加了 property 的裝飾器, 而且增加了 setter 的操作以及在內(nèi)部邏輯中 增加 self.notify()

      從而每次被 set 更新 self._data  的時(shí)候進(jìn)行觸發(fā). 觸發(fā)每個(gè)觀察者的 notify_by 方法

      最終的打印結(jié)果

      裝飾器模式相關(guān)原理以及不同方法實(shí)現(xiàn)

      什么是閉包

      閉包: 引用了外部自由變量的函數(shù)

      自由變量: 不在當(dāng)前函數(shù)定義的變量

      特性: 自由變量會(huì)和閉包函數(shù)同時(shí)存在

      常見(jiàn)應(yīng)用: 裝飾器就是最常見(jiàn)的閉包

      實(shí)例

       簡(jiǎn)單來(lái)說(shuō)就是一個(gè)函數(shù)的內(nèi)部函數(shù)使用了外部函數(shù)的變量就是閉包

       而且外部函數(shù)哪怕結(jié)束了, 但是如果內(nèi)部函數(shù)還在使用這個(gè)外部的變量

      那這個(gè)變量就會(huì)一直存在

      Decorator  定義

      Python 中一切皆對(duì)象, 函數(shù)可以作為參數(shù)進(jìn)行傳遞

      裝飾器是接受函數(shù)作為關(guān)鍵字, 添加功能后返回一個(gè)新函數(shù)的函數(shù) (類)

      Python 中通過(guò) @ 使用裝飾器

      property 語(yǔ)法糖

      class Dog:
          def __init__(self):
              self.__age = 13
      
          def get_age(self):
              return self.__age
      
          def set_age(self, v):
              self.__age = v
              return
      
          @property
          def age(self):
              return self.__age
      
          @age.deleter
          def age(self):
              del self.__age
      
          @age.setter
          def age(self, v):
              self.__age = v
      
      
      if __name__ == '__main__':
          d = Dog()
          # 正常的獲取方式
          # print(d.__age) # 雙下劃線私有屬性是無(wú)法直接訪問(wèn)會(huì)報(bào)錯(cuò)
          print(d.get_age())  # 只能通過(guò)寫一個(gè)方法來(lái)獲取到, 正常方式獲取
          print(d.age)  # 13 # 利用 property 裝飾器可以更方便的獲取
      
          d.set_age(5)  # 正常方式的賦值操作
          d.age = 5  # age.setter 裝飾器可以允許使用直接賦值操作
          print(d.age)  # 5
          del d.age  # age.deleter 裝飾器可以允許使用直接賦值操作

       

      編寫一個(gè)記錄函數(shù)耗時(shí)的裝飾器

      函數(shù)方法實(shí)現(xiàn)

       類方法實(shí)現(xiàn)

      利用裝飾器給函數(shù)加參數(shù)

      使用類裝飾器的方法可以很方便的添加裝飾器的參數(shù)

       

      Python  函數(shù)式編程常考題

      • lambda 演算
      • 高階函數(shù) map, reduce, filter 
      • 函數(shù)是編程無(wú)副作用, 相同的參數(shù)調(diào)用始終產(chǎn)生同樣的結(jié)果

      map 的使用

      map 是將一個(gè)序列按照前一個(gè)方法中的邏輯進(jìn)行統(tǒng)一處理 

      通常來(lái)說(shuō)更推薦使用列表推導(dǎo)來(lái)實(shí)現(xiàn), 但是map可以傳入 遠(yuǎn)比lambda 更復(fù)雜的函數(shù)進(jìn)行復(fù)雜運(yùn)算

      以此列表推導(dǎo)是實(shí)現(xiàn)不了的. 但是map 的用法更適用于函數(shù)式編程. 會(huì)影響代碼可讀性

      reduce 的使用

       python 2 中是自帶的., 在 python 3 中需要導(dǎo)入

       reduce 是將前面的結(jié)果往后繼續(xù)操作, 比如這里的 0+1+2+3+4

       

      filter 的使用

       

      filter 則是進(jìn)行了每個(gè)元素的判斷是否進(jìn)行保留 

      Linux 操作系統(tǒng)相關(guān)問(wèn)題

      •  熟練在 Linux 服務(wù)器上操作
      • 了解 Linux 工作原理和常用工具
      • 需要了解查看文件, 進(jìn)程, 內(nèi)存相關(guān)的一些命令, 用以調(diào)試和排查

      如何知道一個(gè)命令的用法

      man 命令查詢用法, 但是 man 手冊(cè)較為復(fù)雜

      工具自帶的 help 比如 pip -- help

      man 的替代品 tldr   pip install tldr   此工具較為簡(jiǎn)單且提供實(shí)例

      文件 / 目錄相關(guān)操作命令

      chown / chmod  / cdgrp 

      ls / rm / cd / cp / mv / rouch / rename / ln ( 軟連接和硬鏈接) 等

      locate / find / grep 定位查找和搜索

      編輯器 vi / nano 

      cat / head / tail 查看文件

      more / less 交互式查看文件

      進(jìn)程相關(guān)的命令

      ps 查看進(jìn)程

      kill 殺死進(jìn)程 常用  kill -9 進(jìn)程號(hào) 

      top / htop 監(jiān)控進(jìn)程

      內(nèi)存操作相關(guān)工具命令

      free 查看可用內(nèi)存

      了解每一列的具體含義

      排查內(nèi)存泄露問(wèn)題

      網(wǎng)絡(luò)操作命令

      ifconfig 查看網(wǎng)卡信息

      lsof / netstat 查看端口信息

      ssh / scp 遠(yuǎn)程登錄 / 復(fù)制 

      tcpdump 抓包

      常見(jiàn)用戶和組操作

      useradd / usermod 

      groupadd / groupmod

      軟連接和硬鏈接的區(qū)別

      簡(jiǎn)單來(lái)說(shuō)

        軟連接類似于 Windows 里面的快捷方式

      原理來(lái)說(shuō)

        軟鏈接是個(gè)文本文件, 里面保存了指向的絕對(duì)路徑

        軟連接在訪問(wèn)的時(shí)候回替換為保存的路徑進(jìn)行指向

        linux 里面的文件保存在磁盤上通過(guò) Inode 值進(jìn)行訪問(wèn)

        所有的文件的訪問(wèn)都是通過(guò)各種連接到這個(gè) Inode 值上的區(qū)塊進(jìn)行訪問(wèn)

        硬鏈接是允許一個(gè)文件有多個(gè)鏈接到此區(qū)塊, 即真實(shí)的訪問(wèn)

        這樣如果用戶誤刪某一個(gè)鏈接時(shí), 其他的鏈接依舊可以訪問(wèn)到此區(qū)塊

        而軟連接如果源文件不在的話, 軟鏈接將會(huì)無(wú)意義失效 ( Windows 也是一樣)

      進(jìn)程和線程的區(qū)別

      進(jìn)程是運(yùn)行程序的封裝, 是操作系統(tǒng)的一種并行機(jī)制, 用于調(diào)度的基本單位

      線程是進(jìn)程的子任務(wù), cpu 調(diào)度的基本單位, 實(shí)現(xiàn)是的進(jìn)程內(nèi)的并發(fā)

      進(jìn)程可以包含多個(gè)線程, 但是線程依賴進(jìn)程的存在, 并共享進(jìn)程的內(nèi)存

      什么是線程安全

      一個(gè)線程的修改被另一個(gè)線程的修改搶先或者覆蓋的時(shí)候?qū)е聰?shù)據(jù)偏差

      可以通過(guò)上鎖的機(jī)制進(jìn)行保護(hù), 讓同一份數(shù)據(jù)同時(shí)間內(nèi)只能一個(gè)線程來(lái)訪問(wèn)

      即順序執(zhí)行而非并發(fā)執(zhí)行, 一般如果是讀取數(shù)據(jù)的時(shí)候不需要考慮線程安全問(wèn)題

      寫操作的時(shí)候則需要考慮

      線程同步的方式

      互斥量 (鎖) : 利用互斥鎖讓防止多個(gè)線程同時(shí)訪問(wèn)公共資源

      信號(hào)量 : 控制同一個(gè)時(shí)刻多個(gè)線程訪問(wèn)同一個(gè)資源的線程數(shù)

      事件: 通過(guò)互相通知的方式保持多個(gè)線程同步

      進(jìn)程間通信方式

      管道 / 匿名管道 /  有名管道

      信號(hào) : 比如 Ctrl + c 產(chǎn)生 SIGINT 程序終止信號(hào)

      消息隊(duì)列

      共享內(nèi)存

      信號(hào)量

      套接字 ( socket ): 最常見(jiàn)的方式,  web 應(yīng)用

      Python 如何實(shí)現(xiàn)多線程

      示例

       

      Python 如何實(shí)現(xiàn)多進(jìn)程

      示例

      系統(tǒng)內(nèi)存管理機(jī)制常見(jiàn)考題

      什么是分頁(yè)機(jī)制

      操作系統(tǒng)為了方便管理內(nèi)存以及減少內(nèi)存采用分頁(yè)機(jī)制

      邏輯地址與物理地址分離的內(nèi)存分配管理方案

      程序的邏輯地址劃分為固定大小的頁(yè) (Page)

      物理地址劃分為同樣大小的幀 (Frame)

      通過(guò)頁(yè)表對(duì)應(yīng)邏輯地址和物理地址

      什么是分段機(jī)制

      分段是為了滿足代碼的一些邏輯需求

      數(shù)據(jù)共享, 數(shù)據(jù)保護(hù), 動(dòng)態(tài)鏈接等

      通過(guò)段表實(shí)現(xiàn)邏輯地址和物理地址的映射關(guān)系

      每個(gè)段內(nèi)都是連續(xù)的內(nèi)存分配, 段和段之間是離散的分配

      分頁(yè)和分段的區(qū)別

      頁(yè)是出于內(nèi)存利用率的角度提出的離散的分配機(jī)制

      段是處于用戶的角度用于數(shù)據(jù)保護(hù), 數(shù)據(jù)隔離等用途的管理機(jī)制

      頁(yè)的大小是固定的, 操作系統(tǒng)來(lái)決定, 段的大小不定, 由用戶程序決定

      什么是虛擬內(nèi)存

      通過(guò)把一部分暫時(shí)不用的內(nèi)存信息放在硬盤上, 系統(tǒng)似乎提供比實(shí)際內(nèi)存更大的容量, 稱之為虛擬內(nèi)存

      局部性原理:  時(shí)間 / 空間局部性

        一段內(nèi)存被訪問(wèn)的時(shí)候, 不遠(yuǎn)的未來(lái)可能還會(huì)被訪問(wèn)

        一段內(nèi)存被訪問(wèn)的時(shí)候, 他周圍的內(nèi)存可能也會(huì)被訪問(wèn)

        因此只將程序運(yùn)行時(shí)候的只有部分必要的信息裝入內(nèi)存, 不必要的放在硬盤

      什么是內(nèi)存抖動(dòng) (顛簸)

      本質(zhì)是頻繁的頁(yè)調(diào)度行為導(dǎo)致進(jìn)程不斷產(chǎn)生缺頁(yè)終端

      通常是使用了不當(dāng)?shù)闹脫Q策略 , 常見(jiàn)的置換策略 (先入先出, LRU, LFU 等等)

      剛剛置換了一個(gè)頁(yè), 有不斷的在次需要這個(gè)頁(yè)

      運(yùn)行程序太多, 頁(yè)面替換策略不好, 終止進(jìn)程或者增加物理內(nèi)存等可以解決

      Python 垃圾回收機(jī)制的原理

      引用計(jì)數(shù)為主 (缺點(diǎn): 循環(huán)引用無(wú)法解決)

      引用標(biāo)記清除分代回收解決引用計(jì)數(shù)的問(wèn)題  (為輔)

      引用計(jì)數(shù)的示例

      a = [1]        a-> [1]         [1] 的 ref 為 1      

      在創(chuàng)建  b = a      a-> [1]<-b     [1] 的 ref 為 2 

      b = None     a-> [1]   b->None   [1] 的 ref 為 1

      def  a    [1] 的 ref 為 0  此時(shí) [1] 被回收, del 的原理就是清除掉 a 的引用

      即 有多少個(gè)變量在引用這塊內(nèi)存, ref 為 0 的時(shí)候進(jìn)行回收

       可以通過(guò) sys 的相關(guān)方法進(jìn)行查看

      當(dāng)然這里也有一些誤導(dǎo)

       1 在未被 b 引用之前就已經(jīng)被 python 的底層很多地方引用了

      所以是這么大的數(shù)字

      循環(huán)引用的問(wèn)題

       循環(huán)引用的時(shí)候會(huì)導(dǎo)致 被循環(huán)引用的對(duì)象的 ref 始終無(wú)法清0 從而無(wú)法被清理最終導(dǎo)致內(nèi)存溢出

      標(biāo)記清除

      使用引用圖的形式, 從根對(duì)象進(jìn)行往下找, 只對(duì)可達(dá)對(duì)象進(jìn)行保留

      不可達(dá)對(duì)象進(jìn)行清除, 根對(duì)象是值棧內(nèi)的對(duì)象

      分代回收

      python 將所有的對(duì)象的生命周期氛圍 0 , 1 , 2 三代

      使用雙端鏈表進(jìn)行保存, 然后對(duì)每一代進(jìn)行標(biāo)記清除

      比如 對(duì)第 0 代清除之后還存活的移入第 1 代, 以此類推

       gc 模塊中可以進(jìn)行這方面的操作比如這里看到第0代保存在第700 的時(shí)候進(jìn)行第0代的回收

      網(wǎng)絡(luò)協(xié)議相關(guān)

      瀏覽器輸入一個(gè) url 中間經(jīng)歷的過(guò)程

      • 中間都都涉及了哪些過(guò)程
      • 包含了哪些網(wǎng)絡(luò)協(xié)議
      • 每個(gè)協(xié)議都干了什么

      TCP / UDP 相關(guān)考題 - 三次 / 四次 / 區(qū)別 / 實(shí)現(xiàn)

      TCP 的三次握手過(guò)程

       

      TCP 的四次揮手過(guò)程

      TCP 和  UDP 的區(qū)別

      TCP / UDP socket 編程

      使用 socket 模塊. 建立 tcp / udp 客戶端和服務(wù)端, 實(shí)現(xiàn)客戶端和服務(wù)端之間的通信

       

      TCP 實(shí)例

      server 端

      import socket
      import time
      
      s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      s.bind(('', 8888))
      s.listen()
      
      while True:
          conn, addr = s.accept()
          print(conn)
          timestr = time.ctime(time.time()) + '\r\n'
          conn.send(timestr.encode())  # send 參數(shù) encode('utf8')
          conn.close()

      client 端

      import socket
      
      s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      s.connect(('127.0.0.1', 8888))
      s.sendall(b"Hello World")
      data = s.recv(1024)
      print(data)
      s.close()

      打印結(jié)果

      server 端

      client 端

      HTTP 協(xié)議常考題 - 請(qǐng)求方式 / 報(bào)文格式 / 狀態(tài)碼 / 長(zhǎng)連接 / 用戶識(shí)別 / 實(shí)現(xiàn)

      代碼請(qǐng)求發(fā)起方式

      常用如使用 curl 

       python 更直觀的展示可以安裝  httppie  pip install httpie  在命令行發(fā)起, 

      加參數(shù)  -v 可以打印整個(gè)過(guò)程

      HTTP 請(qǐng)求的組成

      狀態(tài)行 , 請(qǐng)求頭, 消息主體

       

       HTTP 響應(yīng)的組成

       狀態(tài)行 , 響應(yīng)頭, 響應(yīng)正文

       

       HTTP 常見(jiàn)狀態(tài)碼

       

       比如 220(成功) , 301(永久重定向), 302(臨時(shí)重定向) , 400(請(qǐng)求錯(cuò)誤), 403, 500等 

      HTTP 方法

      常見(jiàn)的 HTTP 方法

      GET  獲取

      POST  創(chuàng)建

      PUT  更新

      DELETE   刪除

      HTTP GET / POST 區(qū)別

      Restful 語(yǔ)義上一個(gè)是獲取, 一個(gè)是創(chuàng)建

      GET 是冪等的, POST是非冪等的 (a=4 是冪等, a+=4 是非冪等的, 冪等可以安全的重發(fā)請(qǐng)求)

      GET 請(qǐng)求參數(shù)放在 url  (明文), 存在長(zhǎng)度限制, POST 放在請(qǐng)求體, 更安全

      冪等方法

      什么是 HTTP 長(zhǎng)連接

      短連接: 建立鏈接 - 數(shù)據(jù)傳輸 - 關(guān)閉鏈接  (鏈接的建立和關(guān)閉的開(kāi)銷都很大)

      長(zhǎng)連接 : 保持 TCP 鏈接不斷開(kāi)

      HTTP 1.1 的時(shí)候?qū)崿F(xiàn)了長(zhǎng)連接  在請(qǐng)求頭中   Connection: keep-alive   即可實(shí)現(xiàn)長(zhǎng)連接

      在接受完畢請(qǐng)求之后不會(huì)馬上關(guān)閉鏈接. 而是繼續(xù)等待下一個(gè)請(qǐng)求的到來(lái)

      因此也出現(xiàn)了一個(gè)問(wèn)題, HTTP 如何在長(zhǎng)連接中識(shí)別每一次的請(qǐng)求

      客戶端告訴服務(wù)端發(fā)生的請(qǐng)求長(zhǎng)度即可, 可通過(guò)  Content-Length  指定長(zhǎng)度

      但是不定長(zhǎng)的動(dòng)態(tài)報(bào)文的時(shí)候則可以通過(guò)  Transfer-Encoding  分段發(fā)送請(qǐng)求

      cookie 和 session 的區(qū)別

      HTTP 是無(wú)狀態(tài)的, 無(wú)法對(duì)每一次請(qǐng)求的客戶進(jìn)行識(shí)別

      Cookie : 實(shí)現(xiàn) session 的一種機(jī)制, 通過(guò) HTTP 的 cookie 字段實(shí)現(xiàn)

      Session : 在服務(wù)端給用戶生成一個(gè)標(biāo)識(shí), 每次讓客戶端帶過(guò)去 (通過(guò) url 參數(shù), 或者 cookie ) 給后端讓后端去識(shí)別

      ps:

        Session 通過(guò)服務(wù)器保存 sessionid 識(shí)別用戶, Cookie  存儲(chǔ)在客戶端

      如何使用 socket 發(fā)送 HTTP 請(qǐng)求

      利用 socket 建立 TCP 鏈接 (HTTP 建立在 TCP 基礎(chǔ)之上)

      然后發(fā)送 HTTP 格式的報(bào)文 (HTTP 是基于文本的協(xié)議)

      import socket
      
      s = socket.socket()
      s.connect(("www.baidu.com", 80))
      http = b"GET / HTTP/1.1\r\nHost: www.baidu.com\r\n\r\n"
      s.sendall(http)
      buf = s.recv(1024)
      print(buf)
      s.close()

      返回結(jié)果

      b'HTTP/1.1 200 OK\r\nBdpagetype: 1\r\nBdqid: 0x966f65a400058cc7\r\nCache-Control: private\r\nContent-Type: text/html;charset=utf-8\r\nDate: Thu, 25 Mar 2021 07:37:06 GMT\r\nExpires: Thu, 25 Mar 2021 07:36:36 GMT\r\nP3p: CP=" OTI DSP COR IVA OUR IND COM "\r\nP3p: CP=" OTI DSP COR IVA OUR IND COM "\r\nServer: BWS/1.1\r\nSet-Cookie: BAIDUID=D837E597D6078C0CC6FAB4602D1FB524:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com\r\nSet-Cookie: BIDUPSID=D837E597D6078C0CC6FAB4602D1FB524; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com\r\nSet-Cookie: PSTM=1616657826; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com\r\nSet-Cookie: BAIDUID=D837E597D6078C0C5A5B2B3EAA2B99C7:FG=1; max-age=31536000; expires=Fri, 25-Mar-22 07:37:06 GMT; domain=.baidu.com; path=/; version=1; comment=bd\r\nSet-Cookie: BDSVRTM=0; path=/\r\nSet-Cookie: BD_HOME=1; path=/\r\nSet-Cookie: H_PS_PSSID=33742_33273_33693_33759_33676_33392_26350; path=/; domain=.baidu.com\r\nTraceid: 161'

      IO 多路復(fù)用

      五種 IO 模型

       

      常見(jiàn)的并發(fā)請(qǐng)求處理

      多線程和多進(jìn)程可以處理

      但是多線程進(jìn)程的創(chuàng)建開(kāi)銷對(duì)服務(wù)器的資源消耗較大 

      而使用 IO 多路復(fù)用可以在單進(jìn)程下處理多個(gè)請(qǐng)求

      什么是 IO 多路復(fù)用

      操作系統(tǒng)提供的同時(shí)監(jiān)聽(tīng)多個(gè) socket 的機(jī)制

      Linux 下常見(jiàn)的是 select / poll / epoll 

      常用的代碼格式

      select / poll / epoll  的區(qū)別

       

       epoll 的時(shí)間復(fù)雜度最小, 大多數(shù)時(shí)間都是選擇使用 epoll 

      Python 如何實(shí)現(xiàn) IO 多路復(fù)用

      select / poll / epoll 模塊

      py2  select 模塊

      py3 selectors 模塊

      官方的示例

      Python 并發(fā)網(wǎng)絡(luò)庫(kù)

      Tornado 

      適用于微服務(wù), 實(shí)現(xiàn)Restful 接口

      底層基于 Linux  的多路復(fù)用

      可以通過(guò)協(xié)程或者回調(diào)實(shí)現(xiàn)異步編程

      可以作為一個(gè)輕量級(jí)的 web 框架

      不過(guò)生態(tài)不完善, 相應(yīng)的異步框架比如 ORM 不完善

      簡(jiǎn)單示例

      Gevent 

      基于greenliet 實(shí)現(xiàn)并發(fā)

      猴子補(bǔ)丁修改內(nèi)置 socket 改為非阻塞

      配合 gunicorn 作為 wsgi server 

      簡(jiǎn)單示例

       

       Asyncio

      在 python3 引入, 協(xié)程 + 時(shí)間循環(huán)實(shí)現(xiàn)

      生態(tài)不夠完善, 沒(méi)有大規(guī)模的生產(chǎn)環(huán)境檢驗(yàn)

      目前影響不夠廣泛, 基于 Aiohttp 可以實(shí)現(xiàn)一些小的服務(wù)

      代碼示例

      Mysql 基礎(chǔ)常考題

      基礎(chǔ)考題

      事務(wù)的原理, 特性, 并發(fā)控制

      常用字段, 含義的區(qū)別

      常用的數(shù)據(jù)庫(kù)引擎的區(qū)別

      事務(wù)

      數(shù)據(jù)庫(kù)并發(fā)控制的基本單位, 是一些列 sql 語(yǔ)句 的集和

      事務(wù)必須全部成功, 要不全部執(zhí)行失敗( 回滾 )

      最常見(jiàn)的操作是轉(zhuǎn)賬操作

      簡(jiǎn)單示例

      事務(wù) ACID 特性

      原子性 (Atomicity) : 一個(gè)事務(wù)中所有操作全部完成或失敗

      一致性 (Consistency) : 事務(wù)開(kāi)始和結(jié)束之后數(shù)據(jù)完整性沒(méi)有被破壞

      隔離性 (Isolation) : 允許多個(gè)事務(wù)同時(shí)對(duì)數(shù)據(jù)庫(kù)修改和讀寫

      持久性 (Durabitlity) : 事務(wù)結(jié)束后, 修改是永久的不會(huì)丟失

      事務(wù)的并發(fā)控制

      事并發(fā)可能出現(xiàn)的問(wèn)題

      幻讀: 一個(gè)事務(wù)第二次查看出現(xiàn)第一次沒(méi)有的結(jié)果

      非重復(fù)讀: 一個(gè)事務(wù)重復(fù)讀取兩次得到不同的結(jié)果

      臟讀:一個(gè)事務(wù)讀取到另一個(gè)事務(wù)沒(méi)有提交的修改

      丟失修改: 并發(fā)寫入造成其中的一些修改丟失

      為了解決以上的問(wèn)題定義了四種事務(wù)隔離級(jí)別

      讀未提交:  別的事務(wù)可以讀取到未提交的改變

      讀已提交: 只能讀取已經(jīng)提交的數(shù)據(jù)

      可重復(fù)讀: 同一個(gè)事務(wù)先后查詢結(jié)果一樣 (Mysql InnoDB 默認(rèn)實(shí)現(xiàn)此級(jí)別)

      串行化: 事務(wù)完全串行化執(zhí)行, 隔離級(jí)別最高, 執(zhí)行效率最低

      如何解決高并發(fā)下的數(shù)據(jù)插入重復(fù)

      使用數(shù)據(jù)庫(kù)的唯一索引, 但是如果分庫(kù)分表此方法則無(wú)效

      使用隊(duì)列異步寫入, 或者使用redis 等實(shí)現(xiàn)分布式鎖

      樂(lè)觀鎖 / 悲觀鎖

      悲觀鎖就是先獲取鎖在進(jìn)行操作, 先鎖再查再更新  (假設(shè)肯定有人在操作) 可通過(guò)  select for update   實(shí)現(xiàn)

      樂(lè)觀鎖就是先修改, 更新的時(shí)候發(fā)現(xiàn)數(shù)據(jù)變了就回滾 (假設(shè)沒(méi)人操作) 可以通過(guò)版本號(hào)或者時(shí)間戳  check and set   實(shí)現(xiàn)

      按照 響應(yīng)速度, 沖突頻率, 重試代價(jià) 來(lái)判斷使用哪一種

        比如悲觀鎖強(qiáng)制上鎖的行為是肯定會(huì)效率低一些

        但是樂(lè)觀鎖沖突回滾的代價(jià)如果過(guò)大或者回滾頻率很高也很難受

      Mysql 常用的數(shù)據(jù)類型 - 字符串 (文本)

       char 定長(zhǎng),varchar 不定長(zhǎng) , text 過(guò)長(zhǎng)文本

      Mysql 常用的數(shù)據(jù)類型 - 數(shù)值

      整數(shù)用 int, 大整數(shù)用 bigint,  定義時(shí)的長(zhǎng)度并非指字節(jié)的長(zhǎng)度, 而是指在數(shù)據(jù)庫(kù)顯示的時(shí)候的字符寬度

      Mysql 常用的數(shù)據(jù)類型 - 日期時(shí)間

       注意時(shí)間戳只能存儲(chǔ)到 2038年 如果有更遠(yuǎn)的時(shí)間要求則需要用其他來(lái)代替

      InnoDB 和 MyISAM 引擎的區(qū)別

      MyISAM 不支持事務(wù), InnoDB 支持事務(wù)

      MyISAM 不支持外鍵, InnoDB 支持外鍵

      MyISAM 只支持表鎖, InnoDB 支持行鎖 + 表鎖

      MyISAM 支持全文索引, InnoDB 不支持

      Mysql  索引原理, 類型, 結(jié)構(gòu)

      為什么需要索引? 

      索引是數(shù)據(jù)表中一個(gè)或者多個(gè)列進(jìn)行排序的數(shù)據(jù)結(jié)構(gòu)

      索引可以大幅度提高檢索速度, 創(chuàng)建和更新索引本身也需要成本

      常見(jiàn)的查找結(jié)構(gòu)

      線性查找:   一個(gè)一個(gè)找, 實(shí)現(xiàn)簡(jiǎn)單, 太慢

      二分查找:   要求有序, 實(shí)現(xiàn)簡(jiǎn)單, 插入很慢 (需要保持有序)

      HASH:   查詢快, 但是占用空間大, 不適合大量數(shù)據(jù)存儲(chǔ)

      二叉查找樹(shù):   插入和查詢很快 (log(n)), 無(wú)法存大規(guī)模數(shù)據(jù), 存在復(fù)雜度退化的情況 (單邊增長(zhǎng), 退化成線性結(jié)構(gòu))

      平衡樹(shù):   解決二叉查找樹(shù)復(fù)雜度退化的問(wèn)題 (左右兩邊均衡, 不會(huì)退化), 無(wú)法解決大量數(shù)據(jù)時(shí)的節(jié)點(diǎn)太多, 樹(shù)高度深的問(wèn)題

      多路查找樹(shù):   一個(gè)父親可以多個(gè)孩子節(jié)點(diǎn) (度), 從而降低樹(shù)高

      多路平衡查找樹(shù):   多路查找樹(shù)的豐富版 - B-tree 

      什么是 B-Tree ?

      多路平衡查找樹(shù) (每個(gè)節(jié)點(diǎn)最多 m(m>=2)) 個(gè)孩子, 稱為m 階或者度)

      葉節(jié)點(diǎn)具有相同的深度

      節(jié)點(diǎn)中的數(shù)據(jù) key 從左到右是遞增的

      但是 B-Tree 實(shí)現(xiàn)范圍查找比較麻煩

      什么是 B+Tree ?

      B+ 樹(shù)是 B-Tree 的變形

      Mysql 使用 B+Tree 作為索引的數(shù)據(jù)結(jié)構(gòu)

      只在葉子節(jié)點(diǎn)帶有指向記錄的指針

      這樣不需要存儲(chǔ)真實(shí)數(shù)據(jù)把更多的空間來(lái)讓樹(shù)更多的度

      葉子節(jié)點(diǎn)通過(guò)指針相連, 可以實(shí)現(xiàn)范圍查詢

      Mysql 索引類型

      普通索引   CREATE INDEX 

      唯一索引  索引列的值必須唯一  CREATE UNIQUE INDEX 

      多列索引  多個(gè)列一共組成索引

      主鍵索引  一個(gè)表只能有一個(gè)  PRIMARY KEY 

      全文索引  InnoDB 不支持   FULLTEXT INDEX  

      什么時(shí)候創(chuàng)建索引

      根據(jù)查詢的需求來(lái)創(chuàng)建索引

        經(jīng)常用作查詢條件的字段 (where)

        經(jīng)常用作表連接的字段 (join)

        經(jīng)常用在排序(order by) ,  分組(group by) 的字段

      創(chuàng)建索引的注意點(diǎn)

      非空字段 NOT NULL , mysql 很難對(duì)空值做查詢優(yōu)化  - 對(duì)索引字段要求有默認(rèn)值

      區(qū)分度較高, 離散度較大, 盡可能不要有大量相同值

      索引的長(zhǎng)度不要太長(zhǎng) (比較費(fèi)時(shí)間)

      索引什么時(shí)候會(huì)失效

      B+樹(shù)的 key 沒(méi)辦法直接比較的時(shí)候就會(huì)失效

      常見(jiàn)的有: 模糊匹配, 類型隱轉(zhuǎn), 最左匹配

      什么是聚集索引, 和非聚集索引

      非聚集索引

      聚集索引

      如何排查慢查詢

      慢查詢通常是缺少索引或者索引不合理或者業(yè)務(wù)代碼實(shí)現(xiàn)導(dǎo)致

        slow_query_log_file 開(kāi)啟并且查詢慢查詢?nèi)罩?/p>

        通過(guò) explain 排查索引問(wèn)題

        調(diào)整數(shù)據(jù)修改索引; 業(yè)務(wù)層限制不合理訪問(wèn)

      為什么 Mysql 數(shù)據(jù)庫(kù)的主鍵使用自增的正數(shù)比較好

      自增表示是有序的增長(zhǎng), 便于 B+tree 的key 的排序

      使用數(shù)字存儲(chǔ)的更小, 更容易操作

      SQL 語(yǔ)句常考

      內(nèi)連接

       INNER JOIN   兩個(gè)表都存在匹配時(shí), 返回匹配行

      外連接

       LEFT/RIGHT JOIN   返回一個(gè)表的行, 及時(shí)另一個(gè)沒(méi)有匹配

      全連接

        FULL JOIN    只要某一個(gè)表存在匹配就返回左右兩個(gè)表的全部

      Redis 緩存相關(guān)問(wèn)題

      什么是緩存, 什么要使用緩存

      Redis 和 Memcached 的主要區(qū)別

      常用的數(shù)據(jù)類型和使用場(chǎng)景

       

      Redis 支持哪些持久化方式

      Redis 的事務(wù)

      Redis 如何實(shí)現(xiàn)分布式鎖

       大體思路, 多臺(tái)機(jī)器上的多線程或者多進(jìn)程同時(shí)訪問(wèn)一個(gè) Redis 服務(wù)器, 有人用就設(shè)置一個(gè)鍵值對(duì). 用完了就刪除這個(gè)鍵值

      這樣每個(gè)程序來(lái)的時(shí)候如果發(fā)現(xiàn)是有這個(gè)鍵值對(duì), 那就說(shuō)明有人再用這個(gè)鎖. 可以進(jìn)行重試或者等待這個(gè)鍵值對(duì)被刪除為止.

      然后自己用的時(shí)候在設(shè)置這個(gè)鍵值對(duì), 讓別人知道你在用

      使用緩存的模式

      緩存穿透問(wèn)題

      緩存擊穿問(wèn)題

       

      緩存雪崩問(wèn)題

      分布式系統(tǒng)下如何生成數(shù)據(jù)庫(kù)的自增 ID ?

      利用分布式鎖的原理, 設(shè)置一個(gè)數(shù)字進(jìn)行增長(zhǎng)更新

      Redis 單機(jī)進(jìn)行分布式鎖如果宕機(jī)怎么辦?

      可以使用類似 Redlock 之類的算法進(jìn)行重啟, 或者多臺(tái) Redis 服務(wù)器進(jìn)行串聯(lián)

      宕機(jī)時(shí)進(jìn)行主從選取重新上線之類的方案來(lái)緩解. 但是依舊存在一些問(wèn)題

      比如主從設(shè)備的數(shù)據(jù)異步同步可能存在延時(shí)導(dǎo)致臟數(shù)據(jù)之類的

      沒(méi)實(shí)際使用過(guò)就隨便扯兩句意思到了就行

      Python WSGI 與 web 框架相關(guān)

      什么是WSGI 

      背景

      q:

        經(jīng)常使用 uwsgi / gunicorn 部署 django / flask 應(yīng)用

        為什么 flask / django 都可以在 gunicorn 上運(yùn)行? 

      a:

        在此之前存在 python  web server  不規(guī)范的亂象

        需要一個(gè)標(biāo)準(zhǔn)協(xié)議定義讓網(wǎng)絡(luò)框架以相同的規(guī)范對(duì)接

        Python  Wwb Server Gateway Interface  (pep3333)

        描述了 Web Server (uwsgi / gunicorn) 如何于 web 框架 (flask / django) 交互嗎 Web 框架如何處理請(qǐng)求

        由此可以讓任意的 web 框架部署在任意的 web server 上

      實(shí)現(xiàn)

      實(shí)現(xiàn)的框架函數(shù)如圖

      具體實(shí)現(xiàn)

      常用的 Python  Web 框架的對(duì)比

      Django / Flask / Tornado

      Django   大而全, 自帶orm, Admin 組件, 第三方插件較多

      Flask  微框架, 插件機(jī)制, 非常靈活. 但是沒(méi)有統(tǒng)一規(guī)范會(huì)導(dǎo)致后續(xù)的維護(hù)存在阻礙 (可以使用  cookiecutter-flask  生成統(tǒng)一的項(xiàng)目模板解決)

      Tornado  異步支持的微框架和異步網(wǎng)絡(luò)庫(kù), 輪子比較少. 相關(guān)支持不多

      什么 MVC

      MVC : 模型(Model), 視圖(View ), 控制器 (Controller)

        Model: 負(fù)責(zé)業(yè)務(wù)對(duì)象和數(shù)據(jù)庫(kù)的交互 (ORM)

        View: 負(fù)責(zé)與用戶的交互展示

        Controller: 接受參數(shù)調(diào)用模型和視圖完成請(qǐng)求

      什么是 ORM 

      Object Relational Mapping  對(duì)象關(guān)系映射

      用于實(shí)現(xiàn)業(yè)務(wù)對(duì)象與數(shù)據(jù)表中字段映射

      常見(jiàn)如 Sqlalchemy , Django ORM , Peewee

      優(yōu)勢(shì): 代碼更加面向?qū)ο? 代碼更少, 靈活性更高, 提升開(kāi)發(fā)效率

      Web  安全問(wèn)題

      SQL 注入

      原理

      通過(guò)特殊構(gòu)造的輸入?yún)?shù)傳入 Web 應(yīng)用, 導(dǎo)致后端執(zhí)行了惡意SQL

      通常由于程序員未對(duì)輸入進(jìn)行過(guò)濾, 直接動(dòng)態(tài)拼接SQL 產(chǎn)生

      可以使用 開(kāi)源工具 sqlmap, SQLninja 檢測(cè)

      示例

      正確的密碼驗(yàn)證

      錯(cuò)誤的密碼驗(yàn)證

       注入式攻擊輸入

      通過(guò)輸入   '--   注釋 將后面的 sql 注釋掉從而無(wú)法執(zhí)行讓 where 的條件失效, 從而繞過(guò)密碼的檢查

      修復(fù)后示例

      不要自己手動(dòng)拼接。 拼接時(shí)使用占位符使用 sql 模塊的內(nèi)置方法自動(dòng)拼接即可避免

       解決方式

      永遠(yuǎn)不要相信用戶的任何輸入

      對(duì)輸入的參數(shù)做好檢查 (類型和范圍), 過(guò)濾和轉(zhuǎn)義特殊字符

      不要手動(dòng)拼接 sql , 使用 ORM 即可降低風(fēng)險(xiǎn)

      數(shù)據(jù)庫(kù)底層: 做好權(quán)限管理配置, 不要明文存儲(chǔ)敏感信息

      XSS 跨站腳本攻擊

      惡意用戶將代碼植入到提供給其他用戶使用的頁(yè)面中, 未經(jīng)轉(zhuǎn)義的惡意代碼輸出到其他用戶的瀏覽器中被執(zhí)行

      用戶瀏覽頁(yè)面的時(shí)候嵌入頁(yè)面中的腳本 (js) 會(huì)被執(zhí)行, 攻擊用戶

      主要分為兩類: 反射型(非持久性), 存儲(chǔ)型 (持久性)

      示例

      評(píng)論中評(píng)論信息中輸入的是 js 代碼. 從而每次對(duì)這段評(píng)論渲染的時(shí)候都會(huì)執(zhí)行這段腳本

      非存儲(chǔ)型的比如在 url 里面插入一段代碼

      解絕方式

      CSRF  跨站請(qǐng)求偽造

      原理

      本質(zhì)依舊是獲取用戶的 cookie 從而進(jìn)行操作

      cookie 都是在瀏覽器保存, 誘導(dǎo)用戶在釣魚網(wǎng)站進(jìn)行惡性操作

      示例

      解決方式

      get 請(qǐng)求的偽造成本是比 post 低很多的, 使用 post 請(qǐng)求方式可以一定程度避免

      但是  post 請(qǐng)求也可以被偽造

      用戶操作限制——驗(yàn)證碼機(jī)制

      方法:添加驗(yàn)證碼來(lái)識(shí)別是不是用戶主動(dòng)去發(fā)起這個(gè)請(qǐng)求,由于一定強(qiáng)度的驗(yàn)證碼機(jī)器無(wú)法識(shí)別,因此危險(xiǎn)網(wǎng)站不能偽造一個(gè)完整的請(qǐng)求
      優(yōu)點(diǎn):簡(jiǎn)單粗暴,低成本,可靠,最安全的方式
      缺點(diǎn):對(duì)用戶極不友好

      請(qǐng)求來(lái)源限制——驗(yàn)證 HTTP Referer 字段

      方法:在HTTP請(qǐng)求頭 Referer 字段用以記錄請(qǐng)求源地址, 服務(wù)器可驗(yàn)證源地址是否合法從而判定是否拒絕響應(yīng)
      優(yōu)點(diǎn):零成本,簡(jiǎn)單易實(shí)現(xiàn)
      缺點(diǎn):由于這個(gè)方法嚴(yán)重依賴瀏覽器自身,因此安全性全看瀏覽器

      1. 兼容性不好:各瀏覽器對(duì)于Referer有差異化實(shí)現(xiàn)
      2. 并不一定可靠:在一些古老的垃圾瀏覽器中,Referer可以被篡改
      3. 對(duì)用戶不友好:
        1. 記錄用戶的訪問(wèn)來(lái)源,部分用戶認(rèn)為這樣會(huì)侵犯到他們自己的隱私權(quán)
        2. 瀏覽器具有防止跟蹤功能,開(kāi)啟后會(huì)取消此字段, 從而導(dǎo)致正常用戶請(qǐng)求被拒絕

      額外驗(yàn)證機(jī)制——token的使用

      方法:使用token來(lái)代替驗(yàn)證碼驗(yàn)證。由于黑客并不能拿到和看到cookie里的內(nèi)容,所以無(wú)法偽造一個(gè)完整的請(qǐng)求。基本思路如下:

      1. 服務(wù)器隨機(jī)產(chǎn)生token,存在session中,放在cookie中或者以ajax的形式交給前端
      2. 前端發(fā)請(qǐng)求的時(shí)候,解析cookie中的token,放到請(qǐng)求url里或者請(qǐng)求頭中
      3. 服務(wù)器驗(yàn)證token,由于黑客無(wú)法得到或者偽造token,所以能防范csrf

      更進(jìn)一步的加強(qiáng)手段(不需要session):

      1. 服務(wù)器隨機(jī)產(chǎn)生token,然后以token為密鑰散列生成一段密文
      2. token和密文都隨cookie交給前端
      3. 前端發(fā)起請(qǐng)求時(shí)把密文和token都交給后端
      4. 后端對(duì)token和密文進(jìn)行正向散列驗(yàn)證,看token能不能生成同樣的密文
      5. 這樣即使黑客拿到了token 也無(wú)法拿到密文

      優(yōu)點(diǎn):

      1. 安全性:極大地提高了破解成本
      2. 易用性:非常容易實(shí)現(xiàn)
      3. 友好性:對(duì)用戶來(lái)說(shuō)十分友好

      缺點(diǎn):

      1. 性能擔(dān)憂:需要hash計(jì)算,增加性能上的成本
      2. cookie臃腫:更加依賴網(wǎng)絡(luò)的情況
      3. 對(duì)于POST請(qǐng)求,難以將token附在請(qǐng)求中。(可以通過(guò)框架和庫(kù)解決)

      曲線救國(guó)——在HTTP頭中自定義屬性并驗(yàn)證

      方法:將 token 放在 HTTP頭的自定義屬性中

      優(yōu)點(diǎn):

      1. 這樣解決了上種方法在請(qǐng)求中加入 token 的不便
      2. 通過(guò) XMLHttpRequest 請(qǐng)求的地址不會(huì)被記錄到瀏覽器的地址欄,安全性較高

      缺點(diǎn):

      1. 局限性大:XMLHttpRequest請(qǐng)求通常用于Ajax,并非所有的請(qǐng)求都適合用這個(gè)類來(lái)發(fā)起,請(qǐng)求后的頁(yè)面無(wú)法被瀏覽器記錄,造成不便。
      2. 舊網(wǎng)站改造需全部改為XMLHttpRequest請(qǐng)求, 代價(jià)過(guò)大

      前后端分離與RESTful 常見(jiàn)面試題

      什么是前后端分離, 好處是什么

       什么是 RESTful

       

      RESTful 的準(zhǔn)則

      什么是 RESTful  API

      示例

      系統(tǒng)設(shè)計(jì)相關(guān)

      什么是系統(tǒng)設(shè)計(jì)

      設(shè)計(jì)難點(diǎn)

       

       設(shè)計(jì)要素

      延伸考點(diǎn)

       

       施工完畢---------------------------------------

       

      posted @ 2021-03-11 20:33  羊駝之歌  閱讀(299)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 日本深夜福利在线观看| 国产成人精品久久一区二区| av中文无码乱人伦在线观看| 精品国产人妻一区二区三区久久| 日韩美女一区二区三区视频| 亚洲精品欧美综合二区| 亚洲成人av免费一区| 精品久久久久久中文字幕202| 久久综合开心激情五月天| 欧美 日韩 国产 成人 在线观看| 午夜成人无码免费看网站| 国产精品一国产精品亚洲| 天堂va亚洲va欧美va国产| 亚洲中文字幕在线二页| 国产自产一区二区三区视频| 无套内射视频囯产| 少妇爆乳无码专区| 久久亚洲日本激情战少妇| 久久精品道一区二区三区| 六月丁香婷婷色狠狠久久| 渑池县| 久久综合狠狠综合久久激情| 91精品国产午夜福利| 资兴市| 久久夜色国产噜噜亚洲av| 成人午夜大片免费看爽爽爽| 亚洲精品国精品久久99热| 黄色免费在线网址| 九九热精彩视频在线免费| 无码AV无码天堂资源网影音先锋 | 精品自拍自产一区二区三区| 丰满少妇呻吟高潮经历| 国产日韩一区二区四季| 日韩亚洲国产中文永久| 一区二区三区精品视频免费播放| 特级aaaaaaaaa毛片免费视频| 欧洲无码一区二区三区在线观看| 色爱av综合网国产精品| 亚洲av首页在线| 日韩少妇人妻vs中文字幕| 国产69精品久久久久久|