前言

对我来说,以前每次面试是我审视自己,检验自己的一种方式。每次准备面试,以及被面试官问住的时候才会发现,其实我 python 我学的还不够好;工作中也是,可以从其他的同事那里获得成长。但是我今天说的是,我也在自己总结和思考最佳实践这件事.

我想很多人都会有意识的去读一些 PEP (Python Enhancement Proposals). 了解语言设计者当时的考虑,这些文案也是经过很长时间的讨论最后才实施的. 既然想用好这门语言,必然需要理解设计之美。比如我听说 gvanrossum 使用 emacs 作为编辑器,我也使用 emacs, 就是希望我可以更贴近一些 python

本文根据 The Best of the Best Practices (BOBP) Guide for PythonKhan's style-guides 中对于开发中一些事物的理解和看法,有出至 PEP, 也有一些 python 界知名开发者,我加入了一些我自己的理解和看法.

价值观

"Build tools for others that you want to be built for you." - Kenneth Reitz (requests 等知名库作者)

你自己都不想用的东西做出来有什么意义呢?

"Simplicity is alway better than functionality." - Pieter Hintjens (ZeroMQ)

我对函数式编程的看法一直是看场景, 甚至于我经常会对比性能,义无反顾的使用性能最好的,但是代码又不难懂和繁琐的

"Fit the 90% use-case. Ignore the nay sayers." - Kenneth Reitz

程序员都有完美主义情怀,但是其实往往我们是在偏激的看事情 - 用户其实不 case

PEP20">"Beautiful is better than ugly." - PEP 20

开发参考

PEP20">"Explicit is better than implicit" - PEP 20

不要留坑,我经常看到一些复杂的代码,这些代码的作者写的时候明显知道自己在做什么,但是别人很难维护和看懂. 所以我对自己的职业的基本要求就是:那天我离职了,后来接手的人不会经常骂我

PEP20">"Readability counts." - PEP 20

Khan'sstyle-guides">"Anybody can fix anything." - Khan's style-guides

我现在更多不是代码炫技,我经常思考的怎么让最少的代码,最简单的设计结构满足当前需求,也能给未来一段时间里也有扩展性

Fix each broken window (bad design, wrong decision, or poor code) as soon as it is discovered.

我们改 bug 有个原则 - 测试要覆盖到出 bug 的地方。每个人内心都有很高的代码质量的要求

PEP20">"Now is better than never." - PEP 20

明日复明日,明日何其多。我们在代码 review 的时候,问题需要在提出的时候就去改,永远不会说下一次再说,因为下一次大多时候是没有下一次了

Test ruthlessly. Write docs for new features.

Even more important that Test-Driven Development--Human-Driven Development

一些细节

PEP8

很多人是排斥的,假如你想让未来部门有自己的风格,习惯。让新人马上上手接受,PEP8 是一个非常明智的选择

文件开头

新的文件的开头需要加一些 docstring. 描述文件的作用,编辑者,修改原因和日期等帮助阅读者的描述.

不要添加#!/usr/bin/python(除非这个文件未来是一个可执行的文件), copyright,__author__或者其他内容.

第一行建议添加# coding-utf-8

命名

  1. Variables, functions, methods, packages, modules

    lower_case_with_underscores

  2. Classes and Exceptions

    CapWords

  3. Protected methods and internal functions

    _single_leading_underscore(self, ...)

  4. Private methods

    __double_leading_underscore(self, ...)

  5. Constants

    ALL_CAPS_WITH_UNDERSCORES

  6. Avoid one-letter variables (esp. l, O, I).

    永远不要使用没有意义的单字符作为变量名

PS: 这点可以折中,假如一个代码块代码逻辑很清晰,而 这个短的便令也只是过程中的一个间接变量之类的情况下是可以接受的

Good or Bad

列举一些正确和错误的用法.

Avoid redundant labeling.

# Good
import audio

core = audio.Core()
controller = audio.Controller()

# Bad
import audio

core = audio.AudioCore()
controller = audio.AudioController()

不要使用重复意义的标签

Prefer "reverse notation".

# Good
elements = ...
elements_active = ...
elements_defunct = ...

# Bad
elements = ...
active_elements = ...
defunct_elements ...

Avoid getter and setter methods.

# Good
person.age = 42

# Bad
person.set_age(42)

Indentation

永远不要 Tab 和空格混用。使用 4 个空格作为 python 缩进

Imports

Import entire modules instead of individual symbols within a module.

PS: 这个时候可以参考 tornado 的代码用法.

比如现在有这样一个包

$ tree
└── canteen
    ├── __init__.py
    ├── sessions.py
# Good
import canteen
import canteen.sessions
from canteen import sessions

# Bad
from canteen import get_user  # Symbol from canteen/__init__.py
from canteen.sessions import get_session  # Symbol from canteen/sessions.py

PS: 除非这个第三方模块的文档显式的要求这些写

Splitting tricky lines

# Bad:
badge_name = badges.topic_exercise_badges.TopicExerciseBadge.name_for_topic_key_name(self.key().name())
# Good:
badge_name = (badges.topic_exercise_badges.TopicExerciseBadge
              .name_for_topic_key_name(self.key().name()))

# Bad:
self.redirect("/class_profile?selected_graph_type=%s&coach_email=%s&graph_query_params=%s" %
    (self.GRAPH_TYPE, urllib.quote(coach.email), urllib.quote(urllib.quote(self.request.query_string))))
# Good:
self.redirect(
    "/class_profile?selected_graph_type=%s&coach_email=%s"
    "&graph_query_params=%s" % (
        self.GRAPH_TYPE,
        urllib.quote(coach.email),
        urllib.quote(urllib.quote(self.request.query_string))))

我添加的规则

from ... import ...

# Bad
from aa import alonglonglonglonglong, alonglonglonglonglonglonglonglonglong, \
               alonglonglonglonglonglong

# Good
from aa import (alonglonglonglonglong, alonglonglonglonglonglonglonglonglong,
                alonglonglonglonglonglong)
from aa import (alonglonglonglonglong, alonglonglonglonglonglonglonglonglong,
    alonglonglonglonglonglong, alonglonglonglonglonglonglong,
    alonglonglonglonglonglong2)  # Good。 当引入的函数/类/变量很多时, 也可以选择空 4 个空格的方式, 而不需要和首行的左括号后对齐

相对引用 (relative import) 和 绝对引用 (absolute import)

$cat xx/models/user/consts.py  # 如果想引用这个变量
TMP = 1

$cat xx/views/user.py
from xx.models.user.consts import TMP  # recommended。

# 假如模块层级>=3。 比如 xx/models/user/consts.py 就在根目录下的第 3 级。 如果其他当前目录下源文件需要调用它的内容。 也可以选择相对引用
$cat xx/models/user/main.py  # 需要和 consts.py 在一个目录下才可以
from consts import TMP  # Bad
from .consts import TMP  # Good

# 而且只能在包内相对引用。 包外都需要绝对引用