在計算機科學領域,有一句著名的格言(俏皮話):html
There are only two hard things in Computer Science: cache invalidation and naming things. 在計算機科學領域只有兩件難事:緩存過時 和 給東西起名字python
— Phil Karltondjango
第一個『緩存過時問題』的難度不用多說,任何用過緩存的人都會懂。至於第二個『給東西起名字』這事的難度,我也是深有體會。在個人職業生涯裏,度過的做爲黑暗的下午之一,就是坐在顯示器前抓耳撓腮爲一個新項目起一個合適的名字。編程
編程時起的最多的名字,還數各類變量。給變量起一個好名字很重要,由於好的變量命名能夠極大的提升代碼總體可讀性。數組
下面幾點,是我總結的爲變量起名時,最好遵照的基本原則。緩存
在可接受的長度範圍內,變量名能把它所指向的內容描述的越精確越好。因此,儘可能不要用那些過於寬泛的詞來做爲你的變量名:app
· GOOD: day_of_week, hosts_to_reboot, expired_cards編程語言
· BAD: day, host, cards, temp函數
老司機們都知道,Python 是一門動態類型語言,它(至少在 PEP 484 出現前)沒有變量類型聲明。因此當你看到一個變量時,除了經過上下文猜想,無法輕易知道它是什麼類型。工具
不過,人們對於變量名和變量類型的關係,一般會有一些直覺上的約定,我把它們總結在了下面。
布爾類型變量的最大特色是:它只存在兩個可能的值**『是』** 或 『不是』。因此,用 is、has等非黑即白的詞修飾的變量名,會是個不錯的選擇。原則就是:讓讀到變量名的人以爲這個變量只會有『是』或『不是』兩種值。
下面是幾個不錯的示例:
· is_superuser:『是否超級用戶』,只會有兩種值:是/不是
· has_error:『有沒有錯誤』,只會有兩種值:有/沒有
· allow_vip:『是否容許 VIP』,只會有兩種值:容許/不容許
· use_msgpack:『是否使用 msgpack』,只會有兩種值:使用/不使用
· debug:『是否開啓調試模式』,被當作 bool 主要是由於約定俗成
人們看到和數字相關的名字,都會默認他們是 int/float 類型,下面這些是比較常見的:
· 釋義爲數字的全部單詞,好比:port(端口號)、age(年齡)、radius(半徑) 等等
· 使用 _id 結尾的單詞,好比:user_id、host_id
· 使用 length/count 開頭或者結尾的單詞,好比: length_of_username、max_length、users_count
**注意:**不要使用普通的複數來表示一個 int 類型變量,好比 apples、trips,最好用 number_of_apples、trips_count 來替代。
對於 str、list、tuple、dict 這些複雜類型,很難有一個統一的規則讓咱們能夠經過名字去猜想變量類型。好比 headers,既多是一個頭信息列表,也多是包含頭信息的 dict。
對於這些類型的變量名,最推薦的方式,就是編寫規範的文檔,在函數和方法的 document string 中,使用 sphinx 格式(Python 官方文檔使用的文檔工具)來標註全部變量的類型。
第一次知道『匈牙利命名法』,是在 Joel on Software 的一篇博文中。簡而言之,匈牙利命名法就是把變量的『類型』縮寫,放到變量名的最前面。
關鍵在於,這裏說的變量『類型』,並不是指傳統意義上的 int/str/list 這種類型,而是指那些和你的代碼業務邏輯相關的類型。
好比,在你的代碼中有兩個變量:students 和 teachers,他們指向的內容都是一個包含 Person 對象的 list 。使用『匈牙利命名法』後,能夠把這兩個名字改寫成這樣:
students -> pl_students teachers -> pl_teachers
pl 是 person list 的首字母縮寫。變量名被加上前綴後,當你看到以 pl_ 打頭的變量時,就能知道它所指向的值類型了。
不少狀況下,使用『匈牙利命名法』是一個不錯的注意,它能夠改善你的代碼可讀性,尤爲在那些變量衆多、同一類型屢次出現時。注意不要濫用就好。
在前面,咱們提到要讓變量名有描述性。若是不給這條原則加上任何限制,那麼你頗有可能寫出這種描述性極強的變量名:how_much_points_need_for_level2。若是代碼中充斥着這種過長的變量名,對於代碼可讀性來講是個災難。
一個好的變量名,長度應該控制在 兩到三個單詞左右。好比上面的名字,能夠縮寫爲 points_level2。
絕大多數狀況下,都應該避免使用那些只有一兩個字母的短名字,好比數組索引三劍客 i、j、k,用有明確含義的名字,好比 persion_index 來代替它們老是會更好一些。
有時,不能使用短名字的原則也會有一些例外。當一些意義明確可是較長的變量名重複出現時,爲了讓代碼更簡潔,使用短名字縮寫是徹底能夠的。可是爲了下降理解成本,同一段代碼內最好不要使用太多這種短名字。
好比在 Python 中導入模塊時,就會常常用到短名字做爲別名,像 Django i18n 翻譯時經常使用的 gettext 方法一般會被縮寫成 _ 來使用(from django.utils.translation import ugettext as _)
其餘一些給變量命名的注意事項:
· 同一段代碼內不要使用過於類似的變量名,好比同時出現 users、users一、 user3 這種序列
· 不要使用帶否認含義的變量名,用 is_special 代替 is_not_normal
前面講了如何爲變量取一個好名字,下面咱們談談在平常使用變量時,應該注意的一些小細節。
若是你在一個方法內裏面把圖片變量叫作 photo,在其餘的地方就不要把它改爲 image,這樣只會讓代碼的閱讀者困惑:『image 和 photo 究竟是不是同一個東西?』
另外,雖然 Python 是動態類型語言,但那也不意味着你能夠用同一個變量名一會表示 str 類型,過會又換成 list。同一個變量名指代的變量類型,也須要保持一致性。
也許你第一次發現 globals()/locals() 這對內建函數時很興奮,火燒眉毛的寫下下面這種極端『簡潔』的代碼:
def render(request, user_id, trip_id): user= User.objects.get(id=user_id) trip= get_object_or_404(Trip, pk=trip_id) is_suggested= is_suggested(user, trip) # 利用 locals() 節約了三行代碼,我是個天才! **return** render(request, 'trip.html', locals())
千萬不要這麼作,這樣只會讓讀到這段代碼的人(包括三個月後的你本身)痛恨你,由於他須要記住這個函數內定義的全部變量(想一想這個函數增加到兩百行會怎麼樣?),更別提 locals() 還會把一些沒必要要的變量傳遞出去。
更況且, The Zen of Python(Python 之禪) 說的清清楚楚:Explicit is better than implicit.(顯式優於隱式)。仍是老老實實把代碼改爲這樣吧:
**return** render(request, 'trip.html', { 'user': user, 'trip': trip, 'is_suggested': is_suggested })
這個原則屬於老生常談了。不少人(包括我)在剛開始學習編程時,會有一個習慣。就是把全部的變量定義寫在一塊兒,放在函數或方法的最前面。
def generate_trip_png(trip): path= [] markers= [] photo_markers= [] text_markers= [] marker_count= 0 point_count= 0 ... ...
這樣作只會讓你的代碼『看上去很整潔』,可是對提升代碼可讀性沒有任何幫助。
更好的作法是,讓變量定義儘可能靠近使用。那樣當你閱讀代碼時,能夠更好的理解代碼的邏輯,而不是費勁的去想這個變量究竟是什麼、哪裏定義的?
Python 的函數能夠返回多個值:
def latlon_to_address(lat, lon): **return** country, province, city # 利用多返回值一次定義多個變量 country, province, city= latlon_to_address(lat, lon)
可是,這樣的用法會產生一個小問題:若是某一天, latlon_to_address 函數須要返回『城區(District)』時怎麼辦?
若是是上面這種寫法,你須要找到全部調用 latlon_to_address 的地方,補上多出來的這個變量,不然 ValueError: too many values to unpack 就會找上你:
country, province, city, district= latlon_to_address(lat, lon) # 或者忽略多出來的返回值 country, province, city, _= latlon_to_address(lat, lon)
對於這種多返回值可能會變更的狀況,使用 dict 做爲返回值會更方便一些,當你新增返回值時,不會對以前的函數調用產生任何破壞性的影響:
def latlon_to_address(lat, lon): **return** { 'country': country, 'province': province, 'city': city } addr_dict= latlon_to_address(lat, lon)
這樣作的壞處也有,代碼兼容性雖然增長了,可是你不能繼續用以前 x, y = f() 的方式一次定義多個變量了。取捨在於你本身。
人腦的能力是有限的,研究代表,人類的短時間記憶只能同時記住不超過十個名字。因此,當你的某個函數過長(通常來講,超過一屏的的函數就會被認爲有點過長了),包含了太多變量時。請及時把它拆分爲多個小函數吧。
這條原則很是簡單,也很容易作到。可是若是沒有遵照,那它對你的代碼質量的打擊是毀滅級的。會讓閱讀你代碼的人有一種被愚弄的感受。
def fancy_func(): # 讀者心理:嗯,這裏定義了一個 fancy_vars fancy_vars= get_fancy() ... ...(一大堆代碼事後) # 讀者心理:這裏就結束了?以前的 fancy_vars 去哪了?被貓吃了嗎? **return** result
因此,請打開 IDE 的智能提示,及時清理掉那些定義了可是沒有使用的變量吧。
有時候,咱們定義變量時的心理活動是這樣的:『嗯,這個值將來說不定會修改/二次使用』,讓咱們先把它定義成變量吧!
def get_best_trip_by_user_id(user_id): user= get_user(user_id) trip= get_best_trip(user_id) result= { 'user': user, 'trip': trip } **return** result
其實,你所想的『將來』永遠不會來,這段代碼裏的三個臨時變量徹底能夠去掉,變成這樣:
def get_best_trip_by_user_id(user_id): **return** { 'user': get_user(user_id), 'trip': get_best_trip(user_id) }
沒有必要爲了那些可能出現的變更,犧牲代碼當前的可讀性。若是之後有定義變量的需求,那就之後再加吧。
這是『Python 編程語言要掌握的技能之一』系列文章的第一篇,不知道看完文章的你,有沒有什麼想吐槽的?請留言告訴我吧。
更多Python技術文章請關注2019年,Python技術持續更新(附教程)