摘要:
pip install black $black mycode.py
PSF(Python Software Foundation),Python的官方组织。PSF 提供的文档包括:
PEP 是 PSF 写出、收纳、提供给社区参考使用、包含新特性、规范性等内容的说明文档。
PEP在 github 上撰写、管理,自动编译html、同步到 网站 上,从2000年开始创建,至今(2019.5)仍非常活跃,如果是python的developer,建议star或watch它,实时跟踪。
PEP分了8类: 核心类、已提交、已接收、讨论中、讨论完、归档、拒绝、废弃 。
要贡献 PEP,需要参考 pep12 的模板来写,并且 PEP 不是用 markdown 写的,而是 ReStructuredText,也是标记(markup)语言,语法比 markdown 稍复杂一点点,这里有一些语法说明:
普通 Python 开发人员必读的PEP有:
核心类 pep 是 PEPs of PEPs,目前只有9个, PEP8 位列其中,内容是 Python 的编码规范,Python开发人员必读文档之一。
PEP8 从2001年创建,至今有141次提交:
修改(提交)集中在2002、 2012~2016年,2019已相对稳定:
参与撰写的人不少,但top3都是python官方的人:
PEP8 有网友翻译: python代码风格指南(PEP8中文版)
下面我来整理一份PEP8的极简版,相比上面的网友翻译,增补了:
极简版正文:
代码的使用频率上看:读取远大于编写,所以一定要写“可读的代码”
入参和if分开讨论
# 函数定义的入参比函数名多1级缩进 def long_function_name( var_one, var_two, var_three, var_four): print(var_one) # 函数调用的入参可对准左括号 or 悬挂缩进1级 foo = long_function_name(var_one, var_two, var_three, var_four) foo = long_function_name( var_one, var_two, var_three, var_four) # if条件语句跨行时缩进:加1级即可 if (this_is_one_thing and that_is_another_thing): do_something()
income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
\x \u \U \N
: str1='\xE4' : print(str1) ä : str2 = '\u4f60\u597d' : print(str2) 你好 : str3='你好'.encode('unicode-escape') : print(str3) b'\\u4f60\\u597d'
from xxx import *
__future__
之后,其他import之前__all__
这种符号称为魔法变量、魔法函数,后来为了破除迷信,称为 double underscore method,但太长了,被简称为 dunder symbol、dunder method……from __future__ import ...
from __future__ import barry_as_FLUFL __all__ = ['a', 'b', 'c'] __version__ = '0.1' __author__ = 'Cardinal Biggles' import os import sys
# 错误示范: spam( ham[ 1 ], { eggs: 2 } ) # 括号里面太多空格 ham[1: 9], ham[1 :9], ham[1:9 :3] # 索引冒号前后不应有空格 long_variable = 3 x = 1 # 不能为了对齐等号而加空格 i=i+1 # =和+左右都要有空格 submitted +=1 # += 右边没有空格 x = x * 2 - 1 # *优先级高,左右不应有空格 def complex(real, imag = 0.0): # 默认参数的=左右不能有空格 return magic(r = real, i = imag)
=
前后有空格, :
和 ->
前无后有python3 中引入了 function annotations,它是一种注释性质的语法,用于声明入参的类型和默认值。
import enforce @enforce.runtime_validation def add_int(a: int, b: int = 0)-> int: return a + b print(add_int.__annotations__)
func.__annotations__
返回函数入参注释信息def add_int(a:int, b: int=0) -> int: # 错误示范,有3处错误 def add_int(a: int, b: int = 0)-> int: # 正确示范
尾随逗号(Trailing Commas)
FILES0 = ('setup.cfg') # 0 FILES1 = ('setup.cfg',) # 1 FILES2 = 'setup.cfg', # 2 FILES3 = ['setup.cfg', 'tox.ini'] # 3 FILES4 = ['setup.cfg', 'tox.ini', ] # 4 FILES5 = [ # 5 'setup.cfg', 'tox.ini', ]
: print(type(FILES0),type(FILES1),type(FILES2),type(FILES3),type(FILES4))
In [42]: FILES1==FILES2 Out[42]: True
In [43]: FILES3==FILES4==FILES5 Out[43]: True
程序猿常见的命名风格多样:
Naming Styles | 举例 | 使用场景 | 备注 |
---|---|---|---|
单个小写字母 | b | 变量名 | 避免使用小写l(易与1,I混) |
单个大写字母 | B | 变量名 | 避免使用大写O(易与0混)、I |
小写串 | lowercase | 变量名,包名,模块名 | 模块名对应文件名,有线系统文件名不区分大小写 |
带下划线的小写 | lower_case_with_underscores | 函数名,类的公开方法 | |
大写串 | UPPERCASE | 常量 | 通常在模块级别定义 |
带下划线的大写串 | UPPER_CASE_WITH_UNDERSCORES | 常量 | 同上 |
驼峰命名法 首字母大写的单词串 |
CapitalizedWords(CapWords) | Class名 | 专有名次缩写可全大写,如HTTP |
匈牙利命名法 混合大小写,首单词小写 |
mixedCase | python抵制 极少特殊情况下用于函数名 |
|
带下划线,首字母大写 | Capitalized_Words_With_Underscores | python抵制,丑陋 | |
短前缀分组+匈牙利或驼峰 | bsp_getPower | python抵制,与C划清界限 | |
单前置下划线 | _single_leading_underscore | 用于不想被外部使用的全局变量 或局部变量 类的非公开方法和实例变量 |
弱内部使用 weak “internal use” |
单后置下划线 | single_trailing_underscore_ | def func(class_='name') |
用于避免与 Python关键词的冲突 |
双前置下划线 | __double_leading_underscore |
类Foo中的 __bar 会变成 _Foo__bar |
当用于命名类属性,会触发名字重整 |
双前后下划线 | __dunder_func__ |
Dunder(魔法)函数 | 不要自己发明这样的名字 |
__all__
下面就不单纯是格式(format)的问题了,而是有利于程序运行的内容。
if foo is not None
优于 if not foo is None
if foo == None
用法错误__eq__, __ne__, __lt__, __le__, __gt__, __ge__
f = lambda x: 2*x #不推荐
def foo(x): if x < 0: return # No!请 return None return math.sqrt(x)
# 字符串方法 >>> type(str) # str是python关键字>>> STR="Hello World" >>> type(STR) >>> str.lower(STR) 'hello world' >>> STR.lower() # str.xxx() == STR.xxx() 'hello world' >>> STR[::-1] # 翻转字符串 'dlroW olleH' >>> import string >>> STR = "Hello {0}" >>> f = string.Formatter() >>> f.format(STR,"world") 'Hello world' >>> STR.format("World") 'Hello World
>>> STR[:5]=="Hello" # 切片 —— No! True >>> STR.startswith("Hello") # Yes! True
if isinstance(obj, int): # Yes! if type(obj) is type(1): # No!
if not seq: # Yes! if len(seq): # No!
if greeting: # Yes! if greeting == True # No! if greeting is True: # Worse
要实现 PEP8 中的大部分条目并不难,但要持之以恒、实现所有条目,应该还是挺难的。
一个人要实现 PEP8 中的条目并不难,但要保证一个团队都做到,应该还是挺难的。
如果有一个自动格式化工具,当我 Ctrl-S 保存文件的时候实时帮我按 PEP8 美化一下,上面2个问题就都不是问题了。
Google 出品的 YAPF 可以满足你这方面的几乎所有需求。
$ pip install yapf $ yapf [options] [files [files ...]]
常用的 options 有:
--diff
: 输出diff文件,不修改源文件--style STYLE
:
[yapf] based_on_style = pep8 spaces_before_comment = 4 split_before_logical_operator = true
Black 帮我们实现这个愿望。
用了 Black,相当于放弃了自己的编码风格,完全由 Black 来接手,一个团队的代码就像一个人写的 —— 至少样子上是。
经过一段时间之后,个人手写水平也能提高,逐步逼近 PEP8 的要求。
$ pip install black $ black [options] [mypythonfile 或 dir]
-t [py27|py33|py34|py35|py36|py37|py38]
--check
:只检查,不修改源文件--diff
: 输出diff文件,不修改源文件--include XXX
, --exclude XXX
: 操作目录时的筛选器Item | YAPF | Black | 点评 |
---|---|---|---|
License | Apache2.0 | MIT | 两者都允许修改后闭源,即允许只发布二进制版本,但必须附带License。Apache要求每个文件头上都要附带,MIT只需要根目录下放一份即可。 |
Author | Python | 所以Black只支持PEP8,YAPF则可定制 | |
创建日期 | 2015-03-18 | 2018-03-14 | 3月是个好季节 |
提交次数 | 1000+ | 500+ | 截至2019.5 |
找一段代码实战比较一下,从左至右依次是:原始文件、black处理后的文件、yapf依pep8处理过的文件
可以看出 black 和 yapf 能做的和 PEP8 比较还是有限,且目前都还在 书写美化 上下功夫,在 代码优化 上不足。