python项目结构

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)
Buy me a 肥仔水!