一種編程語言,都有以下概念:
都有許多需要程序員命名的地方。
不同的語言,命名規范各不相同。
對Python來說,PEP8推薦的命名規范如下(此處基于Effective Python書中列舉的條目整理,PEP8標準中會更詳細些,筆者建議大家可以簡單過一遍PEP8文檔):
用小寫字母配下劃線,例如lowercase_underscore。
類對象的屬性(attributes,筆者會更習慣使用“成員變量”這個詞)和保護變量由變量名前方的下劃線數量決定,一個下劃線是保護變量(_protect_value),兩個下劃線是私有變量(__private_value)。
筆者在此處有一個慚愧測試:我印象中Python類對象的屬性即便用雙下劃線開頭,也是可以直接訪問的。
寫本篇博客時在Python3.10.2和2.7.18中測試,發現自己的記憶是錯誤的:直接訪問雙下劃線變量,會拋出AttributeError異常,Python是有做私有變量的訪問限制的。
# tmp.py
class Xx(object):
def __init__(self):
self._x=9
self.__xx=10
def getXX(self):
return self.__xx
if __name__=='__main__':
x=Xx()
print(x._x)
print(x.getXX())
print(x.__xx)
# 在Python2中的測試(Python3當中是一樣的)
> py -2 tmp.py
9
10
Traceback (most recent call last):
File "tmp.py", line 30, in <module>
print(x.__xx)
AttributeError: 'Xx' object has no attribute '__xx'
筆者近一年的工作中,寫Python很少(這也是筆者去年放棄錄制視頻的原因之一),寫C++較多。當筆者寫本篇筆記時,好奇C++是否有相同的規范供借鑒,便搜一下C++的命名規范,其中排名最靠前的,是谷歌版本的C++代碼規范。
Google C++ Style Guide:
https://google.github.io/styleguide/cppguide.html
由此推理,Python肯定也有谷歌版本的編碼風格存在的。
Google Python Style Guide:
https://google.github.io/styleguide/pyguide.html
筆者看了下谷歌的命名規范,發現谷歌的程序員們并不推薦使用雙下劃線作為變量名稱,即便Python自己做了“訪問限制”。
由此,筆者再發現一個之前從未聽過的概念:name mangling。(待去搜一下,發現不知道的只是名詞“name mangling”,它的另一個名字是“名字裝飾”,筆者的理解是:編譯器為防止代碼中的變量重復,會在編譯時將這些變量加一些額外內容以做差異化。)
維基百科對name mangling的解釋:
https://zh.m.wikipedia.org/wiki/%E5%90%8D%E5%AD%97%E4%BF%AE%E9%A5%B0
筆者有對Python中的name mangling做一下測試:
class Demo:
any_name='any_name'
_any_name='_any_name'
__any_name='__any_name'
def __init__(self):
self.__any_x='__any_x'
self._any_y='_any_y'
class Demo2:
any_name='any_name'
_any_name='_any_name'
__any_name='__any_name'
# 測試一下多繼承(注:Python雖然支持多繼承,但并不推薦)
class Child(Demo, Demo2):
pass
if __name__=='__main__':
for n in dir(Demo):
if('any' in n):
print('cls ->', n)
print('-------------------------')
demo=Demo()
for n in dir(demo):
if('any' in n):
print('object ->', n)
print('Python并不能真正的做到成員私有:', demo._Demo__any_x)
print('-------------------------')
for n in dir(Child):
if('any' in n):
print('child ->', n)
測試結果如下:
> python3 tmp.py
# name mangling,加雙下劃線的變量名加上了類名前綴
cls -> _Demo__any_name
cls -> _any_name
cls -> any_name
-------------------------
object -> _Demo__any_name
object -> _Demo__any_x # 對象中的name mangling
object -> _any_name
object -> _any_y
object -> any_name
Python并不能真正的做到成員私有: __any_x
-------------------------
# 多繼承在這里,被區分出來
child -> _Demo2__any_name
child -> _Demo__any_name
# 但是重復的變量名,最后只剩一個(筆者未來的更新中會和大家一起討論這個問題)
child -> _any_name
child -> any_name
由以上測試,我理解了為什么谷歌文檔中說“沒有真正實現私有”:雙下劃線變量,改一下名字就可以訪問了。
類名應該用首字母大寫的駝峰模式:
class CapitalizedWord:
pass
模塊化常量使用全部大寫形式,單詞間用下劃線分開。
THIS_IS_A_CONST_VALUE=10
類實例的第一個參數應該用self開頭。
class MyTmpCls(object):
def __init__(self, val):
self._value=val
類方法的第一個參數,使用cls,表示這是類本身。
筆者曾經在某一次面試當中被要求手寫Python版本的單例,當時沒寫出來。筆者的借口是:過去單例的使用,都是從網上抄錄下來的。
筆者于此處抄一個簡單單例在此處,供大家參考也供筆者自己記錄。它來自于Stack Overflow:
class Singleton(object):
_instance=None
def __init__(self, *args, **kwargs):
print('this is the init func.', id(self))
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance=super().__new__(cls, *args, **kwargs)
return cls._instance
a=Singleton()
b=Singleton()
print(a is b) # True
筆者最喜歡的程序員社區網站是Stack Overflow,它專業、全面且干凈,筆者在遇到搞不定問題時,最先想到的便是到Stack Overflow去看看是否有前人遇見過相似問題。
絕大部分情況下,是有前人遇見過相似問題的。筆者整理本篇博客時,又去該網站上看了下前人對Python當中命名規范的討論,我找到一個算是很火的帖子,這帖子中主要關注的關鍵字有兩個:PEP8和谷歌標準。
由此,本篇博客以對谷歌標準的摘抄作為結束:
The Google Python Style Guide has the following convention:
module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_CONSTANT_NAME, global_var_name, instance_var_name, function_parameter_name, local_var_name.
谷歌推薦的Python編碼標準原文鏈接如下:
英文:https://google.github.io/styleguide/pyguide.html#316-naming
中文:https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules.html#id15
筆者寫本篇時,再次感覺到之前已經總結過的感受:每一項技能,如果深入進去,是都有許多內容可以輸出的。
本篇博客,對命名規范的討論肯定并不全面。不過筆者的觀點依然是:我們寫代碼,在讓機器正確執行前提下,是需要考慮可讀性的。
如何提升可讀性?保持統一的命名風格,會有幫助。
頭:header
內容:content/container
尾:footer
導航:nav
側欄:sidebar
欄目:column
頁面外圍控制整體布局寬度:wrapper
左右中:left right center
登錄條:loginbar
標志:logo
廣告:banner
頁面主體:main
熱點:hot
新聞:news
下載:download
子導航:subnav
菜單:menu
子菜單:submenu
搜索:search
友情鏈接:friendlink
頁腳:footer
版權:copyright
滾動:scroll
內容:content
標簽頁:tab
文章列表:list
提示信息:msg
小技巧:tips
欄目標題:title
加入:joinus
指南:guild
服務:service
注冊:regsiter
狀態:status
投票:vote
合作伙伴:partner
/ Footer /
內容區
/ End Footer /
(1)頁面結構
容器: container
頁頭:header
內容:content/container
頁面主體:main
頁尾:footer
導航:nav
側欄:sidebar
欄目:column
頁面外圍控制整體布局寬度:wrappe
左右中:left right center
(2)導航
導航:nav
主導航:mainbav
子導航:subnav
頂導航:topnav
邊導航:sidebar
左導航:leftsidebar
右導航:rightsidebar
菜單:menu
子菜單:submenu
標題: title
摘要: summary
(3)功能
標志:logo
廣告:banner
登陸:login
登錄條:loginbar
注冊:regsiter
搜索:search
功能區:shop
標題:title
加入:joinus
狀態:status
按鈕:btn
滾動:scroll
標簽頁:tab
文章列表:list
提示信息:msg
當前的: current
小技巧:tips
圖標: icon
注釋:note
指南:guild
服務:service
熱點:hot
新聞:news
下載:download
投票:vote
合作伙伴:partner
友情鏈接:link
版權:copyright
(1)顏色:使用顏色的名稱或者16進制代碼,如
.red { color: red; }
.f60 { color: #f60; }
.ff8600 { color: #ff8600; }
(2)字體大小,直接使用"font+字體大小"作為名稱,如
.font12px { font-size: 12px; }
.font9pt {font-size: 9pt; }
(3)對齊樣式,使用對齊目標的英文名稱,如
.left { float:left; }
.bottom { float:bottom; }
(4)標題欄樣式,使用"類別+功能"的方式命名,如
.barnews { }
.barproduct { }
主要的 master.css
模塊 module.css
基本共用 base.css
布局,版面 layout.css
主題 themes.css
專欄 columns.css
文字 font.css
表單 forms.css
補丁 mend.css
打印 print.css6、注意事項
(1)一律小寫;
(2)盡量用英文;
(3)不加中杠和下劃線;
(4)盡量不縮寫,除非一看就明白的單詞。
擊右上方紅色按鈕關注“web秀”,讓你真正秀起來
以往我們只是習慣于通過數組下標來訪問正則匹配到的分組,但分組達到4、5個時,標識起來就會非常麻煩。V8早已實現了正則命名分組提案,只是我們很少使用,本文將介紹JS的正則命名分組。
JavaScript 正則命名分組
假設要使用正則匹配一個日期的年月日,以往我們會這樣做:
const RE_DATE=/(\d{4})-(\d{2})-(\d{2})/; const matchObj=RE_DATE.exec('1999-12-31'); const year=matchObj[1]; // 1999 const month=matchObj[2]; // 12 const day=matchObj[3]; // 31
這里有幾個缺點:
所有這些問題,都可以通過正則命名分組來解決。
現在你只需要給分組里面一個命名標識即可:
(?<year>\d{4})
這里,我們用變量year標記了上一個捕獲組#1。 該名稱必須是合法的JavaScript標識符。 匹配后,您可以通過matchObj.groups.year訪問捕獲的字符串。
讓我們通過命名分組重寫前面的代碼:
const RE_DATE=/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; const matchObj=RE_DATE.exec('1999-12-31'); const year=matchObj.groups.year; // 1999 const month=matchObj.groups.month; // 12 const day=matchObj.groups.day; // 31
如果正則里面有了命名分組,那么匹配結果會多了一個groups 的屬性,這個屬性中包含了一切命名分組的捕獲結果。配合上解構大法使用又是一股清流:
const {groups: {day, year}}=RE_DATE.exec('1999-12-31'); console.log(year); // 1999 console.log(day); // 31
當然,即使你使用了命名分組,那么返回的結果還可以通過以往的數組下標方式訪問:
const year2=matchObj[1]; // 1999 const month2=matchObj[2]; // 12 const day2=matchObj[3]; // 31
命名分組具有以下優點:
反向引用命名分組\k<name> 看下面這個匹配重復單詞的例子:
const RE_TWICE=/^(?<word>[a-z]+)!\k<word>$/; RE_TWICE.test('abc!abc'); // true RE_TWICE.test('abc!ab'); // false
同時也可以使用以往的反向引用方式:
const RE_TWICE=/^(?<word>[a-z]+)!\1$/; RE_TWICE.test('abc!abc'); // true RE_TWICE.test('abc!ab'); // false
字符串方法replace()以兩種方式支持命名分組:
方式一
const RE_DATE=/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; console.log('1999-12-31'.replace(RE_DATE, '$<month>/$<day>/$<year>')); // 12/31/1999
如果replace不一定是直接返回新的拼接字符串,那么可以看看下面的辦法:
方式二
const RE_DATE=/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; console.log('1999-12-31'.replace( RE_DATE, (g, y, m, d, offset, input, {year, month, day})=> month+'/'+day+'/'+year)); // 12/31/1999
看看這replace的callback形參密密麻麻看得心慌慌,很多都用不上,那么我們看看更簡單的寫法:
console.log('1999-12-31'.replace(RE_DATE, (...args)=> { const {year, month, day}=args.slice(-1)[0]; return month+'/'+day+'/'+year; })); // 12/31/1999
這里配合上spread operator直取最后一個參數,再接上一個解構大法,結果又是一股清流。
如果可選的命名組不被匹配,則其屬性值被設置為undefined,但key是仍存在:
const RE_OPT_A=/^(?<as>a+)?$/; const matchObj=RE_OPT_A.exec(''); // We have a match: console.log(matchObj[0]===''); // true // Group <as> didn’t match anything: console.log(matchObj.groups.as===undefined); // true // But property as exists: console.log('as' in matchObj.groups); // true
分組名不能有重復項:
/(?<foo>a)(?<foo>b)/ // SyntaxError: Duplicate capture group name
反向引用一個不存在的分組名:
/\k<foo>/u // SyntaxError: Invalid named capture referenced /\k<foo>/.test("k<foo>") // true, 非 Unicode 下為了向后兼容,k 前面的 \ 會被丟棄
在 reaplce() 方法的替換字符串中引用一個不存在的分組:
"abc".replace(/(?<foo>.*)/, "$<bar>") // SyntaxError: Invalid replacement string "abc".replace(/(.*)/, "$<bar>") // "$<bar>",不包含命名分組時會向后兼容
Chrome60 已支持命名分組 通過babel插件處理兼容問題 babel-plugin-transform-modern-regexp
喜歡小編的點擊關注,了解更多知識!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。