第三章:组织你的代码
来源:互联网 发布:石墨文档 mac 编辑:程序博客网 时间:2024/05/22 04:28
3.1 模块和包
3.1.1 使用模块进行封装而不是像其它语言一样使用对象
虽然Python支持面向对象编程,但这不是必须的。很多有经验的Python程序员相对较少的使用类和多态机制。主要有几个原因:
类中大部分数据用列表,字典,和集合存储。Python有更多种类的内建函数和标准库模块对数据交互做了很多优化。一个令人信服的理由,只有在需要的时候类才被使用,几乎不会在API的边界。
在Java中类是基本的封装单元。一个文件代表一个Java类,不管对于解决手边的问题是否是有帮助的。如果我有几个工具函数,把他们封装在Utility类中。如果我们不能直观理解Utility对象意味着什么,没关系。当然,我有点夸张,道理是显然的。一旦一个人强制将所有的东西都变为一个类,将这种符号延伸到其它编程语言是容易的。
在Python中,一组相关的函数和数据很自然的被封装进模块。如果我正在使用MVC框架构建”Chirp”, 我可以用一个名为chirp的包,包含model, view, controller模块。如果”Chirp”是一个野心勃勃的项目,代码量很大,这些模块自己可以很容易的变成包。Controller包可以有一个persistence模块和一个processing模块。包中的任何东西都不相关,除了一个直观的感觉它们应该属于controller。
如果所有的模块都变成类,类与类之间的交互立刻变成一个问题。我们需要小心谨慎的决定方法是否应该被设置为public,状态怎么更新,我们的类支持测试的方式。取代列表和字典,我们有processing和persistence对象,我们必须写代码提供支持。
注意,没有任何描述信息表明”Chirp”应该使用类。简单的导入语句使代码的封装和共享更简单。作为函数参数显式传递状态,使事情变的松耦合。通过我们的系统获取,处理,转换数据流将更简单。
不可否认,类是一种更整洁和自然的代表某些事物的方式。在很多情况下,面向对象编程是一种很方便的范式。但是,别让它成为你可以使用的唯一范式。
3.2 格式化
3.2.1 全局常量全部使用大写字母
为了把定义在模块级别的常量和导入程序中的名字区分开,常量所有字母都为大写。
3.2.1.1 糟糕 的写法
seconds_in_a_day = 60 * 60 * 24 # ... def display_uptime(uptime_in_seconds): percentage_run_time = ( uptime_in_seconds/seconds_in_a_day) * 100 # "Huh!? Where did seconds_in_a_day come from?" return 'The process was up {percent} percent of the day'.format( percent=int(percentage_run_time)) # ... uptime_in_seconds = 60 * 60 * 24 display_uptime(uptime_in_seconds)
3.2.1.2 地道的表达
SECONDS_IN_A_DAY = 60 * 60 * 24 # ... def display_uptime(uptime_in_seconds): percentage_run_time = ( uptime_in_seconds/SECONDS_IN_A_DAY) * 100 # "Clearly SECONDS_IN_A_DAY is a constant defined return 'The process was up {percent} percent of the day'.format( percent=int(percentage_run_time)) # ... uptime_in_seconds = 60 * 60 * 24 display_uptime(uptime_in_seconds)
3.2.2 避免在一行放置多个语句
尽管语言定义允许我们这样做,但是这样写会让代码变的很难阅读。当像if, else, elif这样的语句出现在同一行,这种情况更令人困惑。
3.2.2.1 糟糕的写法
if this_is_bad_code: rewrite_code(); make_it_more_readable();
3.2.2.2 地道的表达
if this_is_bad_code: rewrite_code() make_it_more_readable()
3.2.3 根据PEP8格式化你的代码
Python定义了一个标准的化格式化规则的集合,被称为PEP8.如果你浏览Python项目的提交信息,你会发现到处都会提及PEP8整洁。原因很简单:如果我们商定一套共同的命名和格式化习惯约定,无论是对初学者还是经验丰富的开发者,python代码都将立刻变的更容易使用理解。PEP8应该是Python社区里关于地道代码最明显的例子。读PEP,在你的编辑器里安装一个PEP8风格检查插件,开始用这种风格写你的代码,其它开发者会很感激。下面列出一些例子。
没有列出来的几乎所有的事情都遵循变量/函数命名习惯“字母用下划线连接”。
3.3 可执行脚本
3.3.1 在你的脚本里使用sys.exit返回恰当的错误代码
Python脚本应该是好的shell公民。在if__name__ == ‘__main__’语句后添加大量代码而不返回任何事情是有诱惑的。避免这种倾向。
创建一个main函数,包含将要作为脚本运行的代码。在main函数中使用sys.exit返回错误代码,当有些东西发生错误,或者所有东西运行完返回0.在if __name == ‘main’语句中唯一应该被调用的代码是sys.exit以参数形式返回main函数中的返回值。
这样做,允许脚本在Unix的管道中使用,被用来监控失败不需要客户规则,也可以被其它程序安全的调用。
3.3.3.1 糟糕的写法
if __name__ == '__main__': import sys # What happens if no argument is passed on the if len(sys.argv) > 1: argument = sys.argv[1] result = do_stuff(argument) # Again, what if this is False? How would other if result: do_stuff_with_result(result)
3.3.3.2 地道的表达
def main(): import sys if len(sys.argv) < 2: # Calling sys.exit with a string automatically # prints the string to stderr and exits with sys.exit('You forgot to pass an argument') argument = sys.argv[1] if not result: sys.exit(1) do_stuff_with_result(result) # Optional, since the return value without this return # statment would default to None, which sys.exit treats return 0 # The three lines below are the canonical script entry # point lines. You'll see them often in other Python scripts if __name__ == '__main__': sys.exit(main())
3.3.2 使用if __name__ == ‘__main__’使一个文件被导入后直接运行
不像其它语言中的main()函数,Python没有内建的main入口。相反,加载一个Python源文件之后,python解释器直接开始执行代码。如果你想一个文件即作为可导入的文件又作为独立的脚本,使用if __name__ == ‘main’风格。
3..3.2.1 糟糕的写法
import sys import os FIRST_NUMBER = float(sys.argv[1]) SECOND_NUMBER = float(sys.argv[2]) def divide(a, b): return a/b # I can't import this file (for the super # useful 'divide' function) without the following # code being executed. if SECOND_NUMBER != 0: print(divide(FIRST_NUMBER, SECOND_NUMBER))
3..3.2.2 地道的表达
import sys import os def divide(a, b): return a/b # Will only run if script is executed directly, # not when the file is imported as a module if __name__ == '__main__': first_number = float(sys.argv[1]) second_number = float(sys.argv[2]) print(divide(first_number, second_number))
3.4 导入
3.4.1 优先使用绝对导入而不是相对导入
当需要导入模块时,有两种导入风格可以使用:绝对导入和相对导入。绝对导入从sys.path可以找到的路径中明确模块的位置。绝对导入明确致命模块的路径,并且该路径是sys.path可以找到的。
相对导入明确要导入模块与当前模块在文件系统中的相对位置。如果你正在创建名为package.sub_package.module的模块,需要导入package.orther_module,你可以使用由点表示的相对路径from ..orther_module import foo.一个.代表包含这个模块的当前包。每一个额外的.被用来表示“XX的父包”。相对导入必须使用from … import … 风格。Import foo 被视为绝对导入。
另一种绝对导入的写法:import package.orther_module
为什么我们要优先使用绝对导入而不是相对导入呢?相对导入弄乱的模块的命名空间。From foo import bar,你绑定了bar在你模块的命名空间。对那些读你代码的人,不是很清楚bar从哪里来,尤其在一个复杂的函数或者模块中使用。Foo.bar使更清晰的明白bar在哪里定义。
3.4.1.1 糟糕的写法
# My location is package.sub_package.module # and I want to import package.other_module. # The following should be avoided: from ...package import other_module
3.4.1.2 地道的表达
# My location is package.sub_package.another_sub_package.module # and I want to import package.other_module. # Either of the following are acceptable: import package.other_module import package.other_module as other
3.4.2 不要使用from foo import * 导入一个模块的上下文
考虑到上一条风格,这个是显而易见的。再倒入语句中使用*很容易弄乱你的命名空间。如果你自己定义的名字和包中定义的名字有冲突的话,这也可能导致问题。
如果你必须从foo包中导入一些名字呢?确保在导入语句中用小括号分组。你不必在同一个模块中写10行导入语句,你的命名空间人保持相对干净。
更好的是,简单使用绝对导入。如果报名或者模块名字太长,使用as语句简短表示。
3.4.2.1 糟糕的写法
from foo import *
3.4.2.2 地道的表达
from foo import (bar, baz, qux, quux, quuux) # or even better... import foo
3.4.3 用标准顺序管理导入语句
随着项目的增长(尤其那些使用web框架)需要大量的导入语句。把所有导入语句放在每个文件的最上面,为导入语句选择一个标准的顺序,并坚持使用。虽然导入顺序不是特别的重要,下面是Python程序FAQ的建议:
- 标准库模块
- 安装在site-packages中的第三方库模块
- 位于当前项目的模块
很多人粗暴的按字符顺序组织导入。其它人认为这不重要。事实上,它没什么关系。关键是你要选择一个标准顺序,并坚持使用。
3.4.3.1 糟糕的写法
import os.path # Some function and class definitions, # one of which uses os.path # .... import concurrent.futures from flask import render_template # Stuff using futures and Flask's render_template # .... from flask import (Flask, request, session, g, redirect, url_for, abort, render_template, flash, _app_ctx_stack) import requests # Code using flask and requests # .... if __name__ == '__main__': # Imports when imported as a module are not so # costly that they need to be relegated to inside import this_project.utilities.sentient_network as skynet import sys
3.4.3.2 地道的表达
# Easy to see exactly what my dependencies are and where to # make changes if a module or package name changes import concurrent.futures import os.path import sys from flask import (Flask, request, session, g, redirect, url_for, abort, import requests import this_project.utilities.sentient_network as skynet import this_project.widgets
- 第三章:组织你的代码
- 【编写可读代码的艺术】第三部分 重新组织你的代码
- 如何组织你的javascript代码
- JavaScript代码重构系列-重新组织你的函数
- 第六章 重新组织你的函数
- 《编写可读性代码的艺术》读书笔记 第三部分 重新组织代码
- C++代码的组织
- 代码的物理组织
- 第三章:什么是组织结构
- 《管理的常识》第三章<什么是组织结构>读书笔记
- 第三章:真正弄清楚一个Mod的组织结构
- 组织你的Sass文件
- 悟透delphi 第七章 组织你的模块
- 悟透delphi 第七章 组织你的模块
- PHP 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数
- PHP 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数
- PHP:《重构-改善既有代码的设计》之一 重新组织你的函数
- Ruby 代码的组织方式
- stm32f407之CAN控制器(操作寄存器)
- STM32里CAN总线的问题
- 全面解析Linux 内核 3.10.x
- CAN总线波特率计算方法
- stm32之can总线过滤器研究
- 第三章:组织你的代码
- STM 32 之can 实例+代码解析
- #define和const的区别
- STM32F103的CAN 通信之通讯协议
- CAN波特率设置时的参数计算
- STM32的CAN总线使用指南
- STM32之CAN---配置波特率
- nginx接收包体处理
- CAN波特率计算的内幕