Python后端技术栈
Python后端技術棧
Happiness is a way of travel. Not a destination.
幸福是一種旅行方式。 不是目的地。
1.導航
1.1Python 語言基礎
1.語言特點
2.語法基礎
3.高級特性
1.2算法與數據結構
1.常用算法和數據結構
2.分析時間和空間復雜度
3.實現常見數據結構和算法
1.3編程范式
1.面向對象編程
2.常用設計模式
3.函數式編程
1.4操作系統
1.常用 Linux
2.進程和線程
3.內存管理
1.5網絡編程
1.常用協議 TCP、IP、HTTP
2.Socket 編程基礎
3.Python 并發庫
1.6數據庫
1.MySQL 數據庫、索引優化
2.關系型和 NoSQL 的使用場景
3.Redis 緩存(常用的數據類型以及使用場景,底層實現了解會更好)
1.7Python Web框架
1.常用框架的對比,使用 Restful
2.WSGI 原理
3.Web 安全的問題
1.8系統設計
1.設計原則,如何分析
2.后端系統常用的組件(緩存、數據庫、消息隊列等等)
3.技術選型和實現(短網址服務、Feed 流系統)
1.9技術之外的軟實力
1.學習能力
2.業務理解能力,溝通交流能力
3.心態
2.0小擴展-STAR模型
在生活中描述一件事情或者是在面試中描述項目經驗,如何讓我們的語言更加有條理,邏輯性?可以采用如下的模型進行梳理。
情境(situation)
什么情況下發生的
任務(task)
你是如何明確你的任務的
行動(action)
采取了什么樣的行動
結果(result)
結果怎么樣?學到了什么?
2.技術棧詳解
2.1 Python 語言基礎
2.1.1 Python 語言特性
Python 是動態強類型語言,很多人都誤認為是弱類型語言,其實是錯誤的。
動態是指在運行期確定類型(靜態則是在編譯期確定類型)。強類型指的是在沒有強制類型轉化前,不允許兩種不同類型的變量相互操作(也就是不會發生隱式類型轉換)。
2.1.2 Python 作為后端語言的優缺點
為什么使用 Python ?
答:它是一門膠水語言,輪子多,應用廣泛;語言靈活,生產力高,是一些創業公司以及外包項目節省時間的首選語言。但是在使用的時候,需要考慮到性能的問題,代碼維護問題,以及2和3版本的兼容問題。
2.1.3 Python 其他的重要知識點
鴨子類型簡單介紹
曾有一個生動的例子來描述鴨子類型:當看到一只鳥,如果它走起來像鴨子、叫起來像鴨子,游泳的時候也像鴨子,那么我們就稱這只鳥是鴨子。它的關注點在對象的行為,而不是類型。比如一些常用對象 file、StringIO、socket 都支持 read/write 方法,我們可以看做類似的對象 file like object;再舉個例子,在 Python 中實現了 iter 魔法方法的對象都可以用 for 迭代。
什么是 monkey patch ?
答:所謂的 monkey patch 就是運行時的屬性替換。比如我們常用的一個并發庫 gevent ,需要將內置的 socket 修改為非阻塞。我們在使用的時候,用到如下的代碼:
from gevent import monkey
打補丁,讓gevent框架識別耗時操作,比如:time.sleep,網絡請求延時
monkey.patch_all()
什么是自省?
答:自省就是在運行時判斷一個對象的類型的能力。我們可以通過 type、id和 isinstance 等方法獲取對象類型的信息。Inspect 模塊提供了更多獲取對象信息的函數。
什么是列表和字典推導式?
列表生成式:
my_list = [i for i in range(10) if i % 2 == 0]
字典推導式:
dict1 = {k: v for k, v in zip(a, b)}
特殊的情況:將列表推導式的方括號修改為小括號的時候,會返回一個生成器。
2.1.4 Python 之禪
The Zen of Python 便是著名的Python 之禪,它是由 Tim Peters 編寫的關于 Python 編程的準則,我們可以使用下面的代碼進行查看:
import this
我們在編程的過程中,如果拿不準的時候可以進行一個參考。
2.2 Python2 和 Python3 的差異
2.2.1 Python2/3 差異
Python3 中做了一些改進,我們需要了解。比如 print 成為了函數;還有編碼的問題,Python3 中不再有 Unicode 對象,默認 str 就是 Unicode;除法也有所變化,比如 Python3 除法返回的是浮點數。
Python2 里面是沒有類型申明的,Python3 中我們可以添加一個類型注解(type hint),幫助 IDE 實現類型提示以及類型檢查(mypy)。Python3 中優化的 super() 方便直接調用父類函數。Python3 中還有一些高級的解包操作,如下面示例:
a, b, *rest = range(10)
上面會將0賦值給 a,將1賦值給 b,然后將剩下的賦值給 rest。
類型檢查示例:
In [2]: def fuction(name: str): return ‘hello’ + name
In [3]: fuction(‘Ethan’)
Out[3]: ‘helloEthan’
Python3 限定關鍵字參數。也就是函數在傳參的時候,我們可以通過關鍵字參數方式,指定參數名傳參,避免參數太多時候搞混。
Python3 中重新拋出異常不會丟失棧信息,方便我們去排錯(在 Python2 中如果在一個異常中 raise 一個異常,原來的異常就會丟失,Python3 中支持 raise from,保留異常棧信息)。
Python3 中一切都是返回迭代器,比如 range/zip/map/dict.value/etc. are all iterators。
2.2.2 Python3 新增
1.yield from 鏈接子生成器
2.asyncio 內置庫, async、await 原生協程支持異步編程
3.新的內置庫 enum(枚舉),mock(單測時用),asyncio, ipaddress (處理ip地址)等等。
2.2.3 Python3 改進
1.生成的 pyc 文件統一放到 __pycache__文件夾下。
2.一些內置庫的修改。如 urllib,selector(支持select、epoll等Linux底層的一些封裝,方便我們統一做一些接口,實現異步IO) 等。
3.一些性能的優化,比如 dict。
2.2.4一些兼容2、3的工具
six 模塊。
2to3 等工具轉換代碼。(腳本工具,將 Python2 轉換為 Python3 代碼)
__future__模塊。在 Python2 中使用 Python3 的函數功能可參照如下代碼:
from future import print_fuction
2.3 Python 函數
2.3.1 Python 如何傳遞參數?
答:Python 其實不是引用傳遞也不是值傳遞,而是共享傳參(函數形參獲得實參中各個引用的副本)。簡單的理解一下:
我們在每一次傳遞參數的時候,形參和實參都指向同一個對象,這樣就叫做對象傳遞,既不是拷貝了一個值,也不是直接去操作這塊內存,但是它的結果有兩個。對于可變對象來說,我們直接去修改它,對于不可變對象來說,表現就好像 copy 了一個值,然后去修改新的值。
不可變對象好像是傳值,可變對象好像是傳引用。但是實際是不同的。
2.3.2 Python 可變/不可變對象
1.可變對象:bool、int、float、tuple、str
2.不可變對象:list、set、dict
可變對象作為默認參數的時候,注意默認參數只計算一次。
2.3.3 Python 中 *args 和 **kwargs
函數傳遞中,他們處理可變參數。如果使用 *args那么會將所有的參數打包成一個 tuple 對象。 **kwargs 則是將所有的關鍵字參數打包成一個 dict 對象。
2.4 Python 異常機制
2.4.1什么是 Python 的異常?
答:異常就是一種錯誤處理機制。所有的異常都繼承自 BaseException 。舉幾個和系統相關的異常:SystemExit、KeyboardInterrupt、GeneratorExit(生成器退出的異常)。還有一個異常的基類就是 Exception。
2.4.2使用異常的常見場景
答:網絡請求(超時、連接錯誤);資源訪問(權限問題、資源不存在);代碼邏輯(越界訪問、KeyError等)。
2.4.3處理異常
try:
# 可能會拋出異常的代碼
except (Exception1, Exception2) as e:
# 異常處理代碼
else:
# 異常沒有發生時代碼邏輯
finally:
# 無論異常有沒有發生都會執行的代碼,一般處理資源的關閉和釋放。
2.4.4如何自定義異常
1.繼承自 Exception 實現自定義異常(想想為什么不是 BaseException)
可以通過查看異常的等級信息,發現如果繼承自頂級父類,那么一些常用的異常也沒有了,自己需要定義的異常就太多太多,耗費時間。
2.可以給異常加上一些附加信息。
3.通常都是處理一些和業務相關的特定異常(raise MyException)
2.5 Python 性能分析與優化
Python 作為一門腳本語言來說,它的性能一直被詬病。并且由于存在一個臭名昭著的 GIL 導致沒有辦法充分利用多核,這都限制了 Python 的性能。
2.5.1什么是 CPython GIL?
GIL (Global Interpreter Lock)
1.CPython 解釋器的內存管理并不是線程安全的,存在多個線程時,有可能會出現同時修改同一對象,這樣容易出現問題。
2.為了保護多線程情況下對 Python 對象的訪問,CPython 使用了簡單的鎖機制避免多個線程同時執行字節碼。
缺陷便是沒有辦法同時利用 CPU 的多核,只有一個線程執行字節碼。對于 CPU 密集型的程序來說,影響會比較大。
2.5.2 GIL 的影響
限制了程序的多核執行。
1.同一個時間只能有一個線程執行字節碼
2.CPU 密集型程序難以利用多核優勢。
3.IO 期間會釋放 GIL ,對 IO 密集型程序影響不大。
2.5.3如何規避 GIL 的影響
1.CPU 密集型可以使用多進程 + 進程池的方式充分的利用多核。
2.IO 密集型可以使用多線程或者是協程。
3.使用 cython 擴展(將 Python 程序轉化成 C 代碼的一個擴展)。
2.5.4 GIL 的實現
CPython 中才會有 GIL ,其他的解釋器是沒有的。底層的代碼邏輯是設置一個ticker,每執行多少個字節碼的時候,去檢查當前是否有全局解釋器鎖,如果有那么執行函數釋放,讓其他的線程去執行;如果沒有,就重新獲取鎖。通俗一點就是每隔一段時間,就會嘗試去釋放當前線程的鎖,讓其他線程獲取鎖并去執行。
2.5.5為什么有了 GIL 之后,還要關注線程安全?
Python中什么操作才是原子的?一步到位執行完的。
1.一個操作如果是一個字節碼指令可以完成的就是原子的。
2.原子的是可以保證線程安全的,非原子操作不是線程安全的。
3.使用 dis 操作來分析字節碼。
import dis
def update_list(l):
l[0] = 1
dis.dis(update_list)
2.5.6如何剖析程序性能
使用各種 profile 工具(內置或第三方)
1.遵循二八定律,其實大部分的時間耗時在少量的代碼上。
2.通過內置的 profile 和 cprofile 等工具衡量程序的運行時間。
3.對于 web 應用來說,使用 pyflame(uber開源) 的火焰圖工具分析產品的性能。
2.5.7服務端性能優化措施
web應用一般語言不會成為瓶頸??梢圆捎萌缦碌囊恍﹥灮胧?#xff1a;
1.數據結構和算法優化。
2.數據庫層:索引優化、慢查詢消除、批量操作減少IO、NoSQL的使用。
3.網絡IO:批量操作, pipline 操作減少 IO。
4.緩存:使用內存數據庫 redis、memcached 等。以此抗一些并發比較高的請求。
5.使用異步的框架或者庫如 asyncio 和 celery。
6.對于并發相關的一些請求使用 gevent 協程或者多線程。
2.6 Python 生成器與協程
2.6.1什么是生成器
Generator
1.生成器就是可以生成值的函數。
2.當一個函數里有了 yield 關鍵字就成了生成器。
3.生成器可以掛起執行并且保持當前執行的狀態。
2.6.2基于生成器的協程
Python3 之前沒有原生協程,只有基于生成器的協程。
1.pep 342(Coroutines via Enhanced Generators)增強生成器功能。
2.生成器可以通過 yield 暫停執行和產出數據。
3.同時支持 send() 向生成器發送數據和 throw() 向生成器拋異常。
示例:
def coro():
# yield 關鍵字在 = 右邊作為表達式,可以被send值
hello = yield ‘hello’
yield hello
c = coro()
輸出 hello,這里調用next產出第一個值 hello,之后函數暫停
print(next?)
再次調用 send 發送值,此時hello變量賦值為 world,然后yield 產出hello變量的值 world
print(c.send(‘world’))
之后協程結束,后續再send值會拋出異常StopIteration
2.6.3協程的注意點
1.協程需要使用 send(None) 或者 next(coroutine) 來 『預激』(prime) 才能啟動。
2.在 yield 處協程會暫停執行。
3.單獨的 yield value 會產出值給對方調用
4.可以通過 coroutine.send(value) 來給協程發送值,發送的值會賦值給 yield 表達式左邊的變量 value=yield
5.協程執行完成之后(沒有遇到下一個 yield 語句)會拋出 StopIteration 異常。
2.6.4協程裝飾器
避免每次都要用 send 預激它。
from functools import wraps
def coroutine(func):
# 這樣就不需要每次都用send(None)啟動了
# 裝飾器:向前執行到第一個 yield 表達式,預激func
@wraps(func)
def primer(*args,**kwargs):
gen = func(*args,**kwargs)
next(gen)
return gen
return primer
2.6.5Python3 原生協程
Python3.5 引入 async/await 支持原生協程(native coroutine)
2.7單元測試
2.7.1什么是單元測試
Unit Testing
1.針對程序模塊進行正確性檢驗。
2.一個函數,一個類進行驗證。
3.自底向上保證程序正確性。
2.7.2為什么寫單元測試
三無代碼不可取(無文檔、無注釋、無單測)
1.保證代碼邏輯的正確性(甚至有些采用測試驅動開發(TDD))
2.單測影響設計,易測的代碼往往是高內聚低耦合的。
3.回歸測試,防止改一處整個服務不可用。
2.7.3單元測試相關的庫
1.nose/pytest 較為常用
2.mock 模塊用來模擬替換網絡請求等
3.coverage 統計測試覆蓋率
如何設計測試用例:(等價類劃分): 1.正常值功能測試。 2.邊界值(比如最大最小,最左最右值) 3.異常值(比如None,空值,非法值)
2.8重點知識
2.8.1 Python 深拷貝與淺拷貝
淺拷貝:對于不可變對象相當于引用賦值;淺拷貝對于可變對象拷貝時只拷貝第一層引用。
深拷貝:對于不可變對象同樣相當于引用賦值;對于可變對象會逐層進行拷貝。
Python 中淺拷貝的方式:copy 模塊的 copy 方法;對象本身的 copy 方法;工廠方法;切片(只對列表有效)。
工廠方法就是直接使用 list 等方法進行修改。 Python 中默認使用的就是淺拷貝方式。
Python技術交流群871458817 免費分享機器學習,爬蟲,數據分析等,
2.8.2小結
1.不可變對象在賦值時會開辟新空間
2.可變對象在賦值時,修改一個引用的值,另一個引用也會發生改變。
3.深淺拷貝對不可變對象拷貝時,不開辟新的空間,相當于賦值操作。
4.淺拷貝在拷貝時,只拷貝頂層中的引用,如果元素是可變對象,并且被修改,那么拷貝的對象也會發生變化。
5.深拷貝在拷貝時,會逐層進行拷貝,直到所有的引用都是不可變對象為止。
6.Python 中有多種方式實現淺拷貝,copy 模塊的 copy 函數,對象的 copy 函數,工廠方法,切片等。
7.大多數情況下,編寫程序時,都是使用淺拷貝,除非有特定的需求。
8.淺拷貝的優點:拷貝速度快,占用空間少,拷貝效率高。
如果還有問題未能得到解決,搜索887934385交流群,進入后下載資源工具安裝包等。最后,感謝觀看!
總結
以上是生活随笔為你收集整理的Python后端技术栈的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎样将pdf转换成jpg格式
- 下一篇: 腾讯云 coding使用