数据开发 的 代码规范 以及 代码评审脚本(持续更)

Posted 小基基o_O

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据开发 的 代码规范 以及 代码评审脚本(持续更)相关的知识,希望对你有一定的参考价值。

1、前言

  • 在数据开发中,由于各程序员的风格不一、注释过少等原因,导致后期代码修改时困难重重
  • 代码评审:通过 阅读代码 来 检查代码质量
    代码评审有助于降低后期代码维护成本
  • 使用代码评审自动化脚本(Python3实现),可提高代码评审的效率

2、代码规范

2.1、通用规范

  • 注释
    中文注释≥1行 或 不低于占比下限(例如:100行代码里,至少5行中文注释)
    注释不用太多,建议代码即注释(建议用英文全称,并查词典)
    涉及业务的代码建议加详细注释
  • 代码中不得出现生产环境的明文密码
  • 一般情况下,单行代码不可太长(例外:长URL、长import
  • 代码要有说明文档,名为README.md
常见缩写全称中文常见缩写全称中文
fnfunction函数ymdYear Month Day日期
txttextfile文本文件cntcountv. 计数;n. 总数
objobject对象numnumber数字;号码
lslistn. 列表;v. 列清单lvllevel等级
calccalculationn. 计算uvunique visitor独立访客
idxindex file索引文件pvpage view页面浏览量
liblibrary软件库dwdata warehouse数据仓库
strstring字符串bakbackup备份
probprobability概率skuStock Keeping Unit库存单位

2.1.1、传参规范(待完成)

2.1.2、配置文件格式(待完成)

参照Java的properties

2.2、Python代码规范

2.2.1、命名规范

命名规范说明示例
变量、方法、函数、包全小写,下划线分隔单词function_name
类中的方法和函数(不被外部直接调用的)双下划线开头,全小写,下划线分隔单词__method_name
常量全大写 和 下划线PUBLIC_CONSTANT
模块全小写,下划线分隔单词module_name.py
每个单词首字母大写ClassName

2.2.2、注释规范

注释情况注释建议
模块注释、函数注释、类注释、方法注释…三双引号,注释的前面没有空行
单行代码注释、多行代码注释井号
多行注释(单行注释太长时,建议写成多行)井号(连续行)
"""
模块注释
"""
from time import time

# 多行代码的注释
# 多行注释(一行写不下的时候建议写多行)
NUM = 5
CNT = 1000


def function1():
    """函数注释"""
    t = time()  # 单行代码注释
    return t


class ClassName:
    """类注释"""

    @classmethod
    def method4(cls):
        """方法注释"""


if __name__ == '__main__':
    print(__doc__)
    print(function1.__doc__)
    print(ClassName.__doc__)
    print(ClassName.method4.__doc__)

2.2.3、其它规范

  • 缩进(4个空格)、空行、空格:
    建议把Pycharm更到最新,用Ctrl+Alt+l来规范代码
  • 字符串:
    优先用单引号,多行则用三单引号
    单行子字符串里有单引号时用双引号,如:sql = "SELECT '2021-12-02'"
    不使用三双引号

2.3、SQL代码规范

  • 关键字:全大写
  • 库名、表名、字段名:全小写,下划线分隔单词
    库名:业务名称
    表名:[临时表标识_]分层名_子业务名称
  • 建表时,表和字段要有注释,建议用中文
  • 4个空格为1个缩进量
  • 慎用SELECT *
  • 注释用--(双减号+空格,mysql和HIVE都支持)
    子查询用WITH AS,每个子查询都要有中文注释
WITH
-- 注释1
t1 AS (SELECT a FROM t0),
-- 注释2
t2 AS (SELECT a FROM t1),
-- 注释3
t3 AS (SELECT a FROM t2)
-- 注释4
SELECT a FROM t2;
  • 代码头部
-- 名称:A9527
-- 所属业务:A
-- 需求文档:链接
-- 创建者:小基基
-- 创建日期: 2021-10-24
-- 修改日志(修改日期,修改人,修改内容):
-- 2021-12-12,小黄,添加xxx指标

2.4、Java和Scala代码规范

注释注释方式
文档注释/** */
多行注释/* */
单行注释//
命名规范说明示例
变量、方法第一个单词全小写,后续单词首字母大写methodName
每个单词首字母大写ClassName
包、项目名全小写
常量全大写 和 下划线PUBLIC_CONSTANT

3、代码评审 自动化脚本

功能:整体代码扫描、单个代码文件扫描

使用方法:
将(不含外部包)的代码 和 该代码扫描脚本 放到同级目录,使用Python3运行

代码扫描报告示例:

import os
import re
from collections import defaultdict
from pandas import DataFrame


class File:
    class Compile:
        @staticmethod
        def findall(string):
            return []

        @staticmethod
        def match(string):
            return not None

    # 文件名后缀
    SUFFIX = ''
    # 提取注释的正则表达式
    COMMENT_PATTERN = Compile
    # 合格代码的最低注释占比
    COMMENT_PROPORTION_THRESHOLD = 0
    # 代码头部模板
    HEAD_PATTERN = Compile
    # 不合规代码
    UNQUALIFIED_CODE_PATTERN = Compile

    def __init__(self, file_name):
        self.file_name = file_name
        # 读取文件
        txt = self.read_file()
        # 字数
        self.number_of_words = len(txt)
        # 行数
        self.number_of_lines = len(txt.split('\\n'))
        # 注释抽取
        comments = self.COMMENT_PATTERN.findall(txt)
        # 注释行数
        self.number_of_comments = sum(len(c.strip().split('\\n')) for c in comments)
        # 注释个数的占比
        self.comment_proportion = self.number_of_comments / self.number_of_lines
        # 代码是否合格
        self.flunk = False
        if self.comment_proportion < self.COMMENT_PROPORTION_THRESHOLD:
            self.flunk = True  # 注释太少
        if self.HEAD_PATTERN.match(txt) is None:
            self.flunk = True  # 代码头部没有按照指定模板
        if self.UNQUALIFIED_CODE_PATTERN.findall(txt):
            self.flunk = True  # 出现不合格的代码语句

    def read_file(self):
        try:
            with open(self.file_name, encoding='utf-8') as f:
                return f.read().strip()
        except UnicodeDecodeError:
            return ''

    def report(self):
        print('文件名称', self.file_name)
        print('字数', self.number_of_words)
        print('行数', self.number_of_lines)
        print('注释行数', self.number_of_comments)
        print('注释行数占比', self.comment_proportion)
        print('不及格', self.flunk)


class PyFile(File):
    SUFFIX = '.py'
    COMMENT_PATTERN = re.compile(r'"""[\\s\\S]+?"""|# .+')
    COMMENT_PROPORTION_THRESHOLD = 0.05


class SqlFile(File):
    SUFFIX = '.sql'
    COMMENT_PATTERN = re.compile('-- .+')
    COMMENT_PROPORTION_THRESHOLD = 0.05
    UNQUALIFIED_CODE_PATTERN = re.compile(r'select\\s+\\*', re.I)


class JavaFile(File):
    SUFFIX = '.java'
    COMMENT_PATTERN = re.compile(r'/\\*[\\s\\S]\\*/|//.+')
    COMMENT_PROPORTION_THRESHOLD = 0.1


class ScalaFile(JavaFile):
    SUFFIX = '.scala'


class ShFile(File):
    SUFFIX = '.sh'
    COMMENT_PATTERN = re.compile('# .+')
    COMMENT_PROPORTION_THRESHOLD = 0.1


class Files:
    FILES = (PyFile, SqlFile, ScalaFile, JavaFile, ShFile, File)

    def __init__(self):
        self.statistic = t.SUFFIX: [] for t in self.FILES

    def traversal(self, path=os.path.dirname(__file__)):
        """递归遍历文件"""
        for file_name in os.listdir(path):
            abs_path = os.path.join(path, file_name)
            if os.path.isdir(abs_path):
                for p in self.traversal(abs_path):
                    yield p
            elif os.path.isfile(abs_path):
                yield abs_path

    def calculate(self):
        """计算 文件数、代码量、注释量…"""
        for abs_path in self.traversal():
            if abs_path == __file__:
                continue
            for f in self.FILES:
                if abs_path.endswith(f.SUFFIX):
                    self.statistic[f.SUFFIX].append(f(abs_path))
                    break

    @property
    def flunk(self):
        """不及格代码"""
        return (f.file_name for files in self.statistic.values() for f in files if f.flunk)

    def files_report(self):
        """整体分析"""
        self.calculate()
        df = defaultdict(list)
        index = []
        for suffix, files in self.statistic.items():
            index.append(suffix)
            df['文件数'].append(len(files))
            df['不及格文件数'].append(sum(f.flunk for f in files))
            df['代码字数'].append(sum(f.number_of_words for f in files))
            df['代码行数'].append(sum(f.number_of_lines for f in files))
            df['注释行数'].append(sum(f.number_of_comments for f in files))
        df = DataFrame(df, index)
        df.loc['合计'] = df.sum()
        df['注释占比'] = df['注释行数'] / df['代码行数']
        df['不及格率'] = df['不及格文件数'] / df['文件数']
        print(df)
        # df.to_excel('代码扫描报告.xlsx')
        print('不及格代码:')
        for f in self.flunk:
            print(f)

    def single_file_report(self, the_file):
        """单个文件分析"""
        for f in self.FILES:
            if the_file.endswith(f.SUFFIX):
                f(the_file).report()
                break


if __name__ == '__main__':
    Files().files_report()
    # Files().single_file_report(__file__)  # 检查自己

以上是关于数据开发 的 代码规范 以及 代码评审脚本(持续更)的主要内容,如果未能解决你的问题,请参考以下文章

代码规范

实验四 代码评审

实验四 代码评审

实验四 代码评审

实验四 代码评审

实验四 代码评审