首页 > 技术文章 > 面试题之编程语言规范

welan 2022-06-06 11:34 原文

一、问题

趋势科技golang/python暑期软开实习一面

面试官:你听说过PEP8规范吧,至少列举5条规范
面试官:谈谈golang编码规范

二、PEP8规范

​PEP是Python Enhancement Proposal的缩写,通常翻译为“Python增强提案”。​每个PEP都是一份为Python社区提供的指导Python往更好的方向发展的技术文档,其中的第8号增强提案(PEP 8)是针对Python语言编订的代码风格指南。
语言规范不会影响代码的实际功能,但是好的编程规范,写出可读性强的代码可以保证开发效率和团队协作效率的提升。

1、一些规范如下

  • 每行缩进最好用4个空格代替tab,不允许tab和空格混用,因为有的编辑器默认的是tab键是两个空格,所以如果tab和空格混用会造成代码格式混乱。
  • 单行最大字符限制79,用\换行。
  • 避免通配符导入,分行导入不同的包,通配符导入有时候无法导入一些特定的变量或者方法,并且多个包都是用通配符导入会有命名冲突的问题。
  • 不要害怕用过长的命名变量,越清楚越好,比如使用is,has等前缀标识bool型变量。
  • 命名用英文,而不是拼音,来提高可读性。
  • 下划线分割英文,提高可读性,python更偏向于使用小写英文与下划线组合的方式标识变量名。
  • 大写字母表示常量
  • 类首字母大写,函数全小写
  • class 的 method 之间一个空行
  • 函数内逻辑无关的段落之间空一行,不要过度使用空行
  • except 要接具体的Exception,使用 finally 子句来处理一些收尾操作
  • 用is代替 == ,is比较的是内存地址,而 == 则需要深入对象字典逐个属性比较,更慢。

2、PyCharm中的代码规范

默认的PyCharm中有PEP8代码提示,你敲得代码中不符合规范时,会有下划波浪线提示,想要调整提示级别可以直接在右下角调整 Highlighting Level。

二、golang中的代码规范

golang属于静态类型语言,跟python动态类型对比来说,规范更严格一些。

1、命名规范

  • 当命名(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用。
  • 命名如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的。

1.1 包名:package

尽量保持包名和目录名一致,采取简短有意义的小写单词作为包名,避免和标准库冲突重复,不要使用混合大小写和下划线

package cnblog

1.2 文件名

也就是包下面的.go文件,采用小写单词+下换线组合的方式

/cnblog
———— user_model.go

1.3 结构体命名

大驼峰命名法命名结构体,结构体内部使用多个初始化格式

// 多行申明
type User struct{
    Username  string
    Email     string
}
 
// 多行初始化
u := User{
    Username: "golang",
    Email:    "golang@163.com",
}

1.4 接口命名

命名规则基本和上面的结构体类似,单个函数的结构名以 “er” 作为后缀,例如 Reader , Writer,表示单一职责,只有读或者只有写这种行为。

type Reader interface {
        Read(p []byte) (n int, err error)
}

1.5 变量命名

  • 采用大驼峰命名,如果变量包内私有,则首单词小写,比如apiClient。
  • 如果变量为bool类型,也可以以Has,Is等单词开头命名

1.6 常量命名

全部以大写单词组成常量名,但是采用下划线分隔单词

2、注释

Go提供C风格的/* */块注释和C ++风格的//行注释。行注释是常态;块注释主要显示为包注释,表达式内禁用块注释

  • 单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释
  • 多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段

2.1 块注释

每个包都应该有一个包注释,一个位于package子句之前的块注释或行注释。包如果有多个go文件,只需要出现在一个go文件中(一般是和包同名的文件)即可。 包注释应该包含下面基本信息(请严格按照这个顺序,简介,创建人,创建时间):

  • 包的基本简介(包名,简介)
  • 创建者,格式: 创建人: rtx 名
  • 创建时间,格式:创建时间: yyyyMMdd

2.2 结构体/接口注释

通常放在接口或者结构体的前一行,对其进行简要介绍,格式为: 结构体名, 结构体说明。同时结构体内的每个成员变量都要有说明,该说明放在成员变量的后面(注意对齐)

2.3 函数/方法注释

包括三个部分

  • 简要说明,格式说明:以函数名开头,“,”分隔说明部分
  • 参数列表:每行一个参数,参数名开头,“,”分隔说明部分
  • 返回值: 每行一个返回值

2.4 代码注释

  • 对局部复杂或者关键位置逻辑采用行内注释
  • 注释内中英文混用时候,采用空格分隔,
  • 单行注释不要过长,不超过120个字符

3、import规范

  • 避免引入无用的包,与python不同的是,如果导入的包未被使用,golang会编译不通过,而在Pycharm中,如果导入的py包未被使用,仅仅是代码块变暗。
  • 不同类型的包顺序,通常import多行的时候,会自动格式化,如果导入包含多种类型的包,建议有顺序的组织包的顺序。不同类型的包之间采用空行,如果是当前项目的其他包,最好采用相对路径。
    import (
       "标准库"
    
       "项目包"
    
       "第三方包"
    )
    
  • 合并包,用.点号可以将引入的一个包合并到当前程序下
    这里每次使用Println需要通过fmt包调用
    import (
        "fmt"
    )
    func main(){
        fmt.Println("fmt.Println")
    }
    
    通过将fmt合并到当前程序下,可以直接使用Println
      import (
          . "fmt" // 使用 . 合并
      )
      func main(){
          fmt.Println("fmt.Println")
      }
    
  • 包重命名,包内命名冲突问题,比如有两个包 package1,package2, package2 下有一个对整型做加运算的Add方法,package2 下有一个对float32做加运算的Add方法,两个方法相同,如果都在当前程序下导入两个包,就会出错,如下
    import (
      "package1"
      "package2"
    )
    
    可以采用重命名的方式对包引入当前程序空间内的时候修改包名。在python中,比如import package as my_package
    import (
      "fmt"
      int_add "package1"
      float_add "package2"
    )
    func main(){
      fmt.Println(init_add.Add(1, 2))
      fmt.Println(float_add.Add(1, 2))
    }
    
  • 匿名包,引入无用的包的时候会报错,使用 _ 一个下划线可以标识引入的包为匿名引用,虽然不会出现语法错误,但是可能出现一些额外的意想不到的问题。主要原因在于,只要 import 导入一个包,就会自动执行这个包的 init 函数,并且如果一个 package 包下有多个 .go文件,多个 .go文件中都有 init 函数,那么匿名方式导入这个包,不能保证init函数的执行顺序,所以一般不建议一个包包含多个init函数,也不建议使用匿名引用的方法导入包。
    import (
      "fmt"
      _ "package1"
    )
    

4、错误处理

golang三个痛点可能就是包管理,异常处理以及泛型,go没有异常类型,只有错误。所以也就是有了这种常见的写法

str := "123"
num, err := strconv.Atoi(str)
if err != nil{
  fmt.Println("strconv.Atoi err: ", err)  // 或者打印日志
  return 
}
  • 错误处理的原则就是不能丢弃任何有返回err的调用,不要使用 _ 丢弃,必须全部处理。接收到错误,要么返回err,或者使用log记录下来
  • 尽早return:一旦有错误发生,马上返回
  • 尽量不要使用panic,如果使用panic,要用recover捕获
  • 错误描述如果是英文必须为小写,不需要标点结尾
  • 采用独立的错误流进行处理

推荐阅读