每一种编程语言,都有以下概念:
- 变量;
- 函数或者方法;
- 类;
- 模块;
都有许多需要程序员命名的地方。
不同的语言,命名规范各不相同。
对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:
由此推理,Python肯定也有谷歌版本的编码风格存在的。
Google Python Style Guide:
笔者看了下谷歌的命名规范,发现谷歌的程序员们并不推荐使用双下划线作为变量名称,即便Python自己做了“访问限制”。
由此,笔者再发现一个之前从未听过的概念:name mangling
。(待去搜一下,发现不知道的只是名词“name mangling”,它的另一个名字是“名字装饰”,笔者的理解是:编译器为防止代码中的变量重复,会在编译时将这些变量加一些额外内容以做差异化。)
维基百科对name mangling的解释:
笔者有对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编码标准原文链接如下:
笔者写本篇时,再次感觉到之前已经总结过的感受:每一项技能,如果深入进去,是都有许多内容可以输出的。
本篇博客,对命名规范的讨论肯定并不全面。不过笔者的观点依然是:我们写代码,在让机器正确执行前提下,是需要考虑可读性的。
如何提升可读性?保持统一的命名风格,会有帮助。