基礎篇
Jupyter Notebook
優點
- 整合所有的資源
- 交互性編程體驗
- 零成本重現結果
實踐站點
- Jupyter 官方
- Google Research 提供的 Colab 環境
- 安裝
- 運行
列表與元組
列表和元組,都是 一個可以放置任意數據類型的有序集合 。
l = [1, 2, 'hello', 'world'] # 列表中同時含有 int 和 string 類型的元素
l
[1, 2, 'hello', 'world']
tup = ('jason', 22) # 元組中同時含有 int 和 string 類型的元素
tup
('jason', 22)
- 列表是動態的,長度大小不固定,可以隨意地增加、刪減或者改變元素 (mutable)
- 元組是靜態的,場地大小固定,無法增加刪除或者改變 (immutable)
- 都支持負數索引;
- 都支持切片操作;
- 都可以隨意嵌套;
-
兩者可以通過
list()
和tuple()
函數相互轉換;
列表和元組存儲方式的差異
由于列表是動態的,所以它需要存儲指針,來指向對應的元素。增加/刪除的時間復雜度均為 O(1)。
l = []
l.__sizeof__() // 空列表的存儲空間為 40 字節
40
l.append(1)
l.__sizeof__()
72 // 加入了元素 1 之后,列表為其分配了可以存儲 4 個元素的空間 (72 - 40)/8 = 4
l.append(2)
l.__sizeof__()
72 // 由于之前分配了空間,所以加入元素 2,列表空間不變
l.append(3)
l.__sizeof__()
72 // 同上
l.append(4)
l.__sizeof__()
72 // 同上
l.append(5)
l.__sizeof__()
104 // 加入元素 5 之后,列表的空間不足,所以又額外分配了可以存儲 4 個元素的空間
使用場景
- 如果存儲的數據和數量不變,那么肯定選用元組更合適
- 如果存儲的數據和數量是可變的,那么則用列表更合適
區別
- 列表是動態的,長度可變,可以隨意的增加、刪除或者改變元素;列表的存儲空間略大于元組,性能略遜于元組;
- 元組是靜態的,長度大小固定,不可對元素進行增加、刪除、修改操作,元組相對于列表更加的輕量級、性能稍優;
思考題
# 創建空列表
# option A:list()是一個function call,Python的function call會創建stack,并且進行一系列參數檢查的操作,比較expensive
empty_list = list()
# option B:[]是一個內置的C函數,可以直接被調用,因此效率高
empty_list = []
字典與集合
字典是一系列無序元素的組合,其長度大小可變,元素可以任意的刪除和改變,相比于列表和元組,字典的性能更優,特別是對于查找、添加和刪除操作,字典都能在常數時間復雜度內完成。而集合和字典基本相同,唯一的區別,就是集合沒有件和值的配對,是一系列無序的、唯一的元素組合。
# 定義字典
d = {'name': 'jason', 'age': 20}
# 增加元素對'gender': 'male'
d['gender'] = 'male'
# 增加元素對'dob': '1999-02-01'
d['dob'] = '1999-02-01'
d
{'name': 'jason', 'age': 20, 'gender': 'male', 'dob': '1999-02-01'}
# 更新鍵'dob'對應的值
d['dob'] = '1998-01-01'
# 刪除鍵為'dob'的元素對
d.pop('dob')
'1998-01-01'
d
{'name': 'jason', 'age': 20, 'gender': 'male'}
# 定義集合
s = {1, 2, 3}
# 增加元素 4 到集合
s.add(4)
s
{1, 2, 3, 4}
# 從集合中刪除元素 4
s.remove(4)
s
{1, 2, 3}
d = {'b': 1, 'a': 2, 'c': 10}
# 根據字典鍵的升序排序
d_sorted_by_key = sorted(d.items(), key=lambda x: x[0])
# 根據字典值的升序排序
d_sorted_by_value = sorted(d.items(), key=lambda x: x[1])
可以使用 get(key,default) 函數來進行字典索引。如果鍵不存在,調用該函數可以返回一個默認的值。
集合不支持索引操作,因為集合本質上是一個哈希表,和列表不一樣。
字典和集合性能
字典和集合是進行性能高度優化的數據結構,特別是對于查找、添加和刪除操作。
字典和集合的工作原理
字典和集合的內部結構都是一張哈希表
- 對于字典而言,這張哈希表存儲了哈希值,鍵和值這三個元素
- 對于集合而言,區別就是哈希表內沒有鍵和值的配對,只有單一的元素了
插入操作
每次向字典或集合插入一個元素時,Python 會首先計算鍵的哈希值(hash(key)),再和 mask = PyDicMinSize - 1 做與操作,計算這個元素應該插入哈希表的位置 index = hash(key) & mask。如果哈希表中此位置是空的,那么這個元素就會被插入其中。而如果此位置已被占用,Python 便會比較兩個元素的哈希值和鍵是否相等。
- 若兩者都相等,則表明這個元素已經存在,如果值不同,則更新值。
- 若兩者中有一個不相等,這種情況我們通常稱為哈希沖突(hash collision),意思是兩個元素的鍵不相等,但是哈希值相等。這種情況下,Python 便會繼續尋找表中空余的位置,直到找到位置為止。
查找操作
先通過哈希值找到目標位置,然后比較哈希表這個位置中元素的哈希值和鍵,與需要查找的元素是否相等,如果相等,則直接返回,否則繼續查找,知道為空或拋出異常為止
刪除操作
暫時對這個位置得到元素賦予一個特殊的值,等到重新調整哈希表的大小時,再將其刪除。
字符串
- Python 中字符串使用單引號、雙引號或三引號表示,三者意義相同,并沒有什么區別。其中,三引號的字符串通常用在多行字符串的場景。
- Python 中字符串是不可變的(前面所講的新版本 Python 中拼接操作’+='是個例外)。因此,隨意改變字符串中字符的值,是不被允許的。
- Python 新版本(2.5+)中,字符串的拼接變得比以前高效了許多,你可以放心使用。
- Python 中字符串的格式化(string.format,f)常常用在輸出、日志的記錄等場景。
輸入與輸出
輸入輸出基礎
生產環境中使用強制轉換時,請記得加上 try except
文件輸入輸出
所有 I/O 都應該進行錯誤處理。因為 I/O 操作可能會有各種各樣的情況出現,而一個健壯(robust)的程序,需要能應對各種情況的發生,而不應該崩潰(故意設計的情況除外)。
JSON 序列化與實戰
- json.dumps() 函數,接受 python 的基本數據類型,然后將其序列化為 string;
- json.loads() 函數,接受一個合法字符串,然后將其序列化為 python 的基本數據類型;
條件與循環
- 在條件語句中,if 可以單獨使用,但是 elif 和 else 必須和 if 同時搭配使用;而 If 條件語句的判斷,除了 boolean 類型外,其他的最好顯示出來。
- 在 for 循環中,如果需要同時訪問索引和元素,你可以使用 enumerate() 函數來簡化代碼。
- 寫條件與循環時,合理利用+continue+或者+break+來避免復雜的嵌套,是十分重要的。
- 要注意條件與循環的復用,簡單功能往往可以用一行直接完成,極大地提高代碼質量與效率。
異常處理
- 異常,通常是指程序運行的過程中遇到了錯誤,終止并退出。我們通常使用 try except 語句去處理異常,這樣程序就不會被終止,仍能繼續執行。
- 處理異常時,如果有必須執行的語句,比如文件打開后必須關閉等等,則可以放在 finally block 中。
- 異常處理,通常用在你不確定某段代碼能否成功執行,也無法輕易判斷的情況下,比如數據庫的連接、讀取等等。正常的 flow-control 邏輯,不要使用異常處理,直接用條件語句解決就可以了。
自定義函數
- Python 中函數的參數可以接受任意的數據類型,使用起來需要注意,必要時請在函數開頭加入數據類型的檢查;
- 和其他語言不同,Python 中函數的參數可以設定默認值;
- 嵌套函數的使用,能保證數據的隱私性,提高程序運行效率;
- 合理地使用閉包,則可以簡化程序的復雜度,提高可讀性;
匿名函數
優點:
- 減少代碼的重復性;
- 模塊化代碼;
map(function,iterable)
表示對 iterable 中的每個元素,都運用 function 這個函數,最后返回一個新的可遍歷的集合。
def square(x):
return x**2
squared = map(square, [1, 2, 3, 4, 5]) # [2, 4, 6, 8, 10]
filter(function,iterable)
表示對 iterable 中的每個元素,都使用 function 判斷,并返回 True 或者 False,最后將返回 True 的元素組成一個新的可遍歷的集合。
l = [1, 2, 3, 4, 5]
new_list = filter(lambda x: x % 2 == 0, l) # [2, 4]
reduce(function,iterable)
規定它有兩個參數,表示對 iterable 中的每個元素以及上一次調用后的結果,運用 function 進行計算,所以最后返回的是一個單獨的數值。
l = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, l) # 1*2*3*4*5 = 120
面向對象
基本概念
- 類:一群有著相似性的事物的集合;
- 對象:集合中的一個事物;
- 屬性:對象的某個靜態特征;
- 函數:對象某個動態能力
三要素:
- 繼承
- 封裝
- 多態
模塊化編程
- 通過絕對路徑和相對路徑,我們可以 import 模塊;
- 在大型工程中模塊化非常重要,模塊的索引要通過絕對路徑來做,而絕對路徑從程序的根目錄開始;
-
記著巧用
if __name__ == "__main__"
來避開 import 時執行;
進階篇
Python 對象的比較、拷貝
-
比較操作符
==
表示比較對象間的值是否相等,而is
表示比較對象的標識是否相等,即它們是否指向同一個內存地址。 -
比較操作符
is
效率優于==
,因為is
操作符無法被重載,執行is
操作只是簡單的獲取對象的 ID,并進行比較;而==
操作符則會遞歸地遍歷對象的所有值,并逐一比較。 - 淺拷貝中的元素,是原對象中子對象的引用,因此,如果原對象中的元素是可變的,改變其也會影響拷貝后的對象,存在一定的副作用。
- 深度拷貝則會遞歸地拷貝原對象中的每一個子對象,因此拷貝后的對象和原對象互不相關。另外,深度拷貝中會維護一個字典,記錄已經拷貝的對象及其 ID,來提高效率并防止無限遞歸的發生。
值傳遞與引用傳遞
常見的參數傳遞有 2 種:
- 值傳遞:通常就是拷貝對象的值,然后傳遞給函數里的新變量,原變量和新變量之間相互獨立,互不影響
- 引用傳遞:通常是指把參數的引用傳給新的變量,這樣,原變量和新變量就會指向同一塊內存地址。
準確來說, python 的參數傳遞是 賦值傳遞 ,或者叫做對象的 引用傳遞 ,python 里所有的數據類型都是對象,所以參數傳遞時,只是讓新變量與原變量指向相同的對象而已,并不存在值傳遞或引用傳遞一說。
需要注意的是,這里的賦值或對象的引用傳遞,不是指一個具體的內存地址,二十指一個具體的對象。
- 如果對象是可變的,當其改變時,所有指向這個對象的變量都會改變;
- 如果對象不可變,簡單的賦值只能改變其中一個變量的值,其余變量則不受影響;
裝飾器
函數也是對象
def func(message):
print('Got a message: {}'.format(message))
send_message = func
send_message('hello world')
# 輸出
Got a message: hello world
函數可以作為函數參數
def get_message(message):
return 'Got a message: ' + message
def root_call(func, message):
print(func(message))
root_call(get_message, 'hello world')
# 輸出
Got a message: hello world
函數可以嵌套函數
def func(message):
def get_message(message):
print('Got a message: {}'.format(message))
return get_message(message)
func('hello world')
# 輸出
Got a message: hello world
函數的返回值也可以是函數對象(閉包)
def func_closure():
def get_message(message):
print('Got a message: {}'.format(message))
return get_message
send_message = func_closure()
send_message('hello world')
# 輸出
Got a message: hello world
簡單使用裝飾器
def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func()
return wrapper
def greet():
print('hello world')
greet = my_decorator(greet)
greet()
# 輸出
wrapper of decorator
hello world
更優雅的寫法
def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func()
return wrapper
@my_decorator
def greet():
print('hello world')
greet()
帶參數的裝飾器
def my_decorator(func):
def wrapper(message):
print('wrapper of decorator')
func(message)
return wrapper
@my_decorator
def greet(message):
print(message)
greet('hello world')
# 輸出
wrapper of decorator
hello world
帶自定義參數的裝飾器
def repeat(num):
def my_decorator(func):
def wrapper(*args, **kwargs):
for i in range(num):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
return my_decorator
@repeat(4)
def greet(message):
print(message)
greet('hello world')
# 輸出:
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world
上述 green() 函數被裝飾以后,它的元信息會發生改變,可勇敢
greet__name__
來查看。可通過內置裝飾器來解決這個問題
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
@my_decorator
def greet(message):
print(message)
greet.__name__
# 輸出
'greet'
類裝飾器
class Count:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print('num of calls is: {}'.format(self.num_calls))
return self.func(*args, **kwargs)
@Count
def example():
print("hello world")
example()
# 輸出
num of calls is: 1
hello world
example()
# 輸出
num of calls is: 2
hello world
裝飾器支持嵌套使用
@decorator1
@decorator2
@decorator3
def func():
...
# 等價于
decorator1(decorator2(decorator3(func)))
裝飾器使用場景:
- 身份認證
- 日志記錄
- 輸入合理性檢查
- 緩存(LRU cache)
metaclass
metaclass 是 Python 黑魔法級別的語言特性,它可以改變正常 Python 類型的創建過程。
- 所有 Python 的用戶定義類,都是 type 這個類的實例
- 用戶自定義類,只不過是 type 類的 __ call __ 運算符重載
- metaclass 是 type 的子類,通過替換 type 的 __ call __ 運算符重載機制,超越變形正常的類
class Mymeta(type):
def __init__(self, name, bases, dic):
super().__init__(name, bases, dic)
print('===>Mymeta.__init__')
print(self.__name__)
print(dic)
print(self.yaml_tag)
def __new__(cls, *args, **kwargs):
print('===>Mymeta.__new__')
print(cls.__name__)
return type.__new__(cls, *args, **kwargs)
def __call__(cls, *args, **kwargs):
print('===>Mymeta.__call__')
obj = cls.__new__(cls)
cls.__init__(cls, *args, **kwargs)
return obj
class Foo(metaclass=Mymeta):
yaml_tag = '!Foo'
def __init__(self, name):
print('Foo.__init__')
self.name = name
def __new__(cls, *args, **kwargs):
print('Foo.__new__')
return object.__new__(cls)
foo = Foo('foo')
迭代器和生成器
- 容器時可迭代對象,可迭代對象調用 iter() 函數,可以得到一個迭代器。迭代器可以通過 next() 函數來得到下一個元素,從而支持遍歷
- 生成器時一種特殊的迭代器,合理使用生成器,可以降低內存占用、優化程序結構、提高程序速度
- 生成器在 Python 2 的版本上,是協程的一種重要實現方式;而 Python 3.5 引入的 async、await 語法糖,生成器實現協程的方式就已經落后了。
協程
協程是實現并發編程的一種方式
- 協程和多線程的區別,主要在于兩點,一是協程為單線程;二是協程由用戶決定,在哪些地方交出控制權,切換到下一個任務
- 協程的寫法更加簡潔清晰;把 async/await 語法和 create_task 結合起來用,對于中小級別的并發需求已經毫無壓力
生產者/消費者 模型
import asyncio
import random
async def consumer(queue, id):
while True:
val = await queue.get()
print('{} get a val: {}'.format(id, val))
await asyncio.sleep(1)
async def producer(queue, id):
for i in range(5):
val = random.randint(1, 10)
await queue.put(val)
print('{} put a val: {}'.format(id, val))
await asyncio.sleep(1)
async def main():
queue = asyncio.Queue()
consumer_1 = asyncio.create_task(consumer(queue, 'consumer_1'))
consumer_2 = asyncio.create_task(consumer(queue, 'consumer_2'))
producer_1 = asyncio.create_task(producer(queue, 'producer_1'))
producer_2 = asyncio.create_task(producer(queue, 'producer_2'))
await asyncio.sleep(10)
consumer_1.cancel()
consumer_2.cancel()
await asyncio.gather(consumer_1, consumer_2, producer_1, producer_2, return_exceptions=True)
%time asyncio.run(main())
########## 輸出 ##########
producer_1 put a val: 5
producer_2 put a val: 3
consumer_1 get a val: 5
consumer_2 get a val: 3
producer_1 put a val: 1
producer_2 put a val: 3
consumer_2 get a val: 1
consumer_1 get a val: 3
producer_1 put a val: 6
producer_2 put a val: 10
consumer_1 get a val: 6
consumer_2 get a val: 10
producer_1 put a val: 4
producer_2 put a val: 5
consumer_2 get a val: 4
consumer_1 get a val: 5
producer_1 put a val: 2
producer_2 put a val: 8
consumer_1 get a val: 2
consumer_2 get a val: 8
Wall time: 10 s
并發編程之 Futures
區別并發和并行
- 并發通常應用與 I/O 操作頻繁的場景,比如你要從網站上下載多個文件, I/O 操作的時間可能比 CPU 運行處理的時間長得多,通過線程和任務之間互相切換的方式實現,但同一時刻,只允許有一個線程或任務執行
- 并行更多應用于 CPU heavy 的場景,比如 MapReduce 中的并行計算,為了加快運算速度,一般會用多臺機器,多個處理器來完成。可以讓多個進程完全同步同時的執行
Python 中之所以同一時刻只運行一個線程運行,其實是由于全局解釋鎖的存在。但對 I/O 操作而言,當其被 block 的時候,全局解釋器鎖便會被釋放,使氣體線程繼續執行。
import concurrent.futures
import requests
import threading
import time
def download_one(url):
resp = requests.get(url)
print('Read {} from {}'.format(len(resp.content), url))
# 版本 1
def download_all(sites):
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
executor.map(download_one, sites)
# 版本 2
def download_all(sites):
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
to_do = []
for site in sites:
future = executor.submit(download_one, site)
to_do.append(future)
for future in concurrent.futures.as_completed(to_do):
future.result()
def main():
sites = [
'https://en.wikipedia.org/wiki/Portal:Arts',
'https://en.wikipedia.org/wiki/Portal:History',
'https://en.wikipedia.org/wiki/Portal:Society',
'https://en.wikipedia.org/wiki/Portal:Biography',
'https://en.wikipedia.org/wiki/Portal:Mathematics',
'https://en.wikipedia.org/wiki/Portal:Technology',
'https://en.wikipedia.org/wiki/Portal:Geography',
'https://en.wikipedia.org/wiki/Portal:Science',
'https://en.wikipedia.org/wiki/Computer_science',
'https://en.wikipedia.org/wiki/Python_(programming_language)',
'https://en.wikipedia.org/wiki/Java_(programming_language)',
'https://en.wikipedia.org/wiki/PHP',
'https://en.wikipedia.org/wiki/Node.js',
'https://en.wikipedia.org/wiki/The_C_Programming_Language',
'https://en.wikipedia.org/wiki/Go_(programming_language)'
]
start_time = time.perf_counter()
download_all(sites)
end_time = time.perf_counter()
print('Download {} sites in {} seconds'.format(len(sites), end_time - start_time))
if __name__ == '__main__':
main()
## 輸出
Read 151021 from https://en.wikipedia.org/wiki/Portal:Mathematics
Read 129886 from https://en.wikipedia.org/wiki/Portal:Arts
Read 107637 from https://en.wikipedia.org/wiki/Portal:Biography
Read 224118 from https://en.wikipedia.org/wiki/Portal:Society
Read 184343 from https://en.wikipedia.org/wiki/Portal:History
Read 167923 from https://en.wikipedia.org/wiki/Portal:Geography
Read 157811 from https://en.wikipedia.org/wiki/Portal:Technology
Read 91533 from https://en.wikipedia.org/wiki/Portal:Science
Read 321352 from https://en.wikipedia.org/wiki/Computer_science
Read 391905 from https://en.wikipedia.org/wiki/Python_(programming_language)
Read 180298 from https://en.wikipedia.org/wiki/Node.js
Read 56765 from https://en.wikipedia.org/wiki/The_C_Programming_Language
Read 468461 from https://en.wikipedia.org/wiki/PHP
Read 321417 from https://en.wikipedia.org/wiki/Java_(programming_language)
Read 324039 from https://en.wikipedia.org/wiki/Go_(programming_language)
Download 15 sites in 0.19936635800002023 seconds
并發編程之 Asyncio
import asyncio
import aiohttp
import time
async def download_one(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
print('Read {} from {}'.format(resp.content_length, url))
async def download_all(sites):
tasks = [asyncio.create_task(download_one(site)) for site in sites]
await asyncio.gather(*tasks)
def main():
sites = [
'https://en.wikipedia.org/wiki/Portal:Arts',
'https://en.wikipedia.org/wiki/Portal:History',
'https://en.wikipedia.org/wiki/Portal:Society',
'https://en.wikipedia.org/wiki/Portal:Biography',
'https://en.wikipedia.org/wiki/Portal:Mathematics',
'https://en.wikipedia.org/wiki/Portal:Technology',
'https://en.wikipedia.org/wiki/Portal:Geography',
'https://en.wikipedia.org/wiki/Portal:Science',
'https://en.wikipedia.org/wiki/Computer_science',
'https://en.wikipedia.org/wiki/Python_(programming_language)',
'https://en.wikipedia.org/wiki/Java_(programming_language)',
'https://en.wikipedia.org/wiki/PHP',
'https://en.wikipedia.org/wiki/Node.js',
'https://en.wikipedia.org/wiki/The_C_Programming_Language',
'https://en.wikipedia.org/wiki/Go_(programming_language)'
]
start_time = time.perf_counter()
asyncio.run(download_all(sites))
end_time = time.perf_counter()
print('Download {} sites in {} seconds'.format(len(sites), end_time - start_time))
if __name__ == '__main__':
main()
## 輸出
Read 63153 from https://en.wikipedia.org/wiki/Java_(programming_language)
Read 31461 from https://en.wikipedia.org/wiki/Portal:Society
Read 23965 from https://en.wikipedia.org/wiki/Portal:Biography
Read 36312 from https://en.wikipedia.org/wiki/Portal:History
Read 25203 from https://en.wikipedia.org/wiki/Portal:Arts
Read 15160 from https://en.wikipedia.org/wiki/The_C_Programming_Language
Read 28749 from https://en.wikipedia.org/wiki/Portal:Mathematics
Read 29587 from https://en.wikipedia.org/wiki/Portal:Technology
Read 79318 from https://en.wikipedia.org/wiki/PHP
Read 30298 from https://en.wikipedia.org/wiki/Portal:Geography
Read 73914 from https://en.wikipedia.org/wiki/Python_(programming_language)
Read 62218 from https://en.wikipedia.org/wiki/Go_(programming_language)
Read 22318 from https://en.wikipedia.org/wiki/Portal:Science
Read 36800 from https://en.wikipedia.org/wiki/Node.js
Read 67028 from https://en.wikipedia.org/wiki/Computer_science
Download 15 sites in 0.062144195078872144 seconds
Asyncio 是單線程的,但其內部 event loop 的機制,可以讓它并發地運行多個不同的任務,并且比多線程享有更大的自主控制權。
Asyncio 中的任務, 在運行過程中不會被打斷,因此不會出現 race condition 的情況。尤其是在 I/O 操作 heavy 的場景下, Asyncio 比多線程的運行效率更高,因此 Asyncio 內部任務切換的損耗,遠比線程切換的損耗要小,并且 Asyncio 可以開啟的任務數量,也比多線程中的線程數列多得多。
但需要注意的是,很多情況下,使用 Asyncio 需要特定第三方庫的支持,而如果 I/O 操作很快,并不 heavy ,建議使用多線程來解決問題。
GIL(全局解釋器鎖)
CPython 引進 GIL 主要是由于:
- 設計者為了規避類似于內存管理這樣的復雜的競爭風險問題
- 因為 CPython 大量使用 C 語言庫,但大部分 C 語言庫都不是原生線程安全的(線程安全會降低性能和增加復雜度)
GIL 的設計,主要是為了方便 CPython 解釋器層面的編寫者,而不是 Python 應用層面的程序員。
可以使用
import dis
的方式將代碼編譯成 bytecode
垃圾回收機制
- 垃圾回收是 Python 自帶的機制,用于自動釋放不會再用到的內存空間
- 引用計數是其最簡單的實現,不過切記,這只是充分非必要條件,因為循環引用需要通過不可達判定,來確定是否可以回收
- Python 的自動回收算法包括標記清除和分代收集,主要針對的是循環引用的垃圾收集
- 調試內存泄漏方便,objgraph 是很好的可視化分析工具
編程規范
閱讀者的體驗 > 編程者的體驗 > 機器的體驗
- 學會合理分解代碼,提高代碼可讀性
- 合理利用 assert(線上環境禁止使用)
- 啟用上下文管理器和 with 語句精簡代碼
- 單元測試
- pdf & cprofile
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
