print
是我們平時寫些python
小工具時,最常用的調試工具。
因爲開發代碼時,常常通過print
將執行流程、變量的值以及其他關鍵信息輸出到控制檯來觀察,
以便了解程序執行情況和調試bug
。
但是,print
的輸出過於簡單,在輸出變量內容,函數調用,執行過程等相關信息時,
往往需要自己手動去補充很多的輸出信息的說明,否則很容易搞不清輸出的內容是什麼。
而今天介紹的icecream
,爲我們提供了一種更加優雅和強大的方式來調試代碼。
它不僅可以自動格式化輸出內容,自動添加必要的描述信息,而且使用起來也比print
更加簡單。
1. 安裝
通過pip
安裝:
pip install icecream
安裝之後可以通過打印其版本來驗證是否安裝成功。
2. 使用示例
下面看看icecream
如何替換開發中常見的各種print
場景。
2.1. 調試變量
首先是調試變量,這也是用的最多的場景。
開發中,我們常常需要將變量打印出來以確認是否正確賦值。
print
方式:
# 數值和字符串
i = 100
f = 3.14
s = "abc"
print(i, f, s)
# 元組,列表和字典
t = (10, 20, 30)
l = [1, 2, 3]
d = {"A": "abc", "B": 100}
print(t, l, d)
print(t[0], l[1], d["A"])
# 類
class c:
name = "ccc"
addr = "aa bb cc"
print(c.name, c.addr)
icecream
方式:
from icecream import ic
# 數值和字符串
i = 100
f = 3.14
s = "abc"
ic(i, f, s)
# 元組,列表和字典
t = (10, 20, 30)
l = [1, 2, 3]
d = {"A": "abc", "B": 100}
ic(t, l, d)
ic(t[0], l[1], d["A"])
# 類
class c:
name = "ccc"
addr = "aa bb cc"
ic(c.name, c.addr)
通過比較,可以看出icecream
的幾個優勢:
- 輸入效率更高,因爲
ic
比print
更容易輸入,只有兩個字母。 - 自動帶上變量名稱,一眼看出打印的是哪個變量的值
- 變量名稱和值用不同的顏色顯示,容易區分
2.2. 調試函數輸出
調試函數輸出也是常用的,如果把函數調用也看做一個變量的話,其實這個和上面打印變量類似。print
方式:
def func(a: int, b: int):
return a + b
print(func(2, 3))
icecream
方式:
from icecream import ic
def func(a: int, b: int):
return a + b
ic(func(2, 3))
2.3. 調試執行過程
接下來是調試執行過程,當代碼中有很多分支判斷時,我們常常是在各個分支中print
不同的數字,
然後用不同的輸入看看代碼是否按照預期的那樣進入不同而分支。
比如,下面構造一個多分支判斷的函數,看看分別用print
和icecream
是如何調試的。
print
方式:
def pflow(a: float, b: float):
print(1)
evaluate = ""
if a > 90 and b > 90:
print(2)
evaluate = "優"
elif a > 80 and b > 80:
print(3)
evaluate = "良"
elif a > 70 and b > 70:
print(4)
evaluate = "中"
else:
print(5)
evaluate = "及格"
if a < 60 or b < 60:
print(6)
evaluate = "不合格"
print(7)
return evaluate
pflow(98, 92)
print("---------------------")
pflow(75, 65)
print("---------------------")
pflow(88, 85)
print("---------------------")
pflow(77, 72)
print("---------------------")
pflow(98, 55)
需要根據數字去看看分支執行是否符合預期。
icecream
方式:
from icecream import ic
def flow(a: float, b: float):
ic()
evaluate = ""
if a > 90 and b > 90:
ic()
evaluate = "優"
elif a > 80 and b > 80:
ic()
evaluate = "良"
elif a > 70 and b > 70:
ic()
evaluate = "中"
else:
ic()
evaluate = "及格"
if a < 60 or b < 60:
ic()
evaluate = "不合格"
ic()
return evaluate
flow(98, 92)
ic()
flow(75, 65)
ic()
flow(88, 85)
ic()
flow(77, 72)
ic()
flow(98, 55)
簡簡單單的一個**ic()**
,會把執行的代碼位置和函數名稱,執行時間等打印出來。
2.4. 定製化輸出
最後,icecream
還提供了強大的定製化接口,可以按照自己的需要調整輸出的內容。
首先,我們注意到通過ic()
打印的內容都有一個ic |
前綴,
實際使用時,我們希望將其替換爲和項目相關的文字。
比如,我基於manim
做個小動畫,希望打印的前綴是 manim |
。
from icecream import ic
def cfg():
ic.configureOutput(prefix="manim -> | ")
ic("something")
cfg()
ic("something")
前綴還可以是動態的,比如用執行時間作爲前綴:
from icecream import ic
def cfg():
import time
time_prefix = lambda: time.strftime("%Y-%m-%d %H:%M:%S -> | ", time.localtime())
ic.configureOutput(prefix=time_prefix)
ic("something")
cfg()
ic("something")
除了定義前綴,還可以在輸出時添加我們需要的信息。
比如,我們希望打印字符串,列表和字典變量時,順帶輸出其長度信息,不用在再去額外打印其長度信息。
from icecream import ic
def add_info(obj):
if isinstance(obj, str) or isinstance(obj, list) or isinstance(obj, dict):
return f"{obj}(len:{len(obj)})"
return repr(obj)
ic.configureOutput(argToStringFunction=add_info)
i = 100
f = 3.14
s = "abc"
ic(i, f, s)
t = (10, 20, 30)
l = [1, 2, 3]
d = {"A": "abc", "B": 100}
ic(t, l, d)
從打印內容可以看出,字符串,列表和字典變量後面有長度len
信息,
而數值變量和元組,則沒有打印長度len
信息。
同樣,在數據分析時,也可以通過定製,
讓我們打印pandas
的DataFrame
內容時,順帶打印出其shape
信息。
import pandas as pd
def add_info(obj):
if isinstance(obj, pd.DataFrame):
return f"{obj}\nshape:{obj.shape}"
return repr(obj)
df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
ic(df)
3. 總結
總的來說,icecream
提供了一種更加現代和高效的調試方式,讓我們更關注需要打印的內容,不用去操心打印的格式。
除了python
,icecream
還有一系列其他語言的接口:
- Dart: icecream
- Rust: icecream-rs
- Node.js: node-icecream
- C++: IceCream-Cpp
- C99: icecream-c
- PHP: icecream-php
- Go: icecream-go
- Ruby: Ricecream
- Java: icecream-java
- R: icecream
- Lua: icecream-lua
- Clojure(Script): icecream-cljc
- Bash: IceCream-Bash