Python规范:提高可读性

PEP 8 规范

PEP 是 Python Enhancement Proposal 的缩写,翻译过来叫“Python 增强规范”。

缩进规范

  PEP 8 规范告诉我们,请选择四个空格的缩进,不要使用 Tab,更不要 Tab 和空格混着用。 第二个要注意的是,每行最大长度请限制在 79 个字符。

空行规范

  PEP 8 规定,全局的类和函数的上方需要空两个空行,而类的函数之间需要空一个空行。

空格规范

函数的参数列表中,调用函数的参数列表中会出现逗号,请注意逗号后要跟一个空格,这是英语的使用习惯,也能让每个参数独立阅读,更清晰。

  • 冒号后面也要跟一个空格。
  • 在#后、注释前加一个空格。
  • 操作符,例如+,-,*,/,&,|,=,==,!=,请在两边都保留空格。不过与此对应,括号内的两端并不需要空格。

换行规范

  控制每行的最大长度不超过 79 个字符,但是有时候,函数调用逻辑过长而不得不超过这个数字时按以下规范:

def solve1(this_is_the_first_parameter, this_is_the_second_parameter, this_is_the_third_parameter,
           this_is_the_forth_parameter, this_is_the_fifth_parameter, this_is_the_sixth_parameter):
    return (this_is_the_first_parameter + this_is_the_second_parameter + this_is_the_third_parameter +
            this_is_the_forth_parameter + this_is_the_fifth_parameter + this_is_the_sixth_parameter)


def solve2(this_is_the_first_parameter, this_is_the_second_parameter, this_is_the_third_parameter,
           this_is_the_forth_parameter, this_is_the_fifth_parameter, this_is_the_sixth_parameter):
    return this_is_the_first_parameter + this_is_the_second_parameter + this_is_the_third_parameter + \
           this_is_the_forth_parameter + this_is_the_fifth_parameter + this_is_the_sixth_parameter


(top_secret_func(param1=12345678, param2=12345678, param3=12345678, param4=12345678, param5=12345678).check()
    .launch_nuclear_missile().wait())


top_secret_func(param1=12345678, param2=12345678, param3=12345678, param4=12345678, param5=12345678).check() \
    .launch_nuclear_missile().wait()

  1.通过括号来将过长的运算进行封装.

  2.通过换行符来实现.

文档规范

  • import 尽量放在开头.
  • 不要使用 import 一次导入多个模块.
  • from module import func 这样的语句,请确保 func 在本文件中不会出现命名冲突。或者通过 from module import func as new_func 来进行重命名,从而避免冲突。

注释规范

行注释并不是很推荐的方式。

文档描述

docstring 的写法,它是用三个双引号开始、三个双引号结尾。我们首先用一句话简单说明这个函数做什么,然后跟一段话来详细解释;再往后是参数列表、参数格式、返回值格式。

class SpatialDropout2D(Dropout):
    """Spatial 2D version of Dropout.
    This version performs the same function as Dropout, however it drops
    entire 2D feature maps instead of individual elements. If adjacent pixels
    within feature maps are strongly correlated (as is normally the case in
    early convolution layers) then regular dropout will not regularize the
    activations and will otherwise just result in an effective learning rate
    decrease. In this case, SpatialDropout2D will help promote independence
    between feature maps and should be used instead.
    Arguments:
        rate: float between 0 and 1. Fraction of the input units to drop.
        data_format: 'channels_first' or 'channels_last'.
            In 'channels_first' mode, the channels dimension
            (the depth) is at index 1,
            in 'channels_last' mode is it at index 3.
            It defaults to the `image_data_format` value found in your
            Keras config file at `~/.keras/keras.json`.
            If you never set it, then it will be "channels_last".
    Input shape:
        4D tensor with shape:
        `(samples, channels, rows, cols)` if data_format='channels_first'
        or 4D tensor with shape:
        `(samples, rows, cols, channels)` if data_format='channels_last'.
    Output shape:
        Same as input
    References:
        - [Efficient Object Localization Using Convolutional
          Networks](https://arxiv.org/abs/1411.4280)
  """
    def __init__(self, rate, data_format=None, **kwargs):
        super(SpatialDropout2D, self).__init__(rate, **kwargs)
        if data_format is None:
            data_format = K.image_data_format()
        if data_format not in {'channels_last', 'channels_first'}:
            raise ValueError('data_format must be in '
                           '{"channels_last", "channels_first"}')
        self.data_format = data_format
        self.input_spec = InputSpec(ndim=4)

命名规范

变量使用小写,通过下划线串联起来,例如:data_format、input_spec、image_data_set。唯一可以使用单字符的地方是迭代,比如 for i in range(n) 这种,为了精简可以使用。如果是类的私有变量,请记得前面增加两个下划线。

  • 常量,最好的做法是全部大写,并通过下划线连接,例如:WAIT_TIME、SERVER_ADDRESS、PORT_NUMBER。
  • 函数名,同样也请使用小写的方式,通过下划线连接起来,例如:launch_nuclear_missile()、check_input_validation()。
  • 类名,则应该首字母大写,然后合并起来,例如:class SpatialDropout2D()、class FeatureSet()。

代码分解技巧

不写重复代码。

如:

if i_am_rich:
    money = 100
    send(money)
else:
    money = 10
    send(money)

都有send函数,可改为:

if i_am_rich:
    money = 100
else:
    money = 10
send(money)

代码嵌套过深:

def send(money):
    if is_server_dead:
        LOG('server dead')
        return
    else:
        if is_server_timed_out:
            LOG('server timed out')
            return
        else:
            result = get_result_from_server()
            if result == MONEY_IS_NOT_ENOUGH:
                LOG('you do not have enough money')
                return
            else:
                if result == TRANSACTION_SUCCEED:
                    LOG('OK')
                    return
                else:
                    LOG('something wrong')
                    return

  可改为:

def send(money):
    if is_server_dead:
        LOG('server dead')
        return

    if is_server_timed_out:
        LOG('server timed out')
        return

    result = get_result_from_server()

    if result == MONET_IS_NOT_ENOUGH:
        LOG('you do not have enough money')
        return

    if result == TRANSACTION_SUCCEED:
        LOG('OK')
        return

    LOG('something wrong')

  以一个简单的二分搜索来举例说明。给定一个非递减整数数组,和一个 target,要求找到数组中最小的一个数 x,可以满足 x*x > target。一旦不存在,则返回 -1。

  代码实现如果如下所示,那么可以再以一个函数只干一件事情的原则再优化下。

def solve(arr, target):
    l, r = 0, len(arr) - 1
    ret = -1
    while l <= r:
        m = (l + r) // 2
        if arr[m] * arr[m] > target:
            ret = m
            r = m - 1
        else:
            l = m + 1
    if ret == -1:
        return -1
    else:
        return arr[ret]


print(solve([1, 2, 3, 4, 5, 6], 8))
print(solve([1, 2, 3, 4, 5, 6], 9))
print(solve([1, 2, 3, 4, 5, 6], 0))
print(solve([1, 2, 3, 4, 5, 6], 40))

  优化如下:

def comp(x, target):
    return x * x > target


def binary_search(arr, target):
    l, r = 0, len(arr) - 1
    ret = -1
    while l <= r:
        m = (l + r) // 2
        if comp(arr[m], target):
            ret = m
            r = m - 1
        else:
            l = m + 1
    return ret


def solve(arr, target):
    id = binary_search(arr, target)

    if id != -1:
        return arr[id]
    return -1


print(solve([1, 2, 3, 4, 5, 6], 8))
print(solve([1, 2, 3, 4, 5, 6], 9))
print(solve([1, 2, 3, 4, 5, 6], 0))
print(solve([1, 2, 3, 4, 5, 6], 40))

  类中属性很多时可以抽出相同特性的单独作为类,如:

class Person:
    def __init__(self, name, sex, age, job_title, job_description, company_name):
        self.name = name
        self.sex = sex
        self.age = age
        self.job_title = job_title
        self.job_description = description
        self.company_name = company_name

  job_title , job_description , company_name 都与工作有关,表达是同一个意义实体,就可以抽出单独作为类:

class Person:
    def __init__(self, name, sex, age, job_title, job_description, company_name):
        self.name = name
        self.sex = sex
        self.age = age
        self.job = Job(job_title, job_description, company_name)

class Job:
    def __init__(self, job_title, job_description, company_name):
        
        self.job_title = job_title
        self.job_description = description
        self.company_name = company_name

你可能感兴趣的:(Python规范:提高可读性)