2.2 Python的编码规范
Python语言有自己独特的编码规则,包括命名规则、代码书写规则等。本节将详细介绍Python中常用的规则,并解释这些规则的原理和由来。
2.2.1 命名规则
Python语言有一套自己的命名规则,用户也可以借鉴Java语言的命名规则形成自己命名的规则。命名规则并不是规定,只是一种习惯用法。下面介绍几个常见规范。
1.变量名、包名、模块名
变量名、包名、模块名通常采用小写,可使用下划线,示例如下。
01 # 变量、模块名的命名规则 02 # Filename: ruleModule.py 03 04 _rule="rule information"
【代码说明】
❑第2行代码声明模块的名称,模块名采用小写。也可以不指定模块名,以py后缀的文件就是一个模块。模块名就是文件名。
❑第4行代码定义了一个全局变量_rule。
2.类名、对象名
类名首字母采用大写,对象名采用小写。类的属性和方法名以对象作为前缀。类的私有变量、私有方法以两个下划线作为前缀。下面这段代码演示了类的定义和实例化的规范写法。
01 class Student:+# 类名大写
02+__name=""+# 私有实例变量前必须有两个下划线
03+def __init__(self, name):
04+self.__name=name+# self相当于Java中的this
05+def getName(self):+# 方法名首字母小写,其后每个单词的首字母大写
06+return self.__name
07
08 if __name__ =="__main__":
09+student=Student("borphi")+# 对象名小写
10+print(student.getName())
【代码说明】
❑第1行代码定义了一个名为Student的类,类名首字母大写。
❑第2行代码定义了一个私有的实例变量,变量名前有两个下划线。
❑第4行代码使用self前缀说明_ _name变量属于Student类。
❑第5行代码定义了一个公有的方法,方法名首字母小写,其后的单词Name首字母大写。函数的命名规则和方法名相同。
❑第9行代码创建了一个student对象,对象名小写。
说明 关于面向对象的知识会在第9章详细介绍,这里读者只需要知道类、对象、属性以及方法的书写方式即可。
3.函数名
函数名通常采用小写,并用下划线或单词首字母大写增加名称的可读性,导入的函数以模块名作前缀。下例中,为了演示导入函数前缀写法,使用了生成随机数的模块random。该模块有一个函数randrange()。该函数可以根据给定的数字范围生成随机数。randrange()声明如下所示:
randrange(start, stop[, step])
【代码说明】
❑参数start表示生成随机数所在范围的开始数字。
❑参数stop表示生成随机数所在范围的结束数字,但不包括数字stop。
❑参数step表示从start开始往后的步数。生成的随机数在[start,stop-1]的范围内,取值等于start+step。
例如:
randrange(1, 9, 2)
随机数的范围在1、3、5、7之间选取。下面这段代码演示了函数的规范写法,其中定义了一个compareNum(),该函数用于比较两个数字的大小,并返回对应的结果。
01 # 函数中的命名规则
02 import random
03
04 def compareNum(num1, num2):
05+if(num1> num2):
06+return 1
07+elif(num1 ==num2):
08+return 0
09+else:
10+return -1
11 num1=random.randrange(1, 9)
12 num2=random.randrange(1, 9)
13 print( "num1 =", num1)
14 print ("num2 =", num2)
15 print (compareNum(num1, num2))
【代码说明】
❑第2行代码导入了random模块。
❑第4行代码定义了一个函数compareNum(),参数num1、num2为待比较的两个变量。
❑第5行到第10行代码比较两个数的大小,返回不同的结果。
❑第11、12行代码调用random模块的randrange()函数,返回两个随机数。
❑第13、14行代码输出随机数,不同的机器、不同的执行时间得到的随机数均不相同。
❑第15行代码调用compareNum(),并把产生的两个随机数作为参数传入。
良好命名可以提高编程效率,可以使代码阅读者在不了解文档的情况下,也能理解代码的内容。下面以变量的命名为例说明如何定义有价值的名称。许多程序员对变量的命名带有随意性,如使用i、j、k等单个字母。代码阅读者并不知道这些变量的真实含义,需要阅读文档或仔细查看源代码才能了解其含义。下面是一个命名不规范的例子。
01 # 不规范的变量命名 02 sum=0 03 i=2000 04 j=1200 05 sum=i+12 * j
【代码说明】这段代码定义了一个求和变量sum,以及两个变量i、j。如果只看代码片段,并不知道运算的含义是什么,需要通读整个函数或功能模块才能理解此处表达式的含义。
下面是一个良好命名的例子。
01 # 规范的变量命名 02 sumPay=0 03 bonusOfYear=2000 04 monthPay=1200 05 sumPay=bonusOfYear+12 * monthPay
【代码说明】bonusOfYear表示年终奖金、monthPay表示月薪,因此sumPay表示全年的薪水。命名良好的变量可以节省阅读程序的时间,更快地理解程序的含义。
注意 变量的命名应尽可能地表达此变量的作用,尽量避免使用缩写,以至于任何人都能理解变量名的含义。不用担心变量名的长度,长的变量名往往能更清楚地表达意思。
以上讨论的命名方式同样适用于模块名、类名、方法名、属性名等。命名规则会带来很多益处。统一命名规则便于开发团队合作开发同一个项目;便于统一代码的风格,理解不同程序员编写的代码;命名规范的变量名使函数的内容更容易被理解;避免项目中随意命名变量的情况,促进程序员之间的交流。规则并不是绝对的,统一规则、表达清楚名称的含义才是制定规则的原因。
2.2.2 代码缩进与冒号
代码缩进是指通过在每行代码前输入空格或制表符的方式,表示每行代码之间的层次关系。任何编程语言都需要用代码缩进清晰程序的结构,采用代码缩进的编程风格有利于代码的阅读和理解。对于C、C++、Java等语言,代码缩进只是作为编程的一种良好习惯而延承下来。对于Python而言,代码缩进是一种语法,Python语言中没有采用花括号或begin…end…分隔代码块,而是使用冒号和代码缩进区分代码之间的层次。
使用IDE开发工具或EditPlus等编辑器书写代码时,编辑器会自动缩进代码、补齐冒号,提高编码效率。下面这段代码中的条件语句采用了代码缩进的语法。
01 x=1 02 if x ==1: 03+print( "x =", x)+# 代码缩进 04 else: 05+print( "x =", x)+# 代码缩进 06+x=x+1+# 代码缩进 07 print ("x =", x)
【代码说明】
❑第1行代码创建了变量x,并赋值为1。在赋值运算符的两侧各添加一个空格,这是一种良好的书写习惯,提高了程序的可读性。
❑第2行代码使用了条件语句if,判断x的值是否等于1。if表达式后输入了一个冒号,冒号后面的代码块需要缩进编写。本行代码与第1行代码处于同一个层次,直接从最左端书写代码。
❑第3行代码表示x的值等于1时输出的结果。当if条件成立时,程序才能执行到第3行,所以第3行代码位于第2行代码的下一个层次。在编码时,首先在最左端输入4个空格或制表键(不建议),然后再书写print语句。输出结果:
x=1
❑第4行代码的else关键字后是一段新的代码块。当x的值不等于1时,程序将执行第5、6行代码。
❑第5、6行代码采用缩进式的代码风格。
❑第7行代码输出结果:
x=1
Python对代码缩进要求非常严格。如果程序中不采用代码缩进的编码风格,将抛出一个IndentationError。下面这段代码是错误的编码方式。
01 x=0 02 if x ==1: 03 print( "x =", x) 04 else: 05+print( "x =", x)+# 代码缩进 06+x=x+1+# 代码缩进 07 print ("x =", x)
【代码说明】第3行没有缩进代码,python不能识别出代码的层次关系,python误认为if x ==1:语句后没有代码块。代码运行后输出如下错误:
IndentationError: expected an indented block
注意 如果缩进的代码前只有一个空格或几个制表符也是符合语法的,但是这种写法并不推荐。最佳的方式是编码前统一代码的书写规则,所有代码前的空格数保持一致,最好使用4个空格缩进。
每行代码缩进的情况不同,代码执行的结果也不同。例如,前文提到的hello.py,else子句包含的两条语句都采用了代码缩进的格式,这两条语句组成了一个代码块。如果把hello.py的代码修改为如下的内容,则变量x的值将有所变化。
01 x=1 02 if x ==1: 03+print( "x =", x)+# 代码缩进 04 else: 05+print( "x =", x)+# 代码缩进 06 x=x+1 07 print( "x =", x)
【代码说明】
❑第6行代码与第1行代码处于同一个层次,是主程序必须执行的代码,因此变量x的值为2。
❑第7行代码的输出结果如下。
x=2
注意 当程序出现问题时,程序员首先要检查代码的书写格式,是否因为代码缩进的问题导致了不期望的计算结果。
2.2.3 模块导入的规范
模块是类或函数的集合,用于处理一类问题。模块的导入和Java中包的导入的概念很相似,都使用import语句。在Python中,如果需要在程序中调用标准库或其他第三方库的类,需要先使用import或from…import…语句导入相关的模块。
1.import语句
下面这段代码使用import语句导入sys模块,并打印相关内容。
01 # 规范导入方式 02 import sys 03 04 print (sys.path) 05 print (sys.argv)
【代码说明】
❑第2行代码使用import语句导入了sys模块,sys模块是处理系统环境的函数的集合。
❑第4行代码输出Python环境下的查找路径的集合,Python默认情况下会查找sys.path返回的目录列表。列表是Python内置的数据结构,定义了一组相同意义的数据,通常用来作为参数或返回值。关于列表的知识请参考第4章的内容。本行代码的输出结果如下。
['D:\\developer\\python\\example\\02\\2.2\\2.2.3','C:\\WINDOWS\\system32\\python25.zip', 'D:\\developer\\python\\DLLs', 'D:\\developer\\python\\lib', 'D:\\developer\\python\\lib\\plat-win', 'D:\\developer\\python\\lib\\lib-tk','D:\\developer\\python', 'D:\\developer\\python\\lib\\site-packages','D:\\developer\\python\\lib\\site-packages\\win32', 'D:\\developer\\python\\lib\\site-packages\\win32\\lib', 'D:\\developer\\python\\lib\\site-packages\\Pythonwin', 'D:\\developer\\python\\lib\\site-packages\\wx-2.8-msw-unicode']
❑第5行代码中的sys.argv是存储输入参数的列表。默认情况下,argv自带的参数是文件名。输出结果如下。
['import.py']
2.from…import…语句
使用from…import…语句导入与使用import语句导入有所不同,区别是前者只导入模块中的一部分内容,并在当前的命名空间中创建导入对象的引用;而后者在当前程序的命名空间中创建导入模块的引用,从而可以使用“sys.path”的方式调用sys模块中的内容。
下面这段代码实现了与上面代码相同的功能,不同的是使用了from…import…语句导入指定内容。
01 #不规范导入方式 02 from sys import path 03 from sys import argv 04 05 print (path) 06 print (argv)
【代码说明】第5、6行代码直接调用path、argv列表的内容,没有模块名的限定,但这种写法不够规范。如果程序比较复杂,导入了很多模块,阅读程序的人并不了解path、argv来自哪个模块。而通过sys.path、sys.argv的写法可以清楚地知道path、argv来自sys模块。
2.2.4 使用空行分隔代码
函数之间或类的方法之间用空行分隔,表示一段新的代码的开始。类和函数入口之间也用一行空行分隔,突出函数入口的开始。下面这段代码创建了一个类A,类A中定义了两个方法funX()和funY()。
01 class A: 02+def funX(self): 03+print( "funY()") 04 05+def funY(self): 06+print ("funY()") 07 08 if __name__ =="__main__": 09+a=A() 10+a.funX() 11+a.funY()
【代码说明】
❑第4行代码插入了一个空行,便于程序员阅读代码,表示区分方法之间的间隔。
❑第7行代码也是一个空行。因为下面的if语句是主程序的入口,用于创建类A的对象,并调用其方法。
空行与代码缩进不同,空行并不是Python语法的一部分。书写时不插入空行,Python解释器运行也不会出错。但是空行的作用在于分隔了两段不同功能或含义的代码,便于日后代码的维护或重构。记住,空行也是程序代码的一部分。
2.2.5 正确的注释
注释是用于说明代码实现的功能、采用的算法、代码的编写者以及代码创建和修改的时间等信息。注释是代码的一部分,注释起到了对代码补充说明的作用。C、C++、Java均采用“//”或“/**/”作为注释的标记,Python的注释方式有所不同。如果只对一行代码注释,使用“#”加若干空格开始,后面是注释的内容。如果对一段代码进行注释,也使用“#”,段落之间以一个“#”行分隔。Python会忽略“#”行的内容,跳过“#”行执行后面的内容。下面的代码演示了Python的注释。
01 # note-show Python's note 02 # Copyright (C) 2008 bigmarten 03 # 04 # This program is free software. you can redistribute it and/or modify 05 # it under the terms of the GNU General Public License as published by 06 # the Free Software Foundation 07 # 08 ######################################################################## 09 # 10 # Version is 1.0 11 # 12 # Its contents are calculate payment 13 # 14 ######################################################################## 15 16 # 规范的变量命名 17 sumPay=0+# 年薪 18 bonusOfYear=2000+# 年终奖金 19 monthPay=1200+# 月薪 20 sumPay=bonusOfYear+12 * monthPay+# 年薪=年终奖金+12 * 月薪
【代码说明】
❑第1行代码对本文件进行摘要说明。
❑第2行代码声明了版权信息。
❑第3行代码是空行,说明下面将另起一段,注释其他的内容。
❑第4行到第6行代码说明程序的许可信息。
❑第8行到第14行代码说明程序的版本和实现的功能。
❑第16行开始的代码实现程序的功能,并在行末对每行代码进行单行注释。
Python还有一些特殊的注释,完成一些特别的功能,如中文注释、程序的跨平台。
(1)中文注释:如果需要在代码中使用中文注释,必须在Python文件的最前面加上如下注释说明。
# -*- coding: UTF-8 -*-
注意 Python3中默认的编码是Unicode,所以不需在每个Python文件中再加以上注释,但在Python2中若使用中文则必须加上。
(2)跨平台注释:如果需要使Python程序运行在*nix系统中,最好在Python文件的最前面加上如下注释说明。
#!/usr/bin/python
此外,注释也用于调试程序。由于文本编辑器不具备调试功能,因此,可以使用注释辅助调试。如果一个程序很长,可以把程序分成若干个部分编写。为了更好地调试目前正在编写的程序模块,可以将那些已经编译通过的部分注释掉;或者把多余代码注释掉,把主要精力集中在当前编写的逻辑上。
例如,编写一个比较两个数字大小的函数。
01 def compareNum(num1, num2): 02+if(num1> num2): 03+return str(num1)+"> "+str(num2) 04+elif(num1<num2): 05+return str(num1)+"="+str(num2) 06+elif(num1 ==num2): 07+return str(num1)+"="+str(num2) 08+else: 09+return ""
【代码说明】本段代码中的str()函数实现了数字类型到字符串类型的转换。
编译后显示如下内容:
---------- python ---------- 输出完成 (耗时: 1 秒)-正常终止
说明这个函数编译通过,至少在语法上没有任何错误。下面就可以编写这个函数的调用程序,证明这个程序的逻辑是否正确。在前面的代码段后面添加如下代码。
01 num1=2 02 num2=1 03 print (compareNum(num1, num2)) 04 num1=2 05 num2=2 06 print (compareNum(num1, num2)) 07 num1=1 08 num2=2 09 print (compareNum(num1, num2))
运行程序,发现第3行的输出结果有误。
2> 1 2=2 1=2
第1行和第2行的输出证明函数compareNum()中num1>num2和num1==num2的判断是正确的,于是想到程序可能是在num1<num2的条件判断的逻辑上出现了错误。为了证明这个观点,注释掉num1<num2的分支判断语句。注释后的compareNum()的代码如下。
01 def compareNum(num1, num2): 02+if(num1> num2): 03+return str(num1)+"> "+str(num2) 04+#elif(num1<num2): 05+# return str(num1)+"="+str(num2) 06+elif(num1 ==num2): 07+return str(num1)+"="+str(num2) 08+else: 09+return ""
此时,程序编译通过,证明逻辑错误就在num1<num2的分支语句中。仔细检查注释的语句,发现return语句的返回值表达式写错了,其中的“>”误写为“=”。compareNum()正确的写法如下所示。
01 def compareNum(num1, num2): 02+if(num1> num2): 03+return str(num1)+"> "+str(num2) 04+elif(num1<num2): 05+return str(num1)+"<"+str(num2) 06+elif(num1 ==num2): 07+return str(num1)+"="+str(num2) 08+else: 09+return ""
再次运行整个程序,输出结果正确显示。
2> 1 2=2 1<2
合理地使用注释可以检查程序中的错误。这段代码演示了注释问题语句的做法,并检查这些语句错误的过程。另一种用法是,注释正确语句,单独运行问题语句,检查语句错误。这两种用法可以根据实际情况分别应用,后者更适合于代码比较长的情况。注释对于程序非常重要,表2-1说明了注释的用法和作用。
表2-1 注释的用法
2.2.6 语句的分隔
分号是C、Java等语言中标识语句结束的标志。Python也支持分号,同样可以用分号作为一行语句的结束标识。但在Python中分号的作用已经不像在C、Java中那么重要了,在C、Java中分号是必需的;而Python中的分号可以省略,主要通过换行来识别语句的结束。例如,以下两行代码是等价的。
01 # 下面两条语句是等价的 02 print( "hello world!") 03 print( "hello world!");
【代码说明】
❑第1行代码的输出结果:
hello world!
❑第2行代码的输出结果:
hello world!
如果要在一行中书写多个语句,就必须使用分号分隔了,否则Python无法识别语句之间的间隔。
01 # 使用分号分隔语句 02 x=1; y=1 ; z=1
【代码说明】第2行代码中有3条赋值语句,语句之间需要用分号隔开。如果不隔开语句,则Python解释器不能正确解释,会提示语法错误。
SyntaxError: invalid syntax
注意 分号并不是Python推荐使用的符号,Python倾向于使用换行作为每条语句的分隔。简单直白是Python语法的特点,通常一行只写一条语句,这样便于阅读和理解程序。一行写多条语句的方式是不好的实践。
Python同样支持多行写一条语句,Python使用“\”作为换行符。在实践中,一条语句写在多行也是很常见的。例如,把SQL语句作为参数传递给函数,由于SQL非常长,因此需要换行书写,提高阅读的方便性。
01 # 字符串的换行 02 # 写法一 03 sql="select id,name \ 04 from dept \ 05 where name='A'" 06 print (sql) 07 # 写法二 08 sql="select id,name " \ 09+"from dept " \ 10+"where name='A'" 11 print (sql)
【代码说明】
❑写法一只使用了一对双引号,把SQL语句分为select、from、where 3部分分别书写。
❑第6行代码输出结果如下。
select id,name from dept where name='A'
❑写法二使用了3对双引号,select、from、where分别对应一对双引号。
❑第11行代码输出结果如下。
select id,name from dept where name='A'
第二种写法比第一种写法的可读性更强,可以使用空格和制表符对齐语句,使代码显得更工整。对于简短的语句不推荐使用换行的写法,这种写法只会造成阅读的复杂性。下面这段程序是不合理的换行写法。
01 # 一条语句写在多行 02 print (\ 03 "hello world!")
【代码说明】第2、3行代码是一个整体,调用print输出“hello world!”,这种情况不适合分行书写。