整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          PEP8(二),命名風格

          PEP8(二),命名風格

          一種編程語言,都有以下概念:

          • 變量;
          • 函數或者方法;
          • 類;
          • 模塊;

          都有許多需要程序員命名的地方。

          不同的語言,命名規范各不相同。

          對Python來說,PEP8推薦的命名規范如下(此處基于Effective Python書中列舉的條目整理,PEP8標準中會更詳細些,筆者建議大家可以簡單過一遍PEP8文檔):

          1、函數名、變量名、屬性名

          用小寫字母配下劃線,例如lowercase_underscore

          2、類對象的屬性

          類對象的屬性(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
          

          由以上測試,我理解了為什么谷歌文檔中說“沒有真正實現私有”:雙下劃線變量,改一下名字就可以訪問了。

          3、類名

          類名應該用首字母大寫的駝峰模式:

          class CapitalizedWord:
              pass
          

          4、模塊化常量

          模塊化常量使用全部大寫形式,單詞間用下劃線分開。

          THIS_IS_A_CONST_VALUE=10
          

          5、類實例函數

          類實例的第一個參數應該用self開頭。

          class MyTmpCls(object):
              def __init__(self, val):
                  self._value=val
          

          6、類方法

          類方法的第一個參數,使用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

          筆者寫本篇時,再次感覺到之前已經總結過的感受:每一項技能,如果深入進去,是都有許多內容可以輸出的。

          本篇博客,對命名規范的討論肯定并不全面。不過筆者的觀點依然是:我們寫代碼,在讓機器正確執行前提下,是需要考慮可讀性的。

          如何提升可讀性?保持統一的命名風格,會有幫助。

          SS標準化設計命名:

          1、類class的命名規范示例

          頭: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

          2、注釋的寫法  

          / Footer /  

          內容區  

          / End Footer /

          3、id的命名規范示例

          (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

          4、類class的書寫規范示例

            (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 { }

          5、CSS文件命名示例

            主要的 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
          

          命名分組具有以下優點:

          • 找到分組的“ID”更容易。
          • 匹配的代碼變得自描述性,因為分組的ID描述了捕獲的內容。
          • 如果更改分組的順序,則不必更改匹配的代碼。
          • 分組的名稱也使正則表達式更易于理解,因為您可以直接看到每個組的用途。

          反向引用

          反向引用命名分組\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( )

          字符串方法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

          公告

          喜歡小編的點擊關注,了解更多知識!


          主站蜘蛛池模板: 亚州日本乱码一区二区三区| 国产精品va一区二区三区| 一本AV高清一区二区三区| 久久99国产一区二区三区| 中文字幕在线观看一区| 日韩人妻不卡一区二区三区| 日韩精品无码一区二区中文字幕| 亚洲AV成人精品日韩一区 | 亚洲熟妇无码一区二区三区导航| 另类国产精品一区二区| 香蕉一区二区三区观| 国产一区二区成人| 日韩av片无码一区二区三区不卡| 久久精品国产一区| 无码中文字幕乱码一区 | 精品视频无码一区二区三区| 亚洲男人的天堂一区二区| 色狠狠AV一区二区三区| 亚洲国产精品一区| 久久综合一区二区无码| 国产精品制服丝袜一区| 亲子乱av一区二区三区| 亚洲国产精品一区二区九九| 国模无码视频一区| 中文精品一区二区三区四区| 日韩电影一区二区三区| 久久精品视频一区二区三区| 国产AV一区二区三区传媒| 久久久久一区二区三区| 亚洲AV美女一区二区三区| 中文字幕一区二区三区四区 | 国产午夜精品一区二区三区极品| 日本一区免费电影| 久久无码AV一区二区三区| 亚洲一区中文字幕在线观看| 国产精品一区12p| 亚洲人AV永久一区二区三区久久 | 国产伦精品一区二区三区女| 鲁丝丝国产一区二区| 亚洲色欲一区二区三区在线观看| 色久综合网精品一区二区|