python项目结构
+--- LICENSE
+--- README.rst
+--- requirements.txt
+--- setup.py
+--- sample
| +--- core.py
| +--- helpers.py
| +--- __init__.py
+--- docs
| +--- conf.py
| +--- index.rst
+--- tests
| +--- test_advanced.py
| +--- test_basic.py
1 核心模块 sample 真正的代码
2 License 许可证
在这个文件中要有完整的许可说明和授权
3 Setup.py 打包和发布管理
4 Requirements File 开发依赖
当然可以通过 setup.py 来设置,这个文件不是必须的
5 Documentation docs 包的参考文档
6 Test Suite 包的集合和单元测试
7 Makefile 常规的管理任务
** 样例 Makefile:**
init:
pip install -r requirements.txt
test:
py.test tests
PHONY: init test
8 一些其他的常规管理脚本(比如 manage.py 或者 fabfile.py)也放在仓库的根目录下。
常见的一些注意点
0 正确的启动django项目
启动django 项目
django-admin.py startproject samplesite .
注意末尾的 "."
+--- manage.py
+--- samplesite
| +--- settings.py
| +--- urls.py
| +--- wsgi.py
| +--- __init__.py
1 结构化的重要性
拒绝混乱的循环依赖
避免隐含耦合
避免大量使用全局变量或上下文
拒绝面条型的代码 (大量复制-粘贴 的过程代码,且没有合适的分割)
减少出现混沌代码 (上百段相似的逻辑碎片,通常是缺乏 合适结构的类或对)
2 模块和包
模块名的定义:
最重要的是,不要使用下划线命名空间,而是使用子模块。
# OK
import library.plugin.foo
# not OK
import library.foo_plugin
模块的导入:
差
[...]
from modu import *
[...]
x = sqrt(4) # sqrt是模块modu的一部分么?或是内建函数么?上文定义了么?
稍好
from modu import sqrt
[...]
x = sqrt(4) # 如果在import语句与这条语句之间,sqrt没有被重复定义,它也许是模块modu的一部分。
最好的做法
import modu
[...]
x = modu.sqrt(4) # sqrt显然是属于模块modu的。
3 面向对象编程
把有隐式上下文和副作用的函数
与仅包含逻辑的函数(纯函数)
谨慎地区分开来
会带来以下好处:
纯函数的结果是确定的:给定一个输入,输出总是固定相同。
当需要重构或优化时,纯函数更易于更改或替换。
纯函数更容易做单元测试:很少需要复杂的上下文配置和之后的数据清除工作。
纯函数更容易操作、修饰和分发。
在很多情况下,面向对象编程
是有用甚至必要的 (有相对较长的生命周期)
例如图形桌面 应用或游戏的开发过程中,操作的元素(窗口、按钮、角色、车辆)在计算机内存里拥有相 对较长的生命周期。
4 装饰器
被 ‘装饰’ 的函数或方法会替换原来的函数或方法
手动装饰
def foo():
# 实现语句
def decorator(func):
# 操作func语句
return func
foo = decorator(foo) # 手动装饰
@decorator
def bar():
# 实现语句
# bar()被装饰了
5 上下文管理器 with .. as ..
类方法
class CustomOpen(object):
def __init__(self, filename):
self.file = open(filename)
def __enter__(self):
return self.file
def __exit__(self, ctx_type, ctx_value, ctx_traceback):
self.file.close()
with CustomOpen('file') as f:
contents = f.read()
首先被实例化,然后调用它的`__enter__``方法,而且 __enter__ 的返回值在 as f 语句中被赋给 f 。 当 with 块中的内容执行完后,会调用 __exit__` 方法。
python 自带的 contextlib
from contextlib import contextmanager
@contextmanager
def custom_open(filename):
f = open(filename)
try:
yield f
finally:
f.close()
with custom_open('file') as f:
contents = f.read()
6 动态类型
变量 ‘a’ 先代表值1,然后变成 字符串 ‘a string’ , 然后又变为指向一个函数。
避免对不同类型的对象使用同一个变量名
差
a = 1
a = 'a string'
def a():
pass # 实现代码
好
count = 1
msg = 'a string'
def func():
pass # 实现代码
使用简短的函数或方法能降低对不相关对象使用同一个名称的风险。即使是相关的不同 类型的对象,也更建议使用不同命名:
差
items = 'a b c d' # 首先指向字符串...
items = items.split(' ') # ...变为列表
items = set(items) # ...再变为集合
7 可变不可变类型
python 中字符串是一个不可变类型, 组合字符串实际都是在一个可变的列表中完成的
差
# 创建将0到19连接起来的字符串 (例 "012..1819")
nums = ""
for n in range(20):
nums += str(n) # 慢且低效
print nums
好
# 创建将0到19连接起来的字符串 (例 "012..1819")
nums = []
for n in range(20):
nums.append(str(n))
print "".join(nums) # 更高效
更好
# 创建将0到19连接起来的字符串 (例 "012..1819")
nums = [str(n) for n in range(20)]
print "".join(nums)
最好Best
# 创建将0到19连接起来的字符串 (例 "012..1819")
nums = map(str, range(20))
print "".join(nums)
字符串的拼接
foo = 'foo'
bar = 'bar'
foobar = '%s%s' % (foo, bar) # 可行
foobar = '{0}{1}'.format(foo, bar) # 更好
foobar = '{foo}{bar}'.format(foo=foo, bar=bar) # 最好
代码风格
1 明确的代码 明确和直接的编码方式
糟糕
def make_complex(*args):
x, y = args
return dict(**locals())
优雅
def make_complex(x, y):
return {'x': x, 'y': y}
2 每行一个声明
不推荐在同一行代码中写 两条独立的语句
糟糕
print 'one'; print 'two'
if x == 1: print 'one'
if <complex comparison> and <other complex comparison>:
# do something
优雅
print 'one'
print 'two'
if x == 1:
print 'one'
cond1 = <complex comparison>
cond2 = <other complex comparison>
if cond1 and cond2:
# do something
3 函数的参数
位置参数, 关键字参数, args*, **kwargs
4 返回值
当一个函数在其正常过程中有多个主要出口点时,它会变得难以调试和返回其结果, 所以保持单个出口点可能会更好。这也将有助于提取某些代码路径,而且多个出口点 很有可能意味着这里需要重构。
def complex_function(a, b, c):
if not a:
return None # 抛出一个异常可能会更好
if not b:
return None # 抛出一个异常可能会更好
# 一些复杂的代码试着用a,b,c来计算x
# 如果成功了,抵制住返回x的诱惑
if not x:
# 一些关于x的计算的Plan-B
return x # 返回值x只有一个出口点有利于维护代码
5 常见的pythonic
用语
解包
unpacking
a, *rest = [1, 2, 3]
# a = 1, rest = [2, 3]
a, *middle, c = [1, 2, 3, 4]
# a = 1, middle = [2, 3], c = 4
创建一个不需要的变量
__
比如,在 解包(Unpacking) )但不需要这个变量,请使用 __
filename = 'foobar.txt'
basename, __, ext = filename.rpartition('.')
注意:
"_" 常用在作为 gettext() 函数 的别名,也被用在交互式命令行中记录最后一次操作的值。相反,使用双下划线 十分清晰和方便,
而且能够消除使用其他这些用例所带来的意外干扰的风险。
PEP 8
维护列表的捷径
map()
和 filter()
函数使用一种不同但是更简洁的语法处理列表
赋值永远不会创建新对象
在迭代列表的过程中,永远不要从列表中移除元素
如果有其他变量引用原始列表,则修改它可能会有风险。但如果你真的想这样做,你可以使用 切片赋值(slice assignment) 。
# 修改原始列表的内容
sequence[::] = [value for value in sequence if value != x]
# 或者
sequence[::] = (value for value in sequence if value != x)
读取文件
with .. open ..
可以保证自动关闭文件
反斜杠\可以延续代码
更好的解决方案是在元素周围使用括号
糟糕:
my_very_big_string = """For a long time I used to go to bed early. Sometimes, \
when I had put out my candle, my eyes would close so quickly that I had not even \
time to say “I’m going to sleep.”"""
from some.deep.module.inside.a.module import a_nice_function, another_nice_function, \
yet_another_nice_function
优雅:
my_very_big_string = (
"For a long time I used to go to bed early. Sometimes, "
"when I had put out my candle, my eyes would close so quickly "
"that I had not even time to say “I’m going to sleep.”"
)
from some.deep.module.inside.a.module import (
a_nice_function, another_nice_function, yet_another_nice_function)