代碼整潔之道-編寫 Pythonic 代碼

2021-02-23 Python大本營

作者 | 陳祥安

編輯 | suiling

出品 | Python學習開發(ID:python3-5)

很多新手在開始學一門新的語言的時候,往往會忽視一些不應該忽視的細節,比如變量命名和函數命名以及注釋等一些內容的規範性,久而久之養成了一種習慣。對此呢,我特意收集了一些適合所有學習 Python 的人,代碼整潔之道。

寫出 Pythonic 代碼

談到規範首先想到就是 Python 有名的 PEP8 代碼規範文檔,它定義了編寫Pythonic代碼的最佳實踐。可以在 

https://www.python.org/dev/peps/pep-0008/ 上查看。但是真正去仔細研究學習這些規範的朋友並不是很多,對此呢這篇文章摘選一些比較常用的代碼整潔和規範的技巧和方法,下面讓我們一起來學習吧!

命名

所有的程式語言都有變量、函數、類等的命名約定,以美之稱的 Python 當然更建議使用命名約定。 接下來就針對類、函數、方法等等內容進行學習。

變量和函數

使用小寫字母命名函數和變量,並用下劃線分隔單詞,提高代碼可讀性。

變量的聲明


names = "Python" #變量名 
namejob_title = "Software Engineer" #帶有下劃線的變量名
populated_countries_list = []  #帶有下劃線的變量名 

還應該考慮在代碼中使用非 Python 內置方法名,如果使用 Python 中內置方法名請使用一個或兩個下劃線()。

_books = {}# 變量名私有化
__dict = []# 防止python內置庫中的名稱混淆

那如何選擇是用_還是__呢?

如果不希望外部類訪問該變量,應該使用一個下劃線(_)作為類的內部變量的前綴。如果要定義的私有變量名稱是 Python 中的關鍵字如 dict 就要使用(__)。

函數的聲明

def get_data(): 
    pass
def calculate_tax_data():
    pass

函數的聲明和變量一樣也是通過小寫字母和單下劃線進行連接。

當然對於函數私有化也是和聲明變量類似。

def _get_data():
   pass

函數的開頭使用單下劃線,將其進行私有化。對於使用 Pyton 中的關鍵字來進行命名的函數


要使用雙下劃線。

def __path():
    pass

除了遵循這些命名規則之外,使用清晰易懂的變量名和很重要。

函數名規範


# Wrong Way
def get_user_info(id):
   db = get_db_connection()
   user = execute_query_for_user(id)
   return user

# Right way
def get_user_by(user_id):
    db = get_db_connection()
    user = execute_user_query(user_id) 
    return user

這裡,第二個函數 get_user_by 確保使用相同的參數來傳遞變量,從而為函數提供正確的上下文。 第一個函數 get_user_info 就不怎麼不明確了,因為參數 id 意味著什麼這裡我們不能確定,它是用戶 ID,還是用戶付款ID或任何其他 ID? 這種代碼可能會對使用你的API的其他開發人員造成混淆。為了解決這個問題,我在第二個函數中更改了兩個東西; 我更改了函數名稱以及傳遞的參數名稱,這使代碼可讀性更高。

作為開發人員,你有責任在命名變量和函數時仔細考慮,要寫讓人能夠清晰易懂的代碼。

當然也方便自己以後去維護。

類的命名規範

類的名稱應該像大多數其他語言一樣使用駝峰大小寫。

class UserInformation:
     def get_user(id):
          db = get_db_connection()
          user = execute_query_for_user(id)
          return user


常量的命名規範

通常應該用大寫字母定義常量名稱。

TOTAL = 56
TIMOUT = 6
MAX_OVERFLOW = 7

函數和方法的參數

函數和方法的參數命名應遵循與變量和方法名稱相同的規則。因為類方法將self作為第一個關鍵字參數。所以在函數中就不要使用 self 作為關鍵字作為參數,以免造成混淆。

def calculate_tax(amount, yearly_tax):
    passs

class Player:
    def get_total_score(self, player_name):
         pass

關於命名大概就強調這些,下面讓我們看看表達式和語句中需要的問題。

代碼中的表達式和語句

users = [
    {"first_name":"Helen", "age":39},
    {"first_name":"Buck", "age":10},
    {"first_name":"anni", "age":9}
]
users = sorted(users, key=lambda user: user["first_name"].lower())

這段代碼有什麼問題?

乍一看並不容易理解這段代碼,尤其是對於新開發人員來說,因為 lambdas 的語法很古怪,所以不容易理解。雖然這裡使用 lambda 可以節省行,然而,這並不能保證代碼的正確性和可讀性。同時這段代碼無法解決字典缺少鍵出現異常的問題。

讓我們使用函數重寫此代碼,使代碼更具可讀性和正確性; 該函數將判斷異常情況,編寫起來要簡單得多。

users = [
    {"first_name":"Helen", "age":39},
    {"first_name":"Buck", "age":10},
    {"name":"anni", "age":9}
]
def get_user_name(users):
    """Get name of the user in lower case"""
    return users["first_name"].lower()
def get_sorted_dictionary(users):
    """Sort the nested dictionary"""
    if not isinstance(users, dict):
        raise ValueError("Not a correct dictionary")
    if not len(users):
       raise ValueError("Empty dictionary")
    users_by_name = sorted(users, key=get_user_name)
    return users_by_name

如你所見,此代碼檢查了所有可能的意外值,並且比起以前的單行代碼更具可讀性。 單行代碼雖然看起來很酷節省了行,但是會給代碼添加很多複雜性。 但是這並不意味著單行代碼就不好 這裡提出的一點是,如果你的單行代碼使代碼變得更難閱讀,那麼就請避免使用它,記住寫代碼不是為了炫酷的,尤其在項目組中。

讓我們再考慮一個例子,你試圖讀取 CSV 文件並計算 CSV 文件處理的行數。下面的代碼展示使代碼可讀的重要性,以及命名如何在使代碼可讀中發揮重要作用。

import csv
with open("employee.csv", mode="r") as csv_file:
    csv_reader = csv.DictReader(csv_file)
    line_count = 0
    for row in csv_reader:
        if line_count == 0:
            print(f'Column names are {", ".join(row)}')
            line_count += 1
            print(f'\t{row["name"]} salary: {row["salary"]}'
            f'and was born in {row["birthday month"]}.')
        line_count += 1
    print(f'Processed {line_count} lines.')

將代碼分解為函數有助於使複雜的代碼變的易於閱讀和調試。

這裡的代碼在 with 語句中執行多項操作。為了提高可讀性,您可以將帶有 process salary 的代碼從 CSV 文件中提取到另一個函數中,以降低出錯的可能性。

import csv
with open("employee.csv", mode="r") as csv_file:
    csv_reader = csv.DictReader(csv_file)
    line_count = 0
    process_salary(csv_reader)    


def process_salary(csv_reader):
"""Process salary of user from csv file."""
    for row in csv_reader:
            if line_count == 0:
                print(f'Column names are {", ".join(row)}')
                line_count += 1
                print(f'\t{row["name"]} salary: {row["salary"]}'
                f'and was born in {row["birthday month"]}.')
            line_count += 1
        print(f'Processed {line_count} lines.') 

代碼是不是變得容易理解了不少呢。

在這裡,創建了一個幫助函數,而不是在with語句中編寫所有內容。這使讀者清楚地了解了函數的實際作用。如果想處理一個特定的異常或者想從CSV文件中讀取更多的數據,可以進一步分解這個函數,以遵循單一職責原則,一個函數一做一件事,這個很重要。

return語句的類型儘量一致

如果希望函數返回一個值,請確保該函數的所有執行路徑都返回該值。但是,如果期望函數只是在不返回值的情況下執行操作,則 Python 會隱式返回 None 作為函數的默認值。

先看一個錯誤示範:

def calculate_interest(principle, time rate):    
     if principle > 0:
         return (principle * time * rate) / 100
def calculate_interest(principle, time rate):    
    if principle < 0:
        return    
    return (principle * time * rate) / 100ChaPTER 1  PyThonIC ThInkIng

正確的示範應該是下面這樣

def calculate_interest(principle, time rate):    
     if principle > 0:
         return (principle * time * rate) / 100
     else:
        return None
def calculate_interest(principle, time rate):    
    if principle < 0:
        return None    
    return (principle * time * rate) / 100ChaPTER 1  PyThonIC ThInkIng

還是那句話寫易讀的代碼,代碼多寫點沒關係,可讀性很重要。

使用 isinstance() 方法而不是 type() 進行比較

當比較兩個對象類型時,請考慮使用 isinstance() 而不是 type,因為 isinstance() 判斷一個對象是否為另一個對象的子類是 true。考慮這樣一個場景:如果傳遞的數據結構是dict 的子類,比如 orderdict。type() 對於特定類型的數據結構將失敗;然而,isinstance() 可以將其識別出它是 dict 的子類。

錯誤示範

user_ages = {"Larry": 35, "Jon": 89, "Imli": 12}
type(user_ages) == dict:

正確選擇

user_ages = {"Larry": 35, "Jon": 89, "Imli": 12}
if isinstance(user_ages, dict):

比較布爾值

在Python中有多種方法可以比較布爾值。
錯誤示範

if is_empty = False
if is_empty == False:
if is_empty is False:

正確示範

is_empty = False
if is_empty

使用文檔字符串

Docstrings可以在 Python 中聲明代碼的功能的。通常在方法,類和模塊的開頭使用。 docstring是該對象的__doc__特殊屬性。
Python 官方語言建議使用「」三重雙引號「」來編寫文檔字符串。 你可以在 PEP8 官方文檔中找到這些實踐。 下面讓我們簡要介紹一下在 Python 代碼中編寫 docstrings 的一些最佳實踐 。

方法中使用docstring

def get_prime_number():
    """Get list of prime numbers between 1 to 100.""""

關於docstring的格式的寫法,目前存在多種風格,但是這幾種風格都有一些統一的標準。

多行的docstring

def call_weather_api(url, location):
    """Get the weather of specific location.

    Calling weather api to check for weather by using weather api and 
    location. Make sure you provide city name only, country and county 
    names won't be accepted and will throw exception if not found the 
    city name.

    :param url:URL of the api to get weather.
    :type url: str
    :param location:Location of the city to get the weather.
    :type location: str
    :return: Give the weather information of given location.
    :rtype: str"""

說一下上面代碼的注意點

第一行是函數或類的簡要描述

每一行語句的末尾有一個句號

文檔字符串中的簡要描述和摘要之間有一行空白

如果使用 Python3.6 可以使用類型註解對上面的docstring以及參數的聲明進行修改。

def call_weather_api(url: str, location: str) -> str:
    """Get the weather of specific location.

    Calling weather api to check for weather by using weather api and 
    location. Make sure you provide city name only, country and county 
    names won't be accepted and will throw exception if not found the 
    city name.
    """

怎麼樣是不是簡潔了不少,如果使用 Python 代碼中的類型註解,則不需要再編寫參數信息。

關於類型註解(type hint)的具體用法可以參考我之前寫的python類型檢測最終指南--Typing的使用

模塊級別的docstring

一般在文件的頂部放置一個模塊級的 docstring 來簡要描述模塊的使用。

這些注釋應該放在在導包之前,模塊文檔字符串應該表明模塊的使用方法和功能。

如果覺得在使用模塊之前客戶端需要明確地知道方法或類,你還可以簡要地指定特定方法或類。

"""This module contains all of the network related requests. 
This module will check for all the exceptions while making the network 
calls and raise exceptions for any unknown exception.
Make sure that when you use this module,
you handle these exceptions in client code as:
NetworkError exception for network calls.
NetworkNotFound exception if network not found.
"""

import urllib3
import json

在為模塊編寫文檔字符串時,應考慮執行以下操作:

NetworkError exception for network calls.
NetworkNotFound exception if network not found.

類級別的docstring

類docstring主要用於簡要描述類的使用及其總體目標。 讓我們看一些示例,看看如何編寫類文檔字符串

單行類docstring

class Student:
   """This class handle actions performed by a student."""
   def __init__(self):
      pass

這個類有一個一行的 docstring,它簡要地討論了學生類。如前所述,遵守了所以一行docstring 的編碼規範。

多行類docstring

class Student:
    """Student class information.

    This class handle actions performed by a student.
    This class provides information about student full name, age, 
    roll-number and other information.
    Usage:
        import student
        student = student.Student()
        student.get_name()
        >>> 678998
   """
   def __init__(self):
       pass

這個類 docstring 是多行的; 我們寫了很多關於 Student 類的用法以及如何使用它。

函數的docstring

函數文檔字符串可以寫在函數之後,也可以寫在函數的頂部。

def is_prime_number(number):
    """Check for prime number.

    Check the given number is prime number 
    or not by checking against all the numbers 
    less the square root of given number.

    :param number:Given number to check for prime
    :type number: int
    :return: True if number is prime otherwise False.
    :rtype: boolean
    """

如果我們使用類型註解對其進一步優化。

def is_prime_number(number: int)->bool:
    """Check for prime number.

    Check the given number is prime number 
    or not by checking against all the numbers 
    less the square root of given number.
    """

結語

當然關於 Python 中的規範還有很多很多,建議大家參考 Python 之禪和 Pep8 對代碼進行優化,養成編寫 Pythonic 代碼的良好習慣。

原文地址:

https://mp.weixin.qq.com/s/fPpZlHS4tBDB3Y_bKaKoBg

(*本文僅代表作者觀點,轉載請聯繫原作者)


相關焦點

  • Python代碼如何升級為Pythonic 代碼
    符合這樣要求的代碼也被python社區稱為pythonic的代碼。正文共:7841 字預計閱讀時間:20 分鐘Python是當今最流行的語言之一。相對較新的領域如數據科學、人工智慧、機器人和數據分析,以及傳統的專業如Web開發和科學研究等,都在擁抱Python。對於用Python這樣的動態語言編寫代碼的程式設計師來說,確保代碼的高質量和無錯誤變得越來越重要。
  • 《代碼整潔之道》:5大基本要點
    全文共3257字,預計學習時長10分鐘評論區常常有小夥伴推薦羅伯特·C·馬丁的《代碼整潔之道(Clean Code)》。今天我們就來了解一下這本書,它值不值得一看?關於此書《代碼整潔之道》出版於2008年,近年來,一直被列為「亞馬遜最暢銷的五本書」之一。本書作者被親切地稱為「Bob叔叔」,他也是《敏捷宣言》的原作者之一,資歷非常豐富。本書在Goodreads上平均評分為4.4(評分人數超13,000)。可以說,這是一本程式設計師的必讀書。
  • 《代碼整潔之道》精讀與演繹】之一 讓代碼比你來時更乾淨
    本文引用地址:http://www.eepw.com.cn/article/201608/295974.htm  ——《代碼整潔之道》作者 RobertC.Martin,於SD West 2007技術大會  一、系列文章前言  敲完上面這段文字的時候,心裡在想,一個剛踏入編程生涯的新人
  • 代碼整潔之道的 7 個方法
    本文代碼示例均使用 JavaScript。我發現但凡是可讀的代碼必定是可維護的。作為一名開發人員,我的目標是編寫高質量的代碼。團隊中的每個開發人員,不管他/她的技術水平如何,都必須能夠通過閱讀理解我所寫的代碼。代碼的可讀性有助於年輕的開發人員編寫代碼時更加自信。
  • 《代碼整潔之道》精讀與演繹】之四 優秀代碼的格式準則
    《代碼整潔之道》這本書提出了一個觀點:代碼質量與其整潔度成正比,乾淨的代碼,既在質量上可靠,也為後期維護、升級奠定了良好基礎。書中介紹的規則均來自作者多年的實踐經驗,涵蓋從命名到重構的多個編程方面,雖為一「家」之言,然誠有可資借鑑的價值。  但我們知道,很多時候,理想很豐滿,現實很骨感,也知道人在江湖,身不由己。
  • <讀書筆記> 代碼整潔之道
    概述本文引用地址:http://www.eepw.com.cn/article/201608/294841.htm  1、本文檔的內容主要來源於書籍《代碼整潔之道》作者Robert C.Martin,屬於讀書筆記。
  • 【《代碼整潔之道》精讀與演繹】之二 高質量代碼的命名法則
    本文引用地址:http://www.eepw.com.cn/article/201608/296112.htm  一、引言  《代碼整潔之道》這本書提出了一個觀點:代碼質量與其整潔度成正比,乾淨的代碼,既在質量上可靠,也為後期維護、升級奠定了良好基礎。
  • 《代碼整潔之道(Clean Code)》讀書筆記
    注釋可以用來放大某種看似不合理之物的重要性8. 公共 API 注釋4. 壞注釋1. 喃喃自語。應該過程需要就添加注釋,是無畏之舉2. 多餘的注釋。代碼清晰明了,讀注釋比讀代碼花的時間長3.只可編寫剛好足以通過當前失敗測試的生產代碼2. 保持測試整潔1. 測試代碼和生產代碼一樣重要。它需要被思考、被設計和被照料。它該像生產代碼一般保持整潔,避免無法維護測試代碼2. 測試越髒,代碼就會變得越髒。
  • 【《代碼整潔之道》精讀與演繹】之五 整潔類的書寫準則
    《代碼整潔之道》這本書提出了一個觀點:代碼質量與其整潔度成正比,乾淨的代碼,既在質量上可靠,也為後期維護、升級奠定了良好基礎。書中介紹的規則均來自作者多年的實踐經驗,涵蓋從命名到重構的多個編程方面,雖為一「家」之言,然誠有可資借鑑的價值。  但我們知道,很多時候,理想很豐滿,現實很骨感,也知道人在江湖,身不由己。
  • JavaScript 代碼整潔之道(中)
    目錄概述變量函數對象和數據結構類測試並發錯誤處理格式注釋類單一職責原則 (SRP)正如《代碼整潔之道extends Mammal {    constructor(age, furColor, languageSpoken) {        super(age, furColor);        this.languageSpoken = languageSpoken;    }    speak() {}}使用方法鏈在這裡我的意見與《代碼整潔之道
  • 代碼這樣寫更優雅(Python版)
    題圖:unsplash.comPython 這門語言最大的優點之一就是語法簡潔,好的代碼就像偽代碼一樣,乾淨、整潔、一目了然。而用 join 方法整個過程只會產生一個字符串對象。5、打開/關閉文件執行文件操作時,最後一定不能忘記的操作是關閉文件,即使報錯了也要 close。普通的方式是在 finnally 塊中顯示的調用 close 方法。
  • 如何編寫簡潔美觀的Python代碼
    介紹你有沒有遇到過一段寫得很糟糕的Python代碼?我知道你們很多人都會點頭的。編寫代碼是數據科學家或分析師角色的一部分。另一方面,編寫漂亮整潔的Python代碼完全是另一回事。作為一個精通分析或數據科學領域(甚至軟體開發)的程式設計師,這很可能會改變你的形象。
  • 讓你的 Python 代碼優雅又地道
    —— Edsger Wybe Dijkstra在Python社區文化的澆灌下,演化出了一種獨特的代碼風格,去指導如何正確地使用Python,這就是常說的pythonic。一般說地道(idiomatic)的python代碼,就是指這份代碼很pythonic。Python的語法和標準庫設計,處處契合著pythonic的思想。
  • Python編寫代碼的規範要求
    打開APP Python編寫代碼的規範要求 碼農阿勇 發表於 2020-01-16 17:44:00 在我們日常生活中,做什麼事情講究規矩
  • 使用with關鍵字讓你的Python代碼更加Pythonic
    以文件操作為例,很多人會像下面的代碼這樣寫:fp = open('test.txt', 'w')fp.write('Hello world')fp.close()這也是文件操作的標準套路:打開文件-->讀寫文件-->關閉文件,看上去也中規中矩 。
  • 經驗之談:代碼該怎樣寫才能幹淨整潔
    能把代碼寫出來是一回事,但是寫出整潔、可讀的代碼又是另一回事。然而,什麼是「乾淨的代碼」呢?怎麼才能寫出「乾淨的代碼」?為了解答這些問題,本文作者寫了一份針對初級開發者的乾淨代碼指南。不妨想像一下,你正在閱讀一篇文章。文章開頭有一個段落簡要概述了文章的內容。
  • Kotlin代碼優雅之道
    Kotlin更便捷先進的語法比之於蘋果IOS平臺,有稱之為安卓的Swift語言。在本文中,蟲蟲會介紹一些在安卓開發中可能用到的技巧(代碼),幫你優化代碼,讓代碼更簡潔、明了,易於維護。記錄日誌更優雅在安卓中如果要添加日誌日誌,通常是下面這樣的寫法上述代碼,首先聲明一個TAG,它只是現有類的類名。寫法比較凌亂,不好看清楚。
  • 代碼詳解:如何用Python運行高性能的數學範式?
    首先需要明確的是:編寫python代碼和編寫pythonic代碼之間存在很大差異。這篇文章圍繞一些最常用的數據科學操作編寫了最佳(也存在爭議的)實踐。這個庫之所以變得非常便捷,是因為內置的多線程支持。下面是在Github上運行的連結,用於整個代碼基礎:harshitcodes/python_paradigmsA guide to: How to compare different implementation paradigms
  • 0基礎學習JavaScript一定要知道如何使用VS2019去編寫代碼
    編寫JS代碼對於.NET開發者來說,前端技術也是不能少的,0基礎的開發者來說,使用Visual Studio 2019開發工具是非常容易上手的。這裡創建一個空白的Web應用程式項目,ASP.NET Web應用程式是完全支持JavaScript代碼編寫的。
  • 程式設計師每天編寫3000行代碼是什麼水平
    每天寫3000行代碼在當前的IT行業內是很難想像的,即使很多早期從事外包開發的程式設計師也很難有這樣的工作效率,大部分程式設計師每天的代碼量都在幾百行左右,研發級程式設計師一天的代碼量通常不會超過300行,應用級程式設計師的代碼量也很少能夠突破500行。