一种编程语言,都有以下概念:
都有许多需要程序员命名的地方。
不同的语言,命名规范各不相同。
对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小时内与您取得联系。