Python编码中从容易出错的两个小问题中看其编码规范

 Python
 

最近在写Python的一些小程序,由于没有系统地学习过Python的语法及编码风格,在编码规范上栽了几个跟头。先看看我遇到的这两个问题,接着引出Python的编码规范。

缩进问题

缩进是Python中最重要的,也是最容易出错的细节,这个对于每个初学者都要牢记于心并养成习惯,我的问题出在,从一个文本文档拷贝一段代码到一个IDE的时候,出现了如下的两个错误。

IndentationError: unindent does not match any outer indentation level 
IndentationError:expected an indented block  

这两个问题就是提醒你要缩进,但是我明明缩进了,为什么还不行,原因就是拷贝的代码的缩进是用空格,缩进不统一(用肉眼完全看不出来),另外一个就是IDE它本身加了一些功能让你方便写代码,像自动缩进,换行等,拷贝的代码本身也拷贝了文件的格式,所以拷贝过来的代码IDE不能识别,不知道为什么不能识别,按理说删除空格换成Tab就应该可以了,但是不管怎么弄都不行,后来就只得重新编写代码才解决这个问题。

Python库import的导入顺序有讲究

库的导入有两种方式:
1)并排导入:

import module1,module2,module3.......  

2)顺序导入:

import module1  
import module2  
:  
import moduleN  

显然第二种看着要清爽,但是由于Python库众多,什么库应该先导入,就非常有讲究。我的第二个问题就是这个。先来看看一个小例子:

这上面导入的两个库的顺序不一样,完全出现不同的结果,可以看到,正确的版本是先导入第三方的库,在导入标准库,但是。。。但是Python的官方编码规范中要求的顺序是:

  • python 标准库模块
  • python 第三方模块
  • 应用程序自定义模块

这就让人很匪夷所思了,这里先mark一下,以后遇到在进一步补上,但是我决定以后的做法是,用谁,谁最后导入,这里可能存在着覆盖的问题。

好了,上面是我遇到的两个小问题,希望自己牢记,不要再犯同样的错误。关于Python的编码规范,更多的详见Google的官方的风格指南:
其中,我挑几点记录一下:

1、不要在行尾加分号
2、一行太长,不要加\换行,这和C++这些不同,Python用()支持直接换行,如:

foo = long_function_name(  
    var_one, var_two, var_three,  
    var_four)  

3、括号内不要有空格,如:

Yes: spam(ham[1], {eggs: 2}, [])
No: spam( ham[ 1 ], { eggs: 2 }, [ ] )  

4、一般’=’或比较符号(’<’’=’’>’)两边我们都会加空格,但这里特别注意一下:
当’=’用于指示关键字参数或默认参数值时, 不要在其两侧使用空格,如:

Yes: def complex(real, imag=0.0): return magic(r=real, i=imag)
No:  def complex(real, imag = 0.0): return magic(r = real, i = imag)

5、一般在C、C++程序中我们都会这样写,看着舒服极了:

foo       = 1000  # comment
long_name = 2     # comment that should not be aligned

dictionary = {
    "foo"      : 1,
    "long_name": 2,
}

但最好别这样写,这样写就行:

foo = 1000  # comment
long_name = 2  # comment that should not be aligned

dictionary = {
    "foo": 1,
    "long_name": 2,
}

6、大部分.py文件不必以#!作为文件的开始. 根据 PEP-394 , 程序的main文件应该以 #!/usr/bin/python2或者 #!/usr/bin/python3开始.

(#!)叫做Shebang(也叫Hashbang),一般在科学计算中,类Unix操作系统的程序载入器会分析Shebang后的内容,将这些内容作为解释器指令,并调用该指令, 并将载有Shebang的文件路径作为该解释器的参数. 例如, 以指令#!/bin/sh开头的文件在执行时会实际调用/bin/sh程序.)

(#!)先用于帮助内核找到Python解释器, 但是在导入模块时, 将会被忽略. 因此只有被直接执行的文件中才有必要加入#!.

7、使用文档字符串对模块、函数或方法进行注释,并在必要时增加行内注释,这一条决定着编写大型模块程序时,不会被搞得晕头转向,也不会让别人看自己程序时摸不清头脑。具体怎么做见下面这个模板即可。

def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
"""Fetches rows from a Bigtable.

Retrieves rows pertaining to the given keys from the Table instance
represented by big_table.  Silly things may happen if
other_silly_variable is not None.

Args:
    big_table: An open Bigtable Table instance.
    keys: A sequence of strings representing the key of each table row
        to fetch.
    other_silly_variable: Another optional variable, that has a much
        longer name than the other args, and which does nothing.

Returns:
    A dict mapping keys to the corresponding table row data
    fetched. Each row is represented as a tuple of strings. For
    example:

    {'Serak': ('Rigel VII', 'Preparer'),
     'Zim': ('Irk', 'Invader'),
     'Lrrr': ('Omicron Persei 8', 'Emperor')}

    If a key from the keys argument is missing from the dictionary,
    then that row was not found in the table.

Raises:
    IOError: An error occurred accessing the bigtable.Table object.
"""
pass

8、如果一个类不继承自其他类,就应该显示地从object继承,嵌套类也一样,继承自 object 是为了使属性(properties)正常工作, 并且这样可以保护你的代码, 使其不受Python 3000的一个特殊的潜在不兼容性影响. 这样做也定义了一些特殊的方法, 这些方法实现了对象的默认语义, 包括 __new__, __init__, __delattr__, __getattribute__, __setattr__, __hash__, __repr__, and __str__ .如:

class SampleClass(object):
     pass


 class OuterClass(object):

     class InnerClass(object):
         pass


 class ChildClass(ParentClass):
     """Explicitly inherits from another class already."""

9、命名,这个应该是比较重要,好的命名在代码编写过程中,或是在后期调试,维护的过程中都会给人特别舒服的感觉。
Tip:

module_name, package_name, ClassName, method_name, 
ExceptionName, function_name, GLOBAL_VAR_NAME, 
instance_var_name, function_parameter_name,
local_var_name  

应该避免的名称

  • 单字符名称, 除了计数器和迭代器.
  • 包/模块名中的连字符(-)
  • 双下划线开头并结尾的名称(Python保留, 例如__init__)

命名约定

  • 所谓”内部(Internal)”表示仅模块内可用, 或者, 在类内是保护或私有的.
  • 用单下划线(_)开头表示模块变量或函数是protected的(使用import * from时不会包含).
  • 用双下划线(__)开头的实例变量或方法表示类内私有.
  • 将相关的类和顶级函数放在同一个模块里. 不像Java, 没必要限制一个类一个模块.
  • 对类名使用大写字母开头的单词(如CapWords, 即Pascal风格), 但是模块名应该用小写加下划线的方式(如lower_with_under.py). 尽管已经有很多现存的模块使用类似于CapWords.py这样的命名, 但现在已经不鼓励这样做, 因为如果模块名碰巧和类名一致, 这会让人困扰.

Python 之父Guido推荐的规范

10、即使是一个打算被用作脚本的文件, 也应该是可导入的. 并且简单的导入不应该导致这个脚本的主功能(main functionality)被执行, 这是一种副作用. 主功能应该放在一个main()函数中.

在Python中, pydoc以及单元测试要求模块必须是可导入的. 你的代码应该在执行主程序前总是检查 if __name__ == ‘__main__‘ , 这样当模块被导入时主程序就不会被执行.如:

def main():
  ...

if __name__ == '__main__':
    main()