Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
一種編程語言,都有以下概念:
都有許多需要程序員命名的地方。
不同的語言,命名規(guī)范各不相同。
對Python來說,PEP8推薦的命名規(guī)范如下(此處基于Effective Python書中列舉的條目整理,PEP8標(biāo)準(zhǔn)中會更詳細些,筆者建議大家可以簡單過一遍PEP8文檔):
用小寫字母配下劃線,例如lowercase_underscore。
類對象的屬性(attributes,筆者會更習(xí)慣使用“成員變量”這個詞)和保護變量由變量名前方的下劃線數(shù)量決定,一個下劃線是保護變量(_protect_value),兩個下劃線是私有變量(__private_value)。
筆者在此處有一個慚愧測試:我印象中Python類對象的屬性即便用雙下劃線開頭,也是可以直接訪問的。
寫本篇博客時在Python3.10.2和2.7.18中測試,發(fā)現(xiàn)自己的記憶是錯誤的:直接訪問雙下劃線變量,會拋出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當(dāng)中是一樣的)
> 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++較多。當(dāng)筆者寫本篇筆記時,好奇C++是否有相同的規(guī)范供借鑒,便搜一下C++的命名規(guī)范,其中排名最靠前的,是谷歌版本的C++代碼規(guī)范。
Google C++ Style Guide:
https://google.github.io/styleguide/cppguide.html
由此推理,Python肯定也有谷歌版本的編碼風(fēng)格存在的。
Google Python Style Guide:
https://google.github.io/styleguide/pyguide.html
筆者看了下谷歌的命名規(guī)范,發(fā)現(xiàn)谷歌的程序員們并不推薦使用雙下劃線作為變量名稱,即便Python自己做了“訪問限制”。
由此,筆者再發(fā)現(xiàn)一個之前從未聽過的概念:name mangling。(待去搜一下,發(fā)現(xiàn)不知道的只是名詞“name mangling”,它的另一個名字是“名字裝飾”,筆者的理解是:編譯器為防止代碼中的變量重復(fù),會在編譯時將這些變量加一些額外內(nèi)容以做差異化。)
維基百科對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)
測試結(jié)果如下:
> 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
-------------------------
# 多繼承在這里,被區(qū)分出來
child -> _Demo2__any_name
child -> _Demo__any_name
# 但是重復(fù)的變量名,最后只剩一個(筆者未來的更新中會和大家一起討論這個問題)
child -> _any_name
child -> any_name
由以上測試,我理解了為什么谷歌文檔中說“沒有真正實現(xiàn)私有”:雙下劃線變量,改一下名字就可以訪問了。
類名應(yīng)該用首字母大寫的駝峰模式:
class CapitalizedWord:
pass
模塊化常量使用全部大寫形式,單詞間用下劃線分開。
THIS_IS_A_CONST_VALUE=10
類實例的第一個參數(shù)應(yīng)該用self開頭。
class MyTmpCls(object):
def __init__(self, val):
self._value=val
類方法的第一個參數(shù),使用cls,表示這是類本身。
筆者曾經(jīng)在某一次面試當(dāng)中被要求手寫Python版本的單例,當(dāng)時沒寫出來。筆者的借口是:過去單例的使用,都是從網(wǎng)上抄錄下來的。
筆者于此處抄一個簡單單例在此處,供大家參考也供筆者自己記錄。它來自于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
筆者最喜歡的程序員社區(qū)網(wǎng)站是Stack Overflow,它專業(yè)、全面且干凈,筆者在遇到搞不定問題時,最先想到的便是到Stack Overflow去看看是否有前人遇見過相似問題。
絕大部分情況下,是有前人遇見過相似問題的。筆者整理本篇博客時,又去該網(wǎng)站上看了下前人對Python當(dāng)中命名規(guī)范的討論,我找到一個算是很火的帖子,這帖子中主要關(guān)注的關(guān)鍵字有兩個:PEP8和谷歌標(biāo)準(zhǔn)。
由此,本篇博客以對谷歌標(biāo)準(zhǔn)的摘抄作為結(jié)束:
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編碼標(biāo)準(zhǔn)原文鏈接如下:
英文: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
筆者寫本篇時,再次感覺到之前已經(jīng)總結(jié)過的感受:每一項技能,如果深入進去,是都有許多內(nèi)容可以輸出的。
本篇博客,對命名規(guī)范的討論肯定并不全面。不過筆者的觀點依然是:我們寫代碼,在讓機器正確執(zhí)行前提下,是需要考慮可讀性的。
如何提升可讀性?保持統(tǒng)一的命名風(fēng)格,會有幫助。
頭:header
內(nèi)容:content/container
尾:footer
導(dǎo)航:nav
側(cè)欄:sidebar
欄目:column
頁面外圍控制整體布局寬度:wrapper
左右中:left right center
登錄條:loginbar
標(biāo)志:logo
廣告:banner
頁面主體:main
熱點:hot
新聞:news
下載:download
子導(dǎo)航:subnav
菜單:menu
子菜單:submenu
搜索:search
友情鏈接:friendlink
頁腳:footer
版權(quán):copyright
滾動:scroll
內(nèi)容:content
標(biāo)簽頁:tab
文章列表:list
提示信息:msg
小技巧:tips
欄目標(biāo)題:title
加入:joinus
指南:guild
服務(wù):service
注冊:regsiter
狀態(tài):status
投票:vote
合作伙伴:partner
/ Footer /
內(nèi)容區(qū)
/ End Footer /
(1)頁面結(jié)構(gòu)
容器: container
頁頭:header
內(nèi)容:content/container
頁面主體:main
頁尾:footer
導(dǎo)航:nav
側(cè)欄:sidebar
欄目:column
頁面外圍控制整體布局寬度:wrappe
左右中:left right center
(2)導(dǎo)航
導(dǎo)航:nav
主導(dǎo)航:mainbav
子導(dǎo)航:subnav
頂導(dǎo)航:topnav
邊導(dǎo)航:sidebar
左導(dǎo)航:leftsidebar
右導(dǎo)航:rightsidebar
菜單:menu
子菜單:submenu
標(biāo)題: title
摘要: summary
(3)功能
標(biāo)志:logo
廣告:banner
登陸:login
登錄條:loginbar
注冊:regsiter
搜索:search
功能區(qū):shop
標(biāo)題:title
加入:joinus
狀態(tài):status
按鈕:btn
滾動:scroll
標(biāo)簽頁:tab
文章列表:list
提示信息:msg
當(dāng)前的: current
小技巧:tips
圖標(biāo): icon
注釋:note
指南:guild
服務(wù):service
熱點:hot
新聞:news
下載:download
投票:vote
合作伙伴:partner
友情鏈接:link
版權(quán):copyright
(1)顏色:使用顏色的名稱或者16進制代碼,如
.red { color: red; }
.f60 { color: #f60; }
.ff8600 { color: #ff8600; }
(2)字體大小,直接使用"font+字體大小"作為名稱,如
.font12px { font-size: 12px; }
.font9pt {font-size: 9pt; }
(3)對齊樣式,使用對齊目標(biāo)的英文名稱,如
.left { float:left; }
.bottom { float:bottom; }
(4)標(biāo)題欄樣式,使用"類別+功能"的方式命名,如
.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)盡量不縮寫,除非一看就明白的單詞。
擊右上方紅色按鈕關(guān)注“web秀”,讓你真正秀起來
以往我們只是習(xí)慣于通過數(shù)組下標(biāo)來訪問正則匹配到的分組,但分組達到4、5個時,標(biāo)識起來就會非常麻煩。V8早已實現(xiàn)了正則命名分組提案,只是我們很少使用,本文將介紹JS的正則命名分組。
JavaScript 正則命名分組
假設(shè)要使用正則匹配一個日期的年月日,以往我們會這樣做:
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
這里有幾個缺點:
所有這些問題,都可以通過正則命名分組來解決。
現(xiàn)在你只需要給分組里面一個命名標(biāo)識即可:
(?<year>\d{4})
這里,我們用變量year標(biāo)記了上一個捕獲組#1。 該名稱必須是合法的JavaScript標(biāo)識符。 匹配后,您可以通過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
如果正則里面有了命名分組,那么匹配結(jié)果會多了一個groups 的屬性,這個屬性中包含了一切命名分組的捕獲結(jié)果。配合上解構(gòu)大法使用又是一股清流:
const {groups: {day, year}}=RE_DATE.exec('1999-12-31'); console.log(year); // 1999 console.log(day); // 31
當(dāng)然,即使你使用了命名分組,那么返回的結(jié)果還可以通過以往的數(shù)組下標(biāo)方式訪問:
const year2=matchObj[1]; // 1999 const month2=matchObj[2]; // 12 const day2=matchObj[3]; // 31
命名分組具有以下優(yōu)點:
反向引用命名分組\k<name> 看下面這個匹配重復(fù)單詞的例子:
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直取最后一個參數(shù),再接上一個解構(gòu)大法,結(jié)果又是一股清流。
如果可選的命名組不被匹配,則其屬性值被設(shè)置為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
分組名不能有重復(fù)項:
/(?<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
喜歡小編的點擊關(guān)注,了解更多知識!
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。