不是程序员,代码也不能太丑!python官方书写规范:任何人都该了解的 pep8

简介:为什么要强调 书写规范 ?这其实并不关乎“美丑”,而是为了 更高的效率(代码阅读、开发、维护)与更方便的合作(全球通用的标准) 。如今,不管什么方向的同学都要进行“写代码”这项工作,可惜的是,很多朋友并没有意识到:花费1小时了解代码书写规范,可以为自己节省 100+ 小时的写代码的时间。 代码规范的魅力在于 实实在在地简化问题 ,并不需要我们奉为圭臬或引起争论。本文我们主要以 python 为例,以 pep8 为主要参考资料,分三个层次进行讨论。

本文三个层次:

  • 不注意这些,你写的根本不是代码
  • 这些规范,实质是尊重程序的逻辑
  • 一些我会忽略的规范,更多的思考

不注意这些,你写的根本不是代码

1/2 来看看我两年前的代码

import tkinter
import tkinter.messagebox
def message_box(error_content):
top = tkinter.Tk() # *********
top.withdraw() # ****实现主窗口隐藏
top.update() # *********需要update一下
txt=tkinter.messagebox.showinfo("提示",error_content)
top.destroy()
return txt

上述是在用 pythontkinter 做一个桌面应用,看起来似乎没什么问题?大问题,比如项目逻辑/设计架构 的确难以通过这么几行代码看出来 ;但是,上面这几行代码的书写习惯,反映了一个人的思维不够成熟:

  • 注释不需要 # **** 这么书写,完全没必要
  • 需要update一下 这是废话,前面不就是 update() 函数吗?
  • ("提示",error_content) 中间应该打空格
  • txt=tkinter 左右两边应该加空格

如果让现在的我来写,我会如下实现:

import tkinter
from tkinter import messagebox as msg def show_message_box(error_content):
"""
Piper蛋窝:
输入错误信息,messagebox 显示信息 (皮一下:欢迎关注 Piper蛋窝~)
"""
tk = tkinter.Tk()
tk.withdraw()
tk.update()
txt = msg.showinfo("提示", error_content)
tk.destroy()
return txt

如上:

  • 我改掉了些小毛病,比如有没有空格等,但这其实不是重点
  • 我把函数名从 message_box 改为了 show_message_box ,因为 message_box 看起来像一个名词,并不是动词(去执行一项任务),在项目结构复杂后,我们可能有很多函数、类、实例,如果 不做动名词区分,我们自己都可能混淆,还要翻回源码进行查阅,浪费大量时间
  • 我把注释(这个函数负责干什么)放在了 """注释""" 里,这样解释器与 InelliSense 会在我们调用时,做自动的说明,如下图

如图,调用时智能地显示我们的注释

2/2 最基本的:缩进、命名与空间

朋友,如果你写代码时连 缩进、命名与空间 这三点都不会注意到,那恭喜你,这篇文章很有可能让你提升一个阶段。

我就见过刚刚学 c 的大一小朋友,在机房的 VC 6 里一个一个地敲下字符:

#include <stdio.h>
int main()
{int a=5,b=7,c;
printf("a=%d,b=%d,c=%d",a,b,c);
return 0;}

很显然,这位小朋友的代码真的仅仅是“敲进电脑的字符”而已,ta心里完全没有程序的逻辑、层次。这代码是死的,不是活的。我仅仅加一些空格和回车,来解释, 为什么这些缩进、命名与空间让代码成为真正的代码

#include <stdio.h>

int main()
{
int a = 5, b = 7, c;
printf("a = %d, b = %d, c = %d", a, b, c);
return 0;
}

如上:

  • 我把 {} 独立,并对其中代码块做了缩进,表示这些代码是函数 main() 内部的逻辑
  • 我加了空格,如把 a=5 变成了 a = 5 ,是因为程序员也是人,也需要读看得清晰的东西
  • 我在 #include <stdio.h>int main() 间加了空行,因为这二者是两件事:前者负责引入io标准库,后者负责执行逻辑。 在写代码时,不要吝啬空行,来区分不同的逻辑与任务

上面的讨论是不是过于基础?下面我们以 python 以及其官方文档 pep 8 为例,来看看更多体现程序逻辑、增强代码可读性的官方建议。

如果下回有人再让你看ta写的“死代码”,你先把这篇文章扔给ta,让ta改好!

这些规范,实质是尊重程序的逻辑

1/4 缩进与空格:体现逻辑

Indentation 缩进

著名美剧《硅谷》里有个情节, 程序员会因为使用制表符还是空格吵得不可开交,似乎是一个原则性问题?

这当然只是玩笑。

在 python 中,推荐使用 4 个空格来进行缩进。我在打 kdd cup 是见过 2 个空格表示缩进的(官方 start toolkit 里)。我觉得这是无所谓的,关键是, 你要在项目里进行统一。

此外, 缩进是用来体现程序结构的,如果你的结构不是包含关系,仅仅是换行,那么也用 4 个空格缩进将很愚蠢。 如下。

# 推荐
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 或者
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 或者
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 或者
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one) # 愚蠢
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 愚蠢
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)

为什么?我们以最下面的 def long_function_name 为例,注意到了吗:

  • print(var_one) 应该是 def long_function_name 的内部结构,因此必须比 def long_function_name 前多一个缩进
  • 但是 var_one, var_two, var_three 这些参数是 def long_function_name 的一部分,与 def long_function_name 同级
  • 函数参数与函数内部结构对齐,就很容易让人混淆

因此,你只需要 制造区分度 就可以。至于你是多加两个空格,还是减少两个,这个无所谓(除非你们组织做了特定的规定), 能让你和别人一眼看出逻辑结构就好

Should a line break before or after a binary operator?

此外, pep 8 还推荐了 operator 的位置,这个我以前真的没有注意到。

# No: operators sit far away from their operands
income = (gross_wages +
taxable_interest +
(dividends - qualified_dividends) -
ira_deduction -
student_loan_interest) # Yes: easy to match operators with operands
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)

此外,为了防止编译错误,我会在换行时加上 \ 表示将换行键转义。

income = (gross_wages \
+ taxable_interest \
+ (dividends - qualified_dividends) \
- ira_deduction \
- student_loan_interest)

现在看来,可能这种情况下没有这个必要。

拒绝无意义的空格

Yes: spam(ham[1], {eggs: 2})
No: spam( ham[ 1 ], { eggs: 2 } ) Yes: if x == 4: print x, y; x, y = y, x
No: if x == 4 : print x , y ; x , y = y , x

如上是 Pet Peeves 中的建议,很显然,过于松散的结构,也不利于我们阅读和看出逻辑。这是一种所有编程语言通用的习惯,值得养成。

函数变量省缺值

# 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)

如上,在定义某个省缺值时,我们鼓励去掉 = 左右的空格。

# Yes
def munge(sep: AnyStr = None): ...
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ... # No
def munge(input: AnyStr=None): ...
def munge(input: AnyStr, limit = 1000): ...

但是,如上,值得注意的是,如果定义了函数的数据类型(如 AnyStr),则我们需要提醒开发者注意区分,不要去掉 = 左右的空格。

2/4 import

Yes: import os
import sys No: import sys, os # Yes
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b) # No
i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)

关于 import 的规范有几条,我这里着重强调一个新手可能都会有的“坏习惯”: 把毫不相干的库放在一个 import 下。

图什么呢?没有任何意义。比如上面,sys 其实与 os 没有任何包容关系,我们何必吝啬这一行 import 呢?且放在一起,不利于 formatter 帮我们整理书写。

3/4 变量、函数名称

选一个好的命名规则

不同的企业/组织,尤其是大型的企业,会有一套自己的命名规范。对于 java ,我记得大家最常用的应该是“驼峰”式命名:

public void printUserName(int index) {
...
}

在 python ,鼓励各种通用形式的命名,如:

  • printUserName
  • print_user_name

我觉得大家在 python 中最常用的是 下划线+小写 的形式。

在 pep 8 的 Descriptive: Naming Styles 有个标注,很搞笑:

Capitalized_Words_With_Underscores (ugly!)

为什么说 Capitalized_Words_With_Underscores “丑”呢?

我觉得是 信息冗余 了:下划线或者大写首字母,都是用于间隔单词的,两个都使用,真的不简洁、不优雅。

“私有”变量

在 C++ 或者 java 中,我们都会接触到 private 这类概念。初学者可能会一头雾水:为什么变量要分为私有的、公共的、受保护的?

python 让初学者避开了这部分可能产生的费解,但是又没有去掉私有变量等功能,我觉得这正是 pythonic 的体现。 而且,现在的语言都有此趋势,比如 go ,限制首字母大小写区分变量私有共有,简洁优雅,又统一了社区开发规范。

对于 python ,我们在变量前加了两个下划线,则其变为私有了。私有变量为了项目的规范与安全,不能被外部调用,我写了一段程序如下。

如上,直接调用 Foo.__a 或者 foo.__b 会产生 AttributeError 错误。但是 python 给了个“后门”,你仍然可以通过 _类名__变量名 调用私有变量。

4/4 尊重“人类的思维”

# Yes
if foo is not None: # No
if not foo is None:

如上,或许新手会觉得 not ... is 这种结构逻辑来了个反转,很好玩,尽管其作用与 is not 完全相同。

还是那句话,程序员也是人,大家都喜欢简单清晰的逻辑。除非是很巧妙的技巧,否则,没有必要玩文字游戏,降低效率。

一些我会忽略的规范,更多的思考

1/3 每行最多字符数?I say NO.

我们知道, pep 8 希望我们每行 最多 79 个字符。

我觉得对于我这种开发者来说,真的没必要。而且,我读过很多优秀的开源框架,其也没有尊重这个标准。

原因很简单,我们的生产环境不同。

我喜欢大字体,而且我只有一个小小的笔记本电脑,连工位都没有。 很多朋友拥有好几块屏幕/加长屏幕,而我只能把一块小小的笔记本显示器竖着一分为二。如下图。

无论是 79 个字符,还是 109 个字符,我的编辑器都不能一行显示下来,因此这条规范对我来说意义不大。

或许开发 python 标准库时会用上。但不是现在。

2/3 注释文件标准?我复议

pep 8 与 pep 257 中,都对注释方式进行了标准化,如下。

def complex(real=0.0, imag=0.0):
"""Form a complex number. Keyword arguments:
real -- the real part (default 0.0)
imag -- the imaginary part (default 0.0)
"""
if imag == 0.0 and real == 0.0:
return complex_zero
...

我并不是很感兴趣。原因:

  • 我记得 pyCharm 中,默认的注释并不是这样的,说明标准并不是唯一的
  • 具体使用什么标准,我觉得要 就事论事 ,比如 MkDocs 会帮我们自动地把注释编译成文档并发布在网上,因此我们想要使用 MkDocs 时,去学习 MkDocs 的规范便好

如上是 thu-ml/tianshou 源码,其注释使用 markdown 写的, 如果去访问说明文档,你会发现说明文档就是根据源代码注释自动生成的。

Amazing. 这才是“写代码的”该有的美德:懒惰,不做重复的动作,能自动化就自动化。

3/3 读规范文档不如多读好项目

最后,光说不练假把戏。

在我看来,多读优秀的代码、项目, 有意识地注意高手的书写规范和其怎么安排项目结构,意义远比只读 pep 8 要大很多。

祝各位变得更强。欢迎关注公众号:Piper蛋窝,回复微信加我微信。欢迎点赞、点击在看鼓励我。

不是程序员,代码也不能太丑!python官方书写规范:任何人都该了解的 pep8的更多相关文章

  1. 程序员需要掌握的七种 Python 代码更易维护的武器

    检查你的代码风格 PEP 8 是 Python 代码风格规范,它规定了类似行长度.缩进.多行表达式.变量命名约定等内容.尽管你的团队自身可能也会有稍微不同于 PEP 8 的代码风格规范,但任何代码风格 ...

  2. 程序员代码面试指南 IT名企算法与数据结构题目最优解

    原文链接 这是一本程序员面试宝典!书中对IT名企代码面试各类题目的最优解进行了总结,并提供了相关代码实现.针对当前程序员面试缺乏权威题目汇总这一痛点,本书选取将近200道真实出现过的经典代码面试题,帮 ...

  3. 60 个让程序员崩溃的瞬间,太TM真实了

    前方高能!笑死人不偿命系列~ 表演即将开始,吃东西的请停下来,不然你会后悔的 1. 公司实习生找 Bug 2. 在调试时,将断点设置在错误的位置 3. 当我有一个很棒的调试想法时 4. 偶然间看到自己 ...

  4. HarmonyOS UI组件在线预览,程序员直呼“不要太方便~”

    一.介绍 以往大家如果想查看组件的使用效果,需要打开DevEco Studio构建工程.现在为了便于大家高效开发,文档上线了JS UI组件在线预览功能,无需本地构建工程,在线即可修改组件样式等参数.一 ...

  5. 为什么不用C++写游戏(聪明的程序员不用C++折磨自己)(这些工作,QT都替开发者解决了,C++没有根类导致太多的问题,也没有字符串类)

    当今世界上绝大多数游戏都是C++写的,为什么要说不呢? 要做什么?写游戏. 写游戏首先要考虑些什么?做什么样的游戏,图形.音效.游戏逻辑如何实现. 用C++要先考虑什么?定义跨平台数据类型抽象,实现常 ...

  6. 想转行做程序员,是学习JAVA还是Python?哪个更好?

    请大家务必审题,转行做程序员,是程序员,并非数据分析也不是软件测试. 首先声明:这是一篇容易引起撕逼的问答,为了祖国和谐,人民安康,请各位看官尽量理性讨论. 同时,这篇文章是面向一些初入行的朋友进行一 ...

  7. .Net程序员学用Oracle系列(3):数据库编程规范

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.书写规范 1.1.大小写风格 1.2.缩进风格 1.3.换行 1.4.其它 2.命名规范 2.1.数据库对象命名 2.2.变 ...

  8. 程序员快递请查收,来自Python黑客大佬的一份DDOS攻击说明书!

    DDoS攻击没有我们想象中的那么简单,并不是什么Python程序员都能够做到的. 若要知晓黑客利用DDOS攻击原理那么我们必须要知道是实行DDoS攻击比较难的原因是什么? 很简单的一句话概括:&quo ...

  9. [程序员代码面试指南]数组和矩阵问题-找到无序数组中最小的k个数(堆排序)

    题目链接 https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf?tpId=13&tqId=11182&t ...

随机推荐

  1. SQL SERVER级联查询及数据结构《存储过程-递归树形查询》

    --创建表,插入数据 create table tb(id varchar(3) , pid varchar(3) , name varchar(10))insert into tb values(' ...

  2. 电脑查看当前自己的wifi密码

    菜单+R 输入control  点击确认.

  3. 【Java】阿里巴巴开发规范手册

    Java 开发手册 一. 编程规约 (一) 命名风格 [强制]代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束. 反例: _name, $name, __name [强制]代码中 ...

  4. php 使用 phpword 操作 word 读取 word

    思路 1. 加载word文件.2. 循环判断加载出来的数据.( 数据下面有很多个节点 )( 节点是按照数据的类型分类的 例如 无样式的文本是RunText,换行是TextBreak,表格是table. ...

  5. windows本地破解用户口令

    实验所属系列:操作系统安全 实验对象: 本科/专科信息安全专业 相关课程及专业:信息网络安全概论.计算机网络 实验时数(学分):2学时 实验类别:实践实验类 实验目的 1.了解Windows2000/ ...

  6. 基于gin的golang web开发:使用数据库事务

    在前文介绍访问数据库时介绍了github.com/jmoiron/sqlx包,本文基于这个包使用数据库事务. defer 在使用数据库事务之前,首先需要了解go语言的defer关键字.defer是go ...

  7. 数论总结——更新ing

    数论还是有很多没学完 只是小小的总结 一.同余定理 1.反身性:\(a\equiv a (mod m)\) 2.对称性:若\(a\equiv b(mod m)\),则\(b\equiv a (mod ...

  8. 变强——GitHub 热点速览 Vol.46

    作者:HelloGitHub-小鱼干 网络不通怎么办?Ping 就是你的调试大招,而 gping 则是 Ping 的内功,终端可视化显示 Ping 结果,一眼看明数据接收情况.前端调试大招又是什么呢? ...

  9. parted分区对齐

    分区提示未对齐 [root@lab8106 ceph]# parted /dev/sdd GNU Parted 3.1 Using /dev/sdd Welcome to GNU Parted! Ty ...

  10. Jmeter 添加 计数器

    第一步: 添加 > 配置元件  > 计数器    如下图所示: 第二步: 设置递增值与引用名称 第三步:使用引用名称 第四步:执行脚本,查看结果