Skip to main content

命名系统

合法变量名​

首先什么是合法的变量名,所谓合法的变量名就是python的解释器能认识的变量名,在python中一个合法的变量名是由字母、下划线和数字组成的,并且第一个字符不可以是数字。

当然在 Python3 里面这个字母的定义其实是广义的,你可以写一个中文的变量名,你可以认为中文的这个字也算一种字母,但是一般来说我们不推荐使用中文变量名,因为有时候可能会由于一些编码的原因,踩进一些莫名其妙的坑,完全没有必要。

变量名的好坏​

合法的变量名显然给了我们非常大的操作空间,我们可以给变量起各种奇奇怪怪的名字,所以接下来的问题就是什么是一个好的变量名。

我们可以从内容和形式两个角度去判断一个变量的好坏,那从内容的角度一句话概括就是这个变量名传递了多少有效信息。比如说左边这三个变量 n、name 和 username 明显是 username 传递出的有效信息更多。当你的变量名包含的有效信息更多的时候,它就是一个非常好的变量名。

当然我们也要在信息量和这个变量名的长度上做取舍,不能说为了更多的信息我们把这个变量名无限增长,这二者之间的平衡更多的时候是和项目本身有关的,也更考经验。

写法​

从上一个例子我们知道,有的时候我们的一个变量名可能会包含若干个单词,当我们把这些单词全都挤着写在一起的时候,我们就看不清谁是谁,而在变量名中又不能包含空格,所以从形式上我们又有四种不同的变量名写法,这四种写法如下:

lower_underscore = 0
UPPER_UNDERSCORE = 1
CamelCase = 2
mixedCase = 3

你可以用小写字母跟下划线,大写字母跟下划线,或者你可以用 camel case 也就是通过首字母大写的方式来区别单词。

那 camel case 中文叫驼峰命名,它要分为大驼峰跟小驼峰,小驼峰有时候也叫 mixed case,它们的区别就是第一个字母是不是大写。

当然在程序比较复杂的时候也会出现 camel case 跟 under score 混用的情况,比如在 cpython 的原代码里,就出现了大量的这种使用方式。

命名规范​

讲到这里其实都是老生常谈的内容,几乎教学视频都是这么讲的,但很少人会说的是什么是被推荐的变量名,也就是所谓的 naming convention,命名规范。

那python的命名规范呢实际上是在 PEP 8 里面有比较详细的描述的,感兴趣的话,可以读一下全文。我会把这里面最常用的部分拿出来给大家介绍,首先为什么我们要命名规范,它背后的核心是 consistency,也就是一致性。

当你拥有一致性的时候,你就会减少一些无谓的思维开销,那从变量名的角度讲,就是变量名传递出了更多的信息量,比如说当我看到一个变量名的时候我可以迅速地分辨,它代表的是一个变量还是一个常量,或者说它是一个函数还是一个类。只有你拥有了这种一致性,你才有能力做出这样的判断。

所以说学习一个统一的命名方式,并不光是为了让自己的程序好看,它实际上是可以增加你的编程效率的。

在python代码里面,我们使用到的名字格式只有前面三种:小写下划线,大写下划线和大驼峰命名,我们不会用到小驼峰命名。

我们先说最简单的大写下划线,在python中,大写下划线只用来表示常量,当然我们知道在python中没有真正的常量,那常量的意思表达的是,这个值啊,你别动,我也不会动,所以当你要表示一个常量的时候,用大写下划线。同时在python的标准库里,当你看到大写下划线的时候,你也可以知道这个东西是个常量。

那camel case也只有在一个情况下会被使用到就是类的定义上,这就意味着,当你看到大驼峰的时候表示这就是一个类,而不是其他乱七八糟东西。

除了我们上面提到的常量和类名之外,剩下的所有的名字都是小写下划线,这里面包括了module, variable, function, method,那其中一个很重要的原因就是这些东西在python里面经常都会被看成一个变量,它们会被使用,会被传来传去,甚至还有很多共同的性质,所以它们的命名方式也是一致的。

import my_module

my_var = 0

def my_function():
pass

class MyClass:
def my_method(self):
pass

这些是我们聊到的python的基础命名习惯,那在python里关于命名,还有一个部分是需要聊的,就是有关下划线,确切的说是前置下划线,也就是所谓的 prefix。在python中,前置下划线,无论是在语义上还是在功能上都有可能对这个变量本身造成一定的影响。

单下划线 _​

我们首先来讲这个单下划线,就是一个下划线,什么都没有,它也是python里面的一个合法的变量名,那这个变量名一般来说被当做占位符,就是这块在语法上它应该有一个变量,但是这个变量我用不上。

比如我们下面的程序:

import random

for _ in range(10):
print(random.random())

我们想打印10个随机数,我们用这个 for _ in range(10),就可以重复10次,但是这个循环变量我们并不使用它,在这里我们就用一个单下划线来拿到这个循环变量。那相比于很多人更习惯使用的这个for i in range(10),可以看到使用下划线表达出了更多的信息量。就是明确的告诉看这段代码的人,这个变量我是没有用的。

弱私有 _var​

接下来我们要讲的就是一个前置下划线,也就是在变量名的最前面有一个下划线的情况,这种我们在python里面叫弱私有,就是它在语义上尝试表达的是 private 这个性质,也就是友好的告诉其他人,这个东西你不要用,它并不是一个给你开放的接口,这也是通过下划线表达出额外的信息。

实际上单下划线对程序运行是有影响的,我们假设你一个 module 里面有很多的 global variable,当你在做from module import *的时候,它是不会 import 单下划线开头的 global variable 的,但是在这个过程中它尊重了单下划线开头变量的语义,它没有把这个所谓私有变量给 import 进来。

为什么叫弱私有呢?就是如果你强行使用它,你还是使用得了的,看以下例子:

_data_cache: dict = {}


def calculate(x):
if x in _data_cache:
return _data_cache[x]
return x ** 2


class MyClass:
def _get_raw(self):
print("Oh no")

o = MyClass()
o._get_raw()

虽然我告诉你了 get raw 这个东西是私有的东西,你不要用,但是你要强行使用的话,没有一点问题,这是一个善意的提醒。

强私有 __var​

与弱私有相对应的,肯定就是强私有,对不对?在python的 class definition 里面,注意是 class definition 里,用两个下划线开头表示强私有,你是没有办法在这个 class 之外来访问这个强私有的内容的,比如说我们把刚才那个 get raw 再加一个下划线让它变成一个强私有的方法,那下面的这样的代码就没有办法运行了,它就会报错。

class MyClass:
def __get_raw(self):
print("Oh no")

o = MyClass()
o.__get_raw()

那对于 member variable 也是一样的,当你用双下划线开头的时候,它就没有办法直接访问到这个变量。

class MyClass:
def __init__(self):
self.__secret = 0

o = MyClass()
print(o.__secret)

但是大家注意一下python这个部分的实现叫做防君子不防小人,python实际上是在 class definition 里面对双下划线开头的变量进行了一个 name mangling,也就是它在里面偷偷的把这个双下划线开头的变量给改名了,那我们如果是print(o._MyClass__secret),那这个就成功打印出来了。

因为在python名字修饰的过程中它把这个__secret前面增加了一个_class名,这个机制更多的是防止你误用,它不是一个真正的编程意义上的保护机制,所以不要把它当一个安全特性用。

魔术方法 __dunder__​

说完两个下划线,就是四个下划线,前后各有两个下划线,它的学名叫做 dunder,是 double underscore 的缩写。这个名字中文语境的人应该很少用,这个名字是专门表示python中的魔术方法,也就是 magic method。

对于python的使用者来说,就是三个字:不要用,你自己定义的方法永远不要用到这个形式。

命名冲突​

最后再说一点有可能会被大家忽略的,就是我们千万不要在命名的时候覆盖了 builtin functions。比如说给一个list取名叫list,给一个dictionary取名叫dict,不要和内置函数起一样的名字,这样有可能会产生一些莫名其妙的bug。

说到这儿还有另外一件事想提醒大家,就是在给自己的文件起名的时候也不要跟内置的module重名,例如在学python turtle的时候把自己的文件名起名叫turtle.py,然后一导入turtle,所有东西都乱套了,所以说起名的时候,脑子里要想着名字冲突这件事。