PEP8标准(二),命名规范

721 阅读5分钟

每一种编程语言,都有以下概念:

  • 变量;
  • 函数或者方法;
  • 类;
  • 模块;

都有许多需要程序员命名的地方。

不同的语言,命名规范各不相同。

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

google.github.io/styleguide/…

由此推理,Python肯定也有谷歌版本的编码风格存在的。

Google Python Style Guide:

google.github.io/styleguide/…

笔者看了下谷歌的命名规范,发现谷歌的程序员们并不推荐使用双下划线作为变量名称,即便Python自己做了“访问限制”。

由此,笔者再发现一个之前从未听过的概念:name mangling。(待去搜一下,发现不知道的只是名词“name mangling”,它的另一个名字是“名字装饰”,笔者的理解是:编译器为防止代码中的变量重复,会在编译时将这些变量加一些额外内容以做差异化。)

维基百科对name mangling的解释:

zh.m.wikipedia.org/wiki/%E5%90…

笔者有对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编码标准原文链接如下:

英文:google.github.io/styleguide/…

中文:zh-google-styleguide.readthedocs.io/en/latest/g…

笔者写本篇时,再次感觉到之前已经总结过的感受:每一项技能,如果深入进去,是都有许多内容可以输出的。

本篇博客,对命名规范的讨论肯定并不全面。不过笔者的观点依然是:我们写代码,在让机器正确执行前提下,是需要考虑可读性的。

如何提升可读性?保持统一的命名风格,会有帮助。