代码的坏味道
胡乐秋 2020-11-30 11:44:17 new new

代码的坏味道

  1. 版本
  2. 模型与视图

    1. view_welcome.py
    2. model_welcome.py
  3. 函数缺省私有化

  4. 消除全局变量
  5. 显示依赖
  6. 模型不允许使用请求对象
  7. 模型函数返回值
  8. 模型R资源
  9. SQL语句
  10. SQL占位符
  11. 单值容器加逗号
  12. 有序字典初始化
  13. and条件序列
  14. or条件序列
  15. 变量初始化多行合并
  16. 字典设置缺省值
  17. 使用map剔除重复
  18. 使用map多参数剔除重复
  19. 使用zip简化代码
  20. zip的逆
  21. 使用dict.get默认值简化代码
  22. 使用dict.update简化代码
  23. 从列表取值

版本

版本 日期 作者 联系 备注
0.1 2020-11-30 胡乐秋 66536382@qq.com 首次生成

返回


  1. 功能是对客户和老板负责; 代码是对自己和同事负责。
  2. 写得越少, 写得越好。
  3. 最大的谎言: 先完成功能,有时间再整理代码。于是回答这话的人,永远都没时间了。
  4. 不一定需要知道什么是美,但一定要知道什么是丑。丑的少了,自然就美了。
  5. 改变不了外表的容颜,但总可以改变代码的容颜。
  6. 重复的代码就像重复的美人痣。

返回


函数缺省私有化

  1. 总是假设函数是模块私有的,除非是接口函数.
  2. 私有变为公有容易, 公有变私有难, 所谓易放难收。
  3. 私有化以下划线('_')开头

bad

def do_anything() -> tuple:
    ...

good

def _do_anything() -> tuple:
    ...

返回


消除全局变量

bad

DEVICE_TRAITS_DICT = None

def _get_device_traits_dict() -> dict:
    """得到设备特征字典

    Returns:
        返回{{device_id}: {device_traits_dict}}

    """
    global DEVICE_TRAITS_DICT
    if not DEVICE_TRAITS_DICT:
        DEVICE_TRAITS_DICT = {}
    return DEVICE_TRAITS_DICT

good

def _get_device_traits_dict() -> dict:
    """得到设备特征字典

    Returns:
        返回{{device_id}: {device_traits_dict}}

    """
    func, tag_name = (_get_device_traits_dict, '__cache')
    if not hasattr(func, tag_name):
        setattr(func, tag_name, {})
    return getattr(func, tag_name)

返回


显示依赖

显示总比隐式好

bad

from common.utils import *  #pylint:disable=wildcard-import, unused-wildcard-import

good

from common.utils import (invoke, dispatch)

返回


模型与视图

  1. 视图仅负责与用户交互:
  1. 解析用户输入请求, 获取用户输入参数;
  2. 根据用户输入参数调用模型, 获取处理结果;
  3. 渲染模型处理结果, 返回给用户。
  1. 模型仅负责与数据库交互:
  1. 检查视图输入参数;
  2. 根据参数查询、修改数据库;
  3. 返回处理结果
  1. 视图不能包含数据库交互: 不能执行SQL, 查询或修改数据库
  2. 模型不能包含用户交互:不能使用"request"对象

返回


view_welcome.py

from . import model_welcome as model

@get('/welcome')
@view('/welcome.html')
def _() -> dict:
    """请求欢迎页面

    Returns:
        返回欢迎页面

    """
    # 1. 解析用户输入请求, 获取用户输入参数
    user_name_tag, error_tag = ('user_name', 'error')
    user_name = request_query_get(user_name_tag)
    if not user_name:
        return {error_tag: '未指定用户名("{}")参数.'.format(user_name_tag)}
    # 2. 根据用户输入参数调用模型, 获取处理结果
    success, result_or_error = model.is_valid_user(user_name)
    # 3. 渲染模型处理结果, 返回给用户
    if not success:
        return {error_tag: result_or_error)
    return {user_name_tag: user_name_tag}

返回


model_welcome.py

from common.dbhelper import db_search_record_count

def is_valid_user(user_name: str) -> tuple:
    """是否为有效的用户名

    Args:
        user_name: 用户名称

    Returns:
        返回(True, {是否为有效的的用户名: bool})或(False, {error_message})

    """
    # 1. 检查视图输入参数
    if not user_name:
        return (False, '指定用户名参数不能为空.')
    # 2.根据参数查询、修改数据库
    success, result_or_error = db_search_record_count(...)
    # 3. 返回处理结果
    if not success:
        return (success, result_or_error)
    record_count = result_or_error
    return (True, record_count > 0)

返回


模型不允许使用请求对象

模型不能使用用户请求对象(request).

bad

model_station.py

def get_user_station_list(request):
        ...
        filter_dict = request_forms_get_record_dict(request)
        ...

view_station.py

from . import model_station as model

@post('/station_statistcs')
def _() -> str:
    ...
    success, result_or_error = model.get_user_station_list(request)
    ...

good

model_station.py

def get_user_station_list(filter_dict: dict):
        ...

view_station.py

from . import model_station as model

@post('/station_statistcs')
def _() -> str:
    ...
    filter_dict = request_forms_get_record_dict(request)
    success, result_or_error = model.get_user_station_list(filter_dict)
    ...

返回


模型函数返回值

  1. 模型函数推荐总是返回元组: (True, {期望结果})或(False, {error_message})
  2. 除非永不失败, 才可直接返回期望值。

返回


模型R资源

在模型中,推荐定义"R"字符串类。以消除代码中的字符串魔术常量.体现"大范围分布,局部集中"的设计原则.

bad

def _do_something_1(record_dict: dict) -> tuple:
    ...
    record_id, name = map(record_dict.get, ('id', 'name'))
    ...

def _do_something_2(record_dict: dict) -> tuple:
    ...
    record_id = record_dict.get('id')
    ...

good

class R:
    """本模块资源"""
    ID_TAG, NAME_TAG = ('id',' name')

def _do_something_1(record_dict: dict) -> tuple:
    ...
    record_id, name = map(record_dict.get, (R.ID_TAG, R.NAME_TAG))
    ...

def _do_something_2(record_dict: dict) -> tuple:
    ...
    record_id = record_dict.get(R.ID_TAG)
    ...

返回


SQL语句

  1. 复杂SQL使用双引号(")多行, 保留单引号(')给SQL语句字符串使用
  2. SQL关键字大写, 其它均为小写
  3. 从句(SELECT, FROM, WHERE, ...)对齐
  4. 嵌套从句缩进对齐

bad

sql = """SELECT a.concrete_station_sn,
                    c.name AS contract_name,
                    d.concrete_name AS concrete_name,
                    count(*) AS total
             FROM tbl_product_info a JOIN tbl_lab b ON a.concrete_station_sn = b.sn
             JOIN tbl_party c ON b.contract_sn = c.sn
             JOIN tbl_lab d ON a.concrete_station_sn = d.sn
             WHERE a.output_datetime like %s {}
             group by concrete_station_sn,c.name,d.concrete_name
             order by concrete_station_sn,c.name,d.concrete_name"""

good

sql = """SELECT tbl_person.*,
                tbl_party.name AS party_name,
                cost_statistics.total_cost AS total_cost
         FROM tbl_person
         LEFT JOIN tbl_party ON tbl_party.sn = tbl_person.party_sn
         LEFT JOIN (SELECT account AS account,
                           SUM(cost) AS total_cost
                    FROM tbl_cost
                    GROUP BY account) AS cost_statistics
                    ON cost_statistics.account = tbl_person.account
         WHERE ...
         GROUP BY ...
         ORDER BY ...
         LIMIT ...
         OFFSET ..."""

返回


SQL占位符

构建动态SQL语句时, 通常需要根据查询参数的数量生成动态占位符.

bad

    sn_list = [...]
    placeholders = ('%s,' * len(sn_list))[:-1]
    sql = """SELECT *
             FROM tbl_person
             WHERE sn IN ({})""".format(placeholders)

good

    sn_list = [...]
    placeholders = ', '.join(['%s' for _ in sn_list])
    sql = """SELECT *
             FROM tbl_person
             WHERE sn IN ({})""".format(placeholders)

good: more pythonic

import itertools

    sn_list = [...]
    placeholders = ','.join(itertools.repeat('%s', len(sn_list)))
    sql = """SELECT *
             FROM tbl_person
             WHERE sn IN ({})""".format(placeholders)

返回


单值容器加逗号

对于仅有一项的容器,增加一个逗号, 表明这是有意行为。

bad

    sn_list = ['zs']
    sn_tuple = ('zs')
    person_dict_list = [{'name': 'zs', 'age': 18}]

good

    sn_list = ['zs',]
    sn_tuple = ('zs',)
    person_dict_list = [{'name': 'zs', 'age': 18},]

返回


有序字典初始化

有序字典可以使用元组列表初始化

bad

from collections import OrderedDict

    field_id_list = ('sn', 'name', 'age', ...)
    record_list = [('zs', '张三', 18), ...]
    sn_index = field_id_list.index('sn')
    result_record_dict = OrderedDict()  # {'编号':[记录列表],...}
    for record in record_list:
        sn_ = record[sn_index]
        result_record_dict[sn_] = [value for value in record]

good

from collections import OrderedDict

    field_id_list = ('sn', 'name', 'age', ...)
    record_list = [('zs', '张三', 18), ...]
    sn_index = field_id_list.index('concrete_station_sn')
    result_record_dict = OrderedDict([(record[sn_index], list(record)) for record in record_list])  # {'编号':[记录列表],...}

返回


and条件序列

如果出现两个以上的and条件判断, 替换使用"all": 同样支持短路测试.

bad

    if record_list and (len(record_list) == 1) and (record_list[0][0] == '*'):
        pass

good

    if all((record_list, len(record_list) == 1, record_list[0][0] == '*')):
        pass

返回


or条件序列

如果出现两个以上的and条件判断, 替换使用"all": 同样支持短路测试.

bad

    if condition_1 or condition_2 or condition_3:
        pass

good

    if any((condition_1, condition_2, condition_3)):
        pass

返回


变量初始化多行合并

bad

    filter_list = []
    sql_list = []
    value_list = []
    ...

good

    filter_list, sql_list, value_list = ([], [], [])
    ...

返回


字典设置缺省值

使用字典对象的"setdefault", 简化并使代码"more pythonic".

bad

def add_friend(person_dict: dict, friend_name: str) -> None:
    ...
    friends_tag = 'friends'
    if friends_tag not in person_dict:
        person_dict[friends_tag] = [friend_name, ]
    else:
        person_dict[friends_tag].append(friend_name)

good

    friends_tag = 'friends'
    person_dict.setdefault('friends', []).append(friend_name)

返回


使用map剔除重复

bad

    ...
    sql_list.append('SUM(a.practice_cement) AS practice_cement,')
    sql_list.append('SUM(a.practice_water) AS practice_water,')
    sql_list.append('SUM(a.practice_sand) AS practice_sand,')
    sql_list.append('SUM(a.practice_fine_stone) AS practice_fine_stone,')
    sql_list.append('SUM(a.practice_middle_stone) AS practice_middle_stone,')
    sql_list.append('SUM(a.practice_coarse_stone) AS practice_coarse_stone,')
    sql_list.append('SUM(a.practice_admixture_material) AS practice_admixture_material,')
    sql_list.append('SUM(a.practice_additive) AS practice_additive')
    sql_list.append('FROM tbl_product_info a')
    sql_list.append('LEFT JOIN tbl_lab b ON a.concrete_station_sn = b.sn')
    sql_list.append("LEFT JOIN tbl_party c on b.contract_sn = c.sn")
    ...

good

    list(map(sql_list.append, ('SUM(a.practice_cement) AS practice_cement,',
                               'SUM(a.practice_water) AS practice_water,',
                               'SUM(a.practice_sand) AS practice_sand,',
                               'SUM(a.practice_fine_stone) AS practice_fine_stone,',
                               'SUM(a.practice_middle_stone) AS practice_middle_stone,',
                               'SUM(a.practice_coarse_stone) AS practice_coarse_stone,',
                               'SUM(a.practice_admixture_material) AS practice_admixture_material,'
                               'SUM(a.practice_additive) AS practice_additive',
                               'FROM tbl_product_info a',
                               'LEFT JOIN tbl_lab b ON a.concrete_station_sn = b.sn',
                               "LEFT JOIN tbl_party c on b.contract_sn = c.sn")))

返回


使用map多参数剔除重复

bad

    ...
    for tag in ('cement', 'water', 'sand', 'fine_stone', 'middle_stone', 'coarse_stone', 'admixture_material', 'additive', 'water_cement_ratio', 'water_glue_ratio', 'mix_duration'):
        practice_tag = 'practice_%s' % tag
        construct_tag = 'construct_%s' % tag
    ...

good

    ...
    for tag in ('cement', 'water', 'sand', 'fine_stone', 'middle_stone', 'coarse_stone', 'admixture_material', 'additive', 'water_cement_ratio', 'water_glue_ratio', 'mix_duration'):
        practice_tag, construct_tag = map('{}_{}'.format, ('practice', 'construct'), (tag, tag))
    ...

返回


使用zip简化代码

bad

    ...
    for record in record_list:
        # 转换sql记录为字典
        record_dict = {}
        for field_index, field_id in enumerate(field_id_list):
            value = record[field_index]
            if value:
                record_dict[field_id] = value
    ...

good

    ...
    for record in record_list:
        # 转换sql记录为字典
        record_dict = {field_id: value for field_id, value in zip(field_id_list, record) if value}
    ...

返回


zip的逆

    a, b = (('a', 'b', 'c', 'd'), (1, 2, 3, 4))
    zipped = list(zip(a, b))
    assert zipped == [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
    c, d = zip(*zipped)
    assert a == c and b == d

bad

    station_list = [['C1_1', '1标1号拌合站'], ['C1_2', '1标2号拌合站'], ['C2_1', '2标1号拌合站'], ['C2_2', '2标2号拌合站']]
    station_sn_list = [x[0] for x in station_list]
    station_name_list = [x[1] for x in station_list]
    assert station_sn_list == ['C1_1', 'C1_2', 'C2_1', 'C2_2']
    assert station_name_list == ['1标1号拌合站', '1标2号拌合站', '2标1号拌合站', '2标2号拌合站']

good

    station_list = [['C1_1', '1标1号拌合站'], ['C1_2', '1标2号拌合站'], ['C2_1', '2标1号拌合站'], ['C2_2', '2标2号拌合站']]
    station_sn_list,  station_name_list = zip(*station_list)
    assert station_sn_list == ('C1_1', 'C1_2', 'C2_1', 'C2_2')
    assert station_name_list == ('1标1号拌合站', '1标2号拌合站', '2标1号拌合站', '2标2号拌合站')

返回


使用dict.get默认值简化代码

bad

    ...
    water_tag = 'water'
    water = float(record_dict[water_tag]) if water_tag in record_dict else 0
    ...

good

    ...
    water_tag, default_value = ('water', '0')
    water = float(record_dict.get(water_tag, default_value))
    ...

返回


使用dict.update简化代码

bad

    ...
    result_render_dict['date_type'] = date_type
    result_render_dict['station_sn_list'] = station_sn_list
    result_render_dict['station_name_list'] = station_name_list
    result_render_dict['default_station_sn'] = station_sn_list[0]
    ...

good

    ...
    result_render_dict.update({'date_type': date_type,
                               'station_sn_list': station_sn_list,
                               'station_name_list': station_name_list,
                               'default_station_sn': station_sn_list[0]})
    ...

返回


从列表取值

bad

    ...
    assert record and len(record) > 2
    record_id, record_time = record[:2]
    ...

good

    ...
    assert record and len(record) > 2
    record_id, record_time, _ = record
    ...

返回


__

返回