Python 代碼美學 (個人習慣及一些建議)


更簡潔的推導式

if else

# Q: 從服務器返回的響應可能是文本, 也可能是 null, 請判斷當爲 null 時, 轉換爲 ''

# == Good ==
if response is None:
    data = ''
else:
    data = response

# == Better ==
data = '' if response is None else response

# == Best ==
data = response or ''

list comprehension vs filter

# Q: 已知一個數字組成的列表, 獲取裏面大於 0 的數字
alist = [1, 0, -1, 2]

# == Good ==
positive_num = (x for x in alist if x > 0)

# == Better ==
positive_num = filter(lambda x: x > 0, alist)

海象運算符 (Python 3.8)

# Q: 書架上有 a 本書, 用戶借了 b 本, 請分別打印 a, b 和 b / a 的百分比值

# == Bad ==
print('書架上有 {} 本書, 用戶借閱 {} 本, 佔比 {}%'.format(
    len(total_books),
    len(rent_books),
    round(len(rent_books) / len(total_books) * 100, 2)
))

# == Good ==
a = len(total_books)
b = len(rent_books)
print('書架上有 {} 本書, 用戶借閱 {} 本, 佔比 {}%'.format(
    a, b, round(b / a * 100, 2)
))

# == Better ==
print('書架上有 {} 本書, 用戶借閱 {} 本, 佔比 {}%'.format(
    a := len(total_books),
    b := len(rent_books),
    round(b / a * 100, 2)
))

reduce 遞歸替換

# Q: 請將 text 中的 A 替換爲 Apple, B 替換爲 Banana, 得到一個新文本
text = 'A;B'
translator = {'A': 'Apple', 'B': 'Banana'}

# == Good ==
new_text = text
for k, v in translator.items():
    new_text = new_text.replace(k, v)

# == Tricky ==
from functools import reduce
new_text = reduce(lambda x, args: x.replace(*args), translator.items(), text)

列表展平

pass

字典合併

a = {'A': 'Apple', 'B': 'Banana'}
b = {'A': 'Almond', 'C': 'Cherry'}
merged = {**a, **b}
# -> {'A': 'Almond', 'B': 'Banana', 'C': 'Cherry'}

更合理的做法

類變量

# == Bad ==
class AAA:
    # 會產生單例對象 (如果這不是你想要的話)
    keywords = ['first', 'second', 'third']

    def __init__(keywords=None):
        if keywords:
            self.keywords.extend(keywords)

# == Good ==
class BBB:
    def __init__(keywords=None):
        self.keywords = ['first', 'second', 'third']
        if keywords:
            self.keywords.extend(keywords)

# == Better ==
class CCC:
    keywords: list  # 這樣做有利於子類繼承時, 更安全地覆寫

    def __init__(keywords=None):
        self.keywords = ['first', 'second', 'third']
        if keywords:
            self.keywords.extend(keywords)

變量命名習慣 (僅供參考!)

對稱好於不對稱

注: 僅建議在局部範圍嘗試.

# == Good ==
benchmark = Benchmark()
select = Select()
provide = Provide()

# == Obsessive ==
bcmk = Benchmark()
slct = Select()
prvd = Provide()

# ------------------------------------------------

# == Good ==
class Foo:
    def fast_calc():
        pass
    def std_calc():  # std: standard
        pass

# == Obsessive 1 ==
class Foo:
    def fst_calc():  # fst: fast
        pass
    def std_calc():  # std: standard
        pass

# == Obsessive 2 ==
class Foo:
    def fast_calc():
        pass
    def stnd_calc():  # stnd: standard
        pass

語義明確好於語義簡潔

# == Bad ==
er = ExcelReader()
ew = ExcelWriter()
fr = FileReader()
fw = FileWriter()

# == Good ==
reader1 = ExcelReader()
writer1 = ExcelWriter()
reader2 = FileReader()
writer2 = FileWriter()

# == Better ==
exl_reader = ExcelReader()
exl_writer = ExcelWriter()
file_reader = FileReader()
file_writer = FileWriter()

格式一致好於格式不一致

class MyFinder:

    def find_one(self, x):
        pass

    def find_last(self, x):
        pass

    # == Not Good ==
    def findall(self, x):
        pass

    # == Good ==
    def find_all(self, x):
        pass

使用 i-, o- 前綴表示 “輸入”, “輸出”

# (建議在局部變量使用)
for ifile, ofile in file_io.items():
    ilist = get_list_from_file(ifile)
    olist = handle_list(ilist)
    write_result(olist, ofile)

目錄路徑末尾不加斜槓

idir = '../data'
odir = '../output'

for name in name_list:
    ifile = f'{idir}/{name}.html'
    ofile = f'{odir}/{name}.json'
    ...

爲什麼?

顯式的斜槓更易於閱讀. 這點類似於百分號, 查看下面的例子:

# == Bad ==
def calc_percent_1(m, n):
    return str(round(m / n * 100, 2)) + '%'

# == Good ==
def calc_percent_2(m, n):
    return round(m / n * 100, 2)

print('當前的工作進度是 {}'.format(calc_percent_1(1, 3)))
print('當前的工作進度是 {}%'.format(calc_percent_2(1, 3)))

前者在易讀性上更差一些. 特別是當計算百分比的函數和 print 在不同的 py 文件時, 前者更容易讓人忽略掉它是百分數的事實. (而且 calc_percent_1 在函數 “單一職能” 的表達上也不如 calc_percent_2)

路徑的斜槓的使用原則和這個百分號示例有着相同之處. 這是爲什麼不推薦在目錄路徑末尾加斜槓的原因.

代碼風格 (非 PEP8 範疇, 僅供交流)

減少嵌套, 儘快返回

注: 僅在單個函數內涉及的條件判斷過多時使用.

== Bad ==

pass

== Good ==

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fHH12uX4-1579151769273)(…/附件/圖片/2019/20200116113121.png)]

項目目錄結構 (僅供參考!)

# 適合個人目錄
# 豎線 (|) 表示別名

myproj
    bin: 字節碼文件, 可執行文件 (可選)
    conf | config | settings: 配置文件 (可選, 可整合到 data)
        config.json: 總配置文件
    data | model: 數據, 模型
        cache: 緩存 (可選, 可調整爲一級目錄)
    docs: 文檔或資料文件
        sample | demo | examples: 樣本 (可選, 可調整爲一級目錄)
    dustbin: 臨時的回收站 (可選)
    lib: 第三方庫 (可選)
    log: 日誌文件 (可選, 可調整爲二級目錄)
    src | myproj | code | core: 源碼目錄
        utils | tools: 工具類腳本 (可選, 可調整爲一級目錄)
        sidework: 非主線腳本 (可選)
        main.py | app.py | launch.py | launcher.py | run.py: 啓動文件, 主入口
    output | report
    res | resource | source: 資源文件 (可選, 可整合到 data)
    temp (可選)
        in.txt
        out.txt
        tmp.py
    tests | testflight (可選)
    venv | env: 環境和依賴 (可選)
    CHANGELOG.txt | CHANGELOG: 更新日誌 (可整合到 docs)
    README.md | README: 自述文檔 (可整合到 docs)

示例 (截圖如下):

在這裏插入圖片描述

下載項目目錄模板: https://www.lanzous.com/i7dgf3e


參考

  • Python代碼怎麼寫,聽聽頂尖Python大神 kennethreitz 的建議 - 雲+社區 - 騰訊雲 https://cloud.tencent.com/developer/news/332901
  • chiphuyen/python-is-cool https://github.com/chiphuyen/python-is-cool
  • Python 有哪些讓你相見恨晚的技巧? - 量子位的回答 - 知乎 https://www.zhihu.com/question/48755767/answer/873187244
  • 改善 Python 程序的 91 個建議 https://mp.weixin.qq.com/s?__biz=MjM5NzEyMzg4MA==&mid=2649413298&idx=2&sn=88080e190f8ace42c8d5b0a1a9323426&chksm=bec0b4f589b73de379a081e77da32db66a1fa8db4e94236af203a1a995bd6b8a4fbc7c9b9a3c
  • 如何合理的規劃一個 python 的項目目錄? - V2EX - https://www.v2ex.com/t/229241
  • What is the best project structure for a Python application? - Stack Overflow https://stackoverflow.com/questions/193161/what-is-the-best-project-structure-for-a-python-application
  • python基礎6–目錄結構 - Bigberg - 博客園 https://www.cnblogs.com/bigberg/p/6423164.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章