python
python基础
1.python 赋值、深拷贝、浅拷贝
赋值语法: =
浅拷贝语法: copy.copy()
深拷贝语法: copy.deepcopy()
浅层和深层复制操作
python深拷贝与浅拷贝
目录
Python读书笔记… 1
1. 总览… 3
2. 了解python基础知识… 3
2.1. Python底层是用什么语言实现的?… 3
2.2. Python关键字(保留字)一览表… 4
2.3. Python内置函数一览表… 5
2.4. Python三目运算符(三元运算符)用法详解… 5
2.5. Python 内置的四种常用数据结构:列表(list)、元组(tuple)、字典(dict)以及集合(set) 8
2.6. Python lambda表达式(匿名函数)及用法… 8
2.7. Python面向对象编程… 12
2.8. Python异常处理机制… 13
2.9. Python 上下文管理器… 13
2.10. Python线程池及其原理和应用… 15
2.11. Python网络编程… 18
3. Python编码规范… 19
3.1. 行长度… 19
3.2. 括号… 19
3.3. 缩进… 19
3.4. 注释… 19
3.5. 类… 19
3.6. TODO注释… 19
3.7. 命名… 19
3.8. Main. 19
4. 了解设计模式(基于python实现)… 19
4.1. 概览… 19
4.2. 创建型… 19
4.2.1. Factory Method(工厂方法)… 19
4.2.2. Abstract Factory(抽象工厂)… 19
4.2.3. Builder(建造者)… 19
4.2.4. Singleton(单例)… 19
4.3. 结构型… 19
4.3.1. Proxy(代理)… 19
4.4. 行为型… 19
4.4.1. Template Method模板方法… 19
4.4.2. Command(命令)… 19
5. 了解代码重构… 19
5.1. 引言… 19
5.2. 为何重构… 20
5.3. 代码的坏味道… 22
5.4. 构筑测试体系… 22
6. 了解Django. 22
6.1. 这份文档是如何组织的¶. 22
6.2. 快速入门¶. 23
6.3. 模型层¶. 23
6.4. 视图层¶. 23
6.5. 模板层¶. 24
6.6. 表单¶. 24
6.7. 开发进程¶. 24
6.8. 管理¶. 25
6.9. 安全¶. 25
6.10. 国际化和本地化¶. 25
6.11. 性能和优化¶. 25
6.12. 地理框架¶. 26
6.13. 常用的 Web 应用程序工具¶. 26
6.14. 其它核心功能¶. 26
6.15. Django开源项目¶. 27
Python编程基础
Python编程规范
设计模式(基于python实现)
重构,改善既有代码的设计
Python web 应用框架: Django
确切地说,本教程介绍的是用 C 语言编写实现的 Python,又称为 CPython。平时我们所讨论的 Python,指的其实就是 CPython。
随着编程语言的不断发展,Python 的实现方式也发生了变化,除了用 C 语言实现外,Python 还有其他的实现方式。例如,用 Java 语言实现的 Python 称为 JPython,用 .net 实现的 Python 称为 IronPython 等等。
Python 的这些实现方式虽然诞生比 CPython 晚,但一直在努力地跟上主流,并在不同的生产环境中不断地使用并推广 Python。
Python 的实现方式有很多种,Python 官网上介绍了 20 多种语言变体、方言或 C 语言之外的 Python 解释器实现。其中一些只是实现了语言核心语法、功能和内置扩展的一个子集,但至少有几个与 CPython 几乎完全兼容。更重要的是,在这些不同的实现方式中,虽然有些知识玩具项目或实验,但大部分都是为了解决某些实际问题而创建的,这些问题要么使用 CPython 无法解决,要么需要开发人员花费巨大的精力,这里举几个例子:
l 在嵌入式系统中运行 Python 代码。
l 与运行框架(如 Java 或 .NET)或其他语言做代码集成。
l 在 Web 浏览器中运行 Python 代码。
Python 可通过 if 语句来实现三目运算符的功能,因此可以近似地把这种 if 语句当成三目运算符。作为三目运算符的 if 语句的语法格式如下:
True_statements if expression else False_statements
三目运算符的规则是:先对逻辑表达式 expression 求值,如果逻辑表达式返回 True,则执行并返回 True_statements 的值;如果逻辑表达式返回 False,则执行并返回 False_statements 的值。看如下代码:
a = 5
b = 3
st = “a大于b” if a > b else “a不大于b”
print(st)
实际上,如果只是为了在控制台输出提示信息,还可以将上面的三目运算符表达式改为如下形式:
print(“a大于b”) if a > b else print(“a不大于b”)
Python 允许在三目运算符的 True_statements 或 False_statements 中放置多条语句。Python 主要支持以下两种放置方式:
多条语句以英文逗号隔开:每条语句都会执行,程序返回多条语句的返回值组成的元组。
多条语句以英文分号隔开:每条语句都会执行,程序只返回第一条语句的返回值。
先看第一种情形,使用如下代码:
st = print(“crazyit”), ‘a大于b’ if a > b else “a不大于b”
print(st)
上面程序中 True_statements 为 print(“crazyit”),‘a大于b’,这两条语句都会执行,程序将会返回这两条语句的返回值组成的元组。由于 print() 函数没有返回值,相当于它的返回值是 None。运行上面代码,将看到如下结果:
crazyit
(None,‘a大于b’)
如果将上面语句中的逗号改为分号,将逗号之后的语句改为赋值语句,即写成如下形式:
st = print(“crazyit”); x = 20 if a > b else “a不大于b”
print(st)
print(x)
此时虽然 True_statements 包含两条语句,但程序只会返回第一条语句 print(”crazyit”) 的返回值,该语句同样返回 None,因此相当于 str 的返回值为 None。运行上面代码,将看到如下结果:
crazyit
None
20
需要指出的是,三目运算符支持嵌套,通过嵌套三目运算符,可以执行更复杂的判断。例如,下面代码需要判断 c、d 两个变量的大小关系:
c = 5
d = 5
print(“c大于d”) if c > d else (print(“c小于d”) if c < d else print(“c等于d”))
上面代码首先对 c>d 求值,如果该表达式为 True,程序将会执行并返回第一个表达式:print(”c大于d”);否则系统将会计算 else 后面的内容:(print(“c小于d”) if c < d else print(“c等于d”)),这个表达式又是一个嵌套的三目运算符表达式。注意,进入该表达式时只剩下“c小于d”或“c等于d”两种情况,因此该三目运算符再次判断 c,如果该表达式为 True,将会输出“c小于d”;否则只剩下“c等于d”一种情况,自然就输出该字符串了。
这四种数据结构一但都可用于保存多个数据项,这对于编程而言是非常重要的,因为程序不仅需要使用单个变量来保存数据,还需要使用多种数据结构来保存大量数据,而列表、元组、字典和集合就可满足保存大量数据的需求。
列表(list)和元组(tuple)比较相似,它们都按顺序保存元素,每个元素都有自己的索引,因此列表和元组都可通过索引访问元素。二者的区别在于元组是不可修改的,但列表是可修改的。
字典(dict)和集合(set)类似,它们存储的数据都是无序的,其中字典是用 key-value 的形式保存数据。
lambda 表达式(又称匿名函数)是现代编程语言争相引入的一种语法,如果说函数是命名的、方便复用的代码块,那么 lambda 表达式则是功能更灵活的代码块,它可以在程序中被传递和调用。
回顾局部函数
回顾《Python函数高级用法》一节中,get_math_func() 函数将返回三个局部函数之一。该函数代码如下:
def get_math_func(type) :
# 定义三个局部函数…
# 返回局部函数
if type == “square” :
return square
if type == “cube” :
return cube
else:
return factorial
由于局部函数的作用域默认仅停留在其封闭函数之内,因此这三个局部函数的函数名的作用太有限了,即仅仅是在程序的 if 语句中作为返回值使用。一旦离开了 get_math_func() 函数体,这三个局部函数的函数名就失去了意义。
既然局部函数的函数名没有太大的意义,那么就考虑使用 lambda 表达式来简化局部函数的写法。
使用 lambda 表达式代替局部函数
如果使用 lambda 表达式来简化 get_math_func() 函数,则可以将程序改写成如下形式:
def get_math_func(type) :
result=1
# 该函数返回的是Lambda表达式
**if** type == 'square':
**return** **lambda** n: n * n# ①
**elif** type == 'cube':
**return** **lambda** n: n * n * n# ②
**else**:
**return** **lambda** n: (1 + n) * n / 2 # ③
math_func = get_math_func(“cube”)
print(math_func(5)) # 输出125
math_func = get_math_func(“square”)
print(math_func(5)) # 输出25
math_func = get_math_func(“other”)
print(math_func(5)) # 输出15.0
在上面代码中,return 后面的部分使用 lambda 关键字定义的就是 lambda 表达式,Python 要求 lambda 表达式只能是单行表达式。
注意:由于 lambda 表达式只能是单行表达式,不允许使用更复杂的函数形式,因此上面 ③ 号代码处改为计算 1+2+3+…+n 的总和。
lambda 表达式的语法格式如下:
lambda [parameter_list] : 表达式
从上面的语法格式可以看出 lambda 表达式的几个要点:
· lambda 表达式必须使用 lambda 关键字定义。
· 在 lambda 关键字之后、冒号左边的是参数列表,可以没有参数,也可以有多个参数。如果有多个参数,则需要用逗号隔开,冒号右边是该 lambda 表达式的返回值。
实际上,lambda 表达式的本质就是匿名的、单行函数体的函数。因此,lambda 表达式可以写成函数的形式。
例如,对于如下 lambda 表达式:
lambda x , y:x + y
可改写为如下函数形式:
def add(x, y):
return x+ y
上面定义函数时使用了简化语法:当函数体只有一行代码时,可以直接把函数体的代码放在与函数头同一行。
总体来说,函数比 lambda 表达式的适应性更强,lambda 表达式只能创建简单的函数对象(它只适合函数体为单行的情形)。但 lambda 表达式依然有如下两个用途:
· 对于单行函数,使用 lambda 表达式可以省去定义函数的过程,让代码更加简洁。
· 对于不需要多次复用的函数,使用 lambda 表达式可以在用完之后立即释放,提高了性能。
下面代码示范了通过 lambda 表达式来调用 Python 内置的 map() 函数:
x = map(lambda x: x*x , range(8))
print([e for e in x]) # [0, 1, 4, 9, 16, 25, 36, 49]
y = map(lambda x: x*x if x % 2 == 0 else 0, range(8))
print([e for e in y]) # [0, 0, 4, 0, 16, 0, 36, 0]
正如从上面代码所看到的,内置的 map() 函数的第一个参数需要传入函数,此处传入了函数的简化形式:lambda 表达式,这样程序更加简洁,而且性能更好。
总结
本节所介绍的 lambda 表达式是 Python 编程的核心机制之一。Python 语言既支持面向过程编程,也支持面向对象编程。而 lambda 表达式是 Python 面向过程编程的语法基础,因此读者必须引起重视。
Python 的 lambda 表达式只是单行函数的简化版本,因此 lambda 表达式的功能比较简单。
Python 就被设计成支持面向对象的编程语言,因此 Python 完全能以面向对象的方式编程。而且 Python 的面向对象比较简单,它不像其他面向对象语言提供了大量繁杂的面向对象特征,它致力于提供简单、够用的语法功能。
正因为如此,在 Python 中创建一个类和对象都很容易。Python 支持面向对象的三大特征:封装、继承和多态,子类继承父类同样可以继承到父类的变量和方法。
异常机制已经成为判断一门编程语言是否成熟的标准,除传统的像 C 语言没有提供异常机制之外,目前主流的编程语言如 Python、 Java、 Kotlin 等都提供了成熟的异常机制。
异常机制可以使程序中的异常处理代码和正常业务代间分离,保证程序代码更加优雅,并可以提高程序的健壮性。
Python 的异常机制主要依赖 try 、except 、else、finally 和 raise 五个关键字:
· try 关键字后缩进的代码块简称 try 块,它里面放置的是可能引发异常的代码;
· 在 except 后对应的是异常类型和一个代码块,用于表明该 except 块处理这种类型的代码块;
· 在多个 except 块之后可以放一个 else 块,表明程序不出现异常时还要执行 else 块;
· 最后还可以跟一个 finally 块,finally 块用于回收在 try 块里打开的物理资源,异常机制会保证 finally 块总被执行;
· raise 用于引发一个实际的异常,raise 可以单独作为语句使用,引发一个具体的异常对象;
在介绍 with as 语句时讲到,该语句操作的对象必须是上下文管理器。那么,到底什么是上下文管理器呢?
简单的理解,同时包含 enter() 和 exit() 方法的对象就是上下文管理器。也就是说,上下文管理器必须实现如下两个方法:
enter(self):进入上下文管理器自动调用的方法,该方法会在 with as 代码块执行之前执行。如果 with 语句有 as子句,那么该方法的返回值会被赋值给 as 子句后的变量;该方法可以返回多个值,因此在 as 子句后面也可以指定多个变量(多个变量必须由“()”括起来组成元组)。
exit(self, exc_type, exc_value, exc_traceback):退出上下文管理器自动调用的方法。该方法会在 with as 代码块执行之后执行。如果 with as 代码块成功执行结束,程序自动调用该方法,调用该方法的三个参数都为 None:如果 with as 代码块因为异常而中止,程序也自动调用该方法,使用 sys.exc_info 得到的异常信息将作为调用该方法的参数。
当 with as 操作上下文管理器时,就会在执行语句体之前,先执行上下文管理器的 enter() 方法,然后再执行语句体,最后执行 exit() 方法。
构建上下文管理器,常见的有 2 种方式:基于类实现和基于生成器实现。
通过上面的介绍不难发现,只要一个类实现了 enter() 和 exit() 这 2 个方法,程序就可以使用 with as 语句来管理它,通过 exit() 方法的参数,即可判断出 with 代码块执行时是否遇到了异常。其实,上面程序中的文件对象也实现了这两个方法,因此可以接受 with as 语句的管理。
系统启动一个新线程的成本是比较高的,因为它涉及与操作系统的交互。在这种情形下,使用线程池可以很好地提升性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池。
线程池在系统启动时即创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它。当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,等待执行下一个函数。
此外,使用线程池可以有效地控制系统中并发线程的数量。当系统中包含有大量的并发线程时,会导致系统性能急剧下降,甚至导致 Python 解释器崩溃,而线程池的最大线程数参数可以控制系统中并发线程的数量不超过此数。
线程池的使用
线程池的基类是 concurrent.futures 模块中的 Executor,Executor 提供了两个子类,即 ThreadPoolExecutor 和 ProcessPoolExecutor,其中 ThreadPoolExecutor 用于创建线程池,而 ProcessPoolExecutor 用于创建进程池。
如果使用线程池/进程池来管理并发编程,那么只要将相应的 task 函数提交给线程池/进程池,剩下的事情就由线程池/进程池来搞定。
Exectuor 提供了如下常用方法:
· submit(fn, *args, **kwargs):将 fn 函数提交给线程池。*args 代表传给 fn 函数的参数,*kwargs 代表以关键字参数的形式为 fn 函数传入参数。
· map(func, *iterables, timeout=None, chunksize=1):该函数类似于全局函数 map(func, *iterables),只是该函数将会启动多个线程,以异步方式立即对 iterables 执行 map 处理。
· shutdown(wait=True):关闭线程池。
程序将 task 函数提交(submit)给线程池后,submit 方法会返回一个 Future 对象,Future 类主要用于获取线程任务函数的返回值。由于线程任务会在新线程中以异步方式执行,因此,线程执行的函数相当于一个“将来完成”的任务,所以 Python 使用 Future 来代表。
实际上,在 Java 的多线程编程中同样有 Future,此处的 Future 与 Java 的 Future 大同小异。
Future 提供了如下方法:
· cancel():取消该 Future 代表的线程任务。如果该任务正在执行,不可取消,则该方法返回 False;否则,程序会取消该任务,并返回 True。
· cancelled():返回 Future 代表的线程任务是否被成功取消。
· running():如果该 Future 代表的线程任务正在执行、不可被取消,该方法返回 True。
· done():如果该 Funture 代表的线程任务被成功取消或执行完成,则该方法返回 True。
· result(timeout=None):获取该 Future 代表的线程任务最后返回的结果。如果 Future 代表的线程任务还未完成,该方法将会阻塞当前线程,其中 timeout 参数指定最多阻塞多少秒。
· exception(timeout=None):获取该 Future 代表的线程任务所引发的异常。如果该任务成功完成,没有异常,则该方法返回 None。
· add_done_callback(fn):为该 Future 代表的线程任务注册一个“回调函数”,当该任务成功完成时,程序会自动触发该 fn 函数。
在用完一个线程池后,应该调用该线程池的 shutdown() 方法,该方法将启动线程池的关闭序列。调用 shutdown() 方法后的线程池不再接收新任务,但会将以前所有的已提交任务执行完成。当线程池中的所有任务都执行完成后,该线程池中的所有线程都会死亡。
使用线程池来执行线程任务的步骤如下:
调用 ThreadPoolExecutor 类的构造器创建一个线程池。
定义一个普通函数作为线程任务。
调用 ThreadPoolExecutor 对象的 submit() 方法来提交线程任务。
当不想提交任何任务时,调用 ThreadPoolExecutor 对象的 shutdown() 方法来关闭线程池。
根据前面对网络分层棋型的介绍,我们知道实际的网络模型大致分为四层,这四层各有对应的网络协议提供支持,如图 1 所示。
图 1 四层网络模型及对应的协议
网络层协议主要是 IP,它是所有互联网协议的基础,其中 ICMP(Internet Control Message Protocol)、IGMP(Internet Group Manage Protocol)、ARP(Address Resolution Protocol)、RARP(Reverse Address Resolution Protocol)等协议都可认为是 IP 协议族的子协议。通常来说,很少会直接基于网络层进行应用程序编程。
传输层协议主要是 TCP 和 UDP,Python 提供了 socket 等模块针对传输层协议进行编程。
应用层协议就更多了,正如图 1 所示的,FTP、HTTP、TELNET 等协议都属于应用层协议,Python 同样为基于应用层协议的编程提供了丰富的支持。
虽然 Python 自带的标准库已经提供了很多与网络有关的模块,但如果在使用时觉得不够方便,则不要忘记了 Python 的优势,即大量的第三方模块随时可用于增强 Python 的功能。
表 2 显示了 Python 标准库中的网络相关模块。
|
表 2 Python 标准库中的网络相关模块
|
|
模块
|
描述
|
|
socket
|
基于传输层 TCP、UDP 协议进行网络编程的模块
|
|
asyncore
|
socket 模块的异步版,支持基于传输层协议的异步通信
|
|
asynchat
|
asyncore 的增强版
|
|
cgi
|
基本的 CGI(Common Gateway Interface,早期开发动态网站的技术)支持
|
|
|
E-mail 和 MLME 消息处理模块
|
|
ftplib
|
支持 FTP 协议的客户端模块
|
|
httplib、http.client
|
支持 HTTP 协议以及 HTTP 客户揣的模块
|
|
imaplib
|
支持 IMAP4 协议的客户端模块
|
|
mailbox
|
操作不同格式邮箱的模块
|
|
mailcap
|
支持 Mailcap 文件处理的模块
|
|
nntplib
|
支持 NTTP 协议的客户端模块
|
|
smtplib
|
支持 SMTP 协议(发送邮件)的客户端模块
|
|
poplib
|
支持 POP3 协议的客户端模块
|
|
telnetlib
|
支持TELNET 协议的客户端模块
|
|
urllib及其子模块
|
支持URL 处理的模块
|
|
xmlrpc、xmlrpc.server、xmlrpc.client
|
支持XML-RPC协议的服务器端和客户端模块
|
每行不超过80个字符
以下情况除外:
长的导入模块语句
注释里的URL
不要使用反斜杠连接行。
Python会将 圆括号, 中括号和花括号中的行隐式的连接起来, 你可以利用这个特点. 如果需要, 你可以在表达式外围增加一对额外的圆括号。
推荐: foo_bar(self, width, height, color=‘black’, design=None, x=‘foo’,
emphasis=None, highlight=0)
if (width == 0 and height == 0 and
color == ‘red’ and emphasis == ‘strong’):
如果一个文本字符串在一行放不下, 可以使用圆括号来实现隐式行连接:
x = ('这是一个非常长非常长非常长非常长 ’
‘非常长非常长非常长非常长非常长非常长的字符串’)
在注释中,如果必要,将长的URL放在一行上。
Yes: # See details at
No: # See details at
注意上面例子中的元素缩进; 你可以在本文的 :ref:缩进
部分找到解释.
宁缺毋滥的使用括号
除非是用于实现行连接, 否则不要在返回语句或条件语句中使用括号. 不过在元组两边使用括号是可以的.
Yes: if foo:
bar()
while x:
x = bar() if x and y:
bar()
if not x:
bar()
return foo
for (x, y) in dict.items(): …
No: if (x):
bar()
if not(x):
bar()
return (foo)
用4个空格来缩进代码
绝对不要用tab, 也不要tab和空格混用. 对于行连接的情况, 你应该要么垂直对齐换行的元素(见 :ref:行长度
部分的示例), 或者使用4空格的悬挂式缩进(这时第一行不应该有参数):
Yes: # 与起始变量对齐
foo = long_function_name(var_one, var_two,
var_three, var_four)
foo = {
long_dictionary_key: value1 +
value2,
…
}
foo = long_function_name(
var_one, var_two, var_three,
var_four)
foo = {
long_dictionary_key:
long_dictionary_value,
…
}
No: # 第一行有空格是禁止的
foo = long_function_name(var_one, var_two,
var_three, var_four)
foo = long_function_name(
var_one, var_two, var_three,
var_four)
foo = {
long_dictionary_key:
long_dictionary_value,
…
}
确保对模块, 函数, 方法和行内注释使用正确的风格
文档字符串
Python__有一种独一无二的的注释方式_: 使用文档字符串. 文档字符串是包, 模块, 类或函数里的第一个语句. 这些字符串可以通过对象的____doc____成员被自动提取, 并且被__pydoc__所用. (你可以在你的模块上运行__pydoc__试一把, 看看它长什么样). 我们对文档字符串的惯例是使用三重双引号"""( PEP-257). 一个文档字符串应该这样组织: 首先是一行以句号, 问号或惊叹号结尾的概述(或者该文档字符串单纯只有一行). 接着是一个空行. 接着是文档字符串剩下的部分, 它应该与文档字符串的第一行的第一个引号对齐. 下面有更多文档字符串的格式化规范._
模块
每个文件应该包含一个许可样板_. 根据项目使用的许可(例如, Apache 2.0, BSD, LGPL, GPL), 选择合适的样板._
函数和方法
下文所指的函数_,包括函数, 方法, 以及生成器._
一个函数必须要有文档字符串_, 除非它满足以下条件:_
外部不可见
非常短小
简单明了
文档字符串应该包含函数做什么_, 以及输入和输出的详细描述. 通常, 不应该描述"怎么做", 除非是一些复杂的算法. 文档字符串应该提供足够的信息, 当别人编写代码调用该函数时, 他不需要看一行代码, 只要看文档字符串就可以了. 对于复杂的代码, 在代码旁边加注释会比使用文档字符串更有意义._
关于函数的几个方面应该在特定的小节中进行描述记录,_ 这几个方面如下文所述. 每节应该以一个标题行开始. 标题行以冒号结尾. 除标题行外, 节的其他内容应被缩进__2__个空格._
Args:
列出每个参数的名字, 并在名字后使用一个冒号和一个空格, 分隔对该参数的描述.如果描述太长超过了单行80字符,使用2或者4个空格的悬挂缩进(与文件其他部分保持一致). 描述应该包括所需的类型和含义. 如果一个函数接受foo(可变长度参数列表)或者**bar (任意关键字参数), 应该详细列出foo和**bar.
Returns: (或者 Yields: 用于生成器)
描述返回值的类型和语义. 如果函数返回None, 这一部分可以省略.
Raises:
列出与接口有关的所有异常.
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
“”"Fetches rows from a Bigtable.
Retrieves rows pertaining to the given keys from the Table instance
represented by big_table. Silly things may happen if
other_silly_variable is not None.
Args:
big_table: An open Bigtable Table instance.
keys: A sequence of strings representing the key of each table row
to fetch.
other_silly_variable: Another optional variable, that has a much
longer name than the other args, and which does nothing.
Returns:
A dict mapping keys to the corresponding table row data
fetched. Each row is represented as a tuple of strings. For
example:
{‘Serak’: (‘Rigel VII’, ‘Preparer’),
‘Zim’: (‘Irk’, ‘Invader’),
‘Lrrr’: (‘Omicron Persei 8’, ‘Emperor’)}
If a key from the keys argument is missing from the dictionary,
then that row was not found in the table.
Raises:
IOError: An error occurred accessing the bigtable.Table object.
“”"
pass
类
类应该在其定义下有一个用于描述该类的文档字符串_. 如果你的类有公共属性(Attributes), 那么文档中应该有一个属性(Attributes)段. 并且应该遵守和函数参数相同的格式._
class SampleClass(object):
“”"Summary of class here.
Longer class information…
Longer class information…
Attributes:
likes_spam: A boolean indicating if we like SPAM or not.
eggs: An integer count of the eggs we have laid.
“”"
def init(self, likes_spam=False):
“”“Inits SampleClass with blah.”""
self.likes_spam = likes_spam
self.eggs = 0
def public_method(self):
“”“Performs operation blah.”""
块注释和行注释
最需要写注释的是代码中那些技巧性的部分_. 如果你在下次 代码审查 的时候必须解释一下, 那么你应该现在就给它写注释. 对于复杂的操作, 应该在其操作开始前写上若干行注释. 对于不是一目了然的代码, 应在其行尾添加注释._
if i & (i-1) == 0: # true iff i is a power of 2
为了提高可读性_, 注释应该至少离开代码__2__个空格._
另一方面_, 绝不要描述代码. _假设阅读代码的人比你更懂__Python, 他只是不知道你的代码要做什么.
如果一个类不继承自其它类, 就显式的从object继承. 嵌套类也一样.
Yes: class SampleClass(object):
pass
class OuterClass(object):
class InnerClass(object):
pass
class ChildClass(ParentClass):
“”“Explicitly inherits from another class already.”""
No: class SampleClass:
pass
class OuterClass:
class InnerClass:
pass
继承自 object
是为了使属性(properties)正常工作, 并且这样可以保护你的代码, 使其不受Python 3000的一个特殊的潜在不兼容性影响. 这样做也定义了一些特殊的方法, 这些方法实现了对象的默认语义, 包括 __new__, __init__, __delattr__, __getattribute__, __setattr__, __hash__, __repr__, and __str__
.
为临时代码使用TODO注释, 它是一种短期解决方案. 不算完美, 但够好了.
TODO注释应该在所有开头处包含"TODO"字符串, 紧跟着是用括号括起来的你的名字, email地址或其它标识符. 然后是一个可选的冒号. 接着必须有一行注释, 解释要做什么. 主要目的是为了有一个统一的TODO格式, 这样添加注释的人就可以搜索到(并可以按需提供更多细节). 写了TODO注释并不保证写的人会亲自解决问题. 当你写了一个TODO, 请注上你的名字.
如果你的TODO是"将来做某事"的形式, 那么请确保你包含了一个指定的日期(“2009年11月解决”)或者一个特定的事件(“等到所有的客户都可以处理XML请求就移除这些代码”).
module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_VAR_NAME, instance_var_name, function_parameter_name, local_var_name.
应该避免的名称
单字符名称, 除了计数器和迭代器.
包/模块名中的连字符(-)
双下划线开头并结尾的名称(Python保留, 例如__init__)
命名约定
所谓"内部(Internal)"表示仅模块内可用, 或者, 在类内是保护或私有的.
用单下划线(_)开头表示模块变量或函数是protected的(使用import * from时不会包含).
用双下划线(__)开头的实例变量或方法表示类内私有.
将相关的类和顶级函数放在同一个模块里. 不像Java, 没必要限制一个类一个模块.
对类名使用大写字母开头的单词(如CapWords, 即Pascal风格), 但是模块名应该用小写加下划线的方式(如lower_with_under.py). 尽管已经有很多现存的模块使用类似于CapWords.py这样的命名, 但现在已经不鼓励这样做, 因为如果模块名碰巧和类名一致, 这会让人困扰.
即使是一个打算被用作脚本的文件, 也应该是可导入的. 并且简单的导入不应该导致这个脚本的主功能(main functionality)被执行, 这是一种副作用. 主功能应该放在一个main()函数中.
在Python中, pydoc以及单元测试要求模块必须是可导入的. 你的代码应该在执行主程序前总是检查 if __name__ == '__main__'
, 这样当模块被导入时主程序就不会被执行.
def main():
…
if name == ‘main’:
main()
所有的顶级代码在模块导入时都会被执行. 要小心不要去调用函数, 创建对象, 或者执行那些不应该在使用pydoc时执行的操作.
二十三种设计模式及其python实现
本文源码寄方于github:https://github.com/w392807287/Design_pattern_of_python
参考文献:
《大话设计模式》——吴强
《Python设计模式》——pythontip.com
《23种设计模式》——http://www.cnblogs.com/beijiguangyong/
设计模式是什么?
设计模式是经过总结、优化的,对我们经常会碰到的一些编程问题的可重用解决方案。一个设计模式并不像一个类或一个库那样能够直接作用于我们的代码。反之,设计模式更为高级,它是一种必须在特定情形下实现的一种方法模板。设计模式不会绑定具体的编程语言。一个好的设计模式应该能够用大部分编程语言实现(如果做不到全部的话,具体取决于语言特性)。最为重要的是,设计模式也是一把双刃剑,如果设计模式被用在不恰当的情形下将会造成灾难,进而带来无穷的麻烦。然而如果设计模式在正确的时间被用在正确地地方,它将是你的救星。
起初,你会认为“模式”就是为了解决一类特定问题而特别想出来的明智之举。说的没错,看起来的确是通过很多人一起工作,从不同的角度看待问题进而形成的一个最通用、最灵活的解决方案。也许这些问题你曾经见过或是曾经解决过,但是你的解决方案很可能没有模式这么完备。
虽然被称为“设计模式”,但是它们同“设计“领域并非紧密联系。设计模式同传统意义上的分析、设计与实现不同,事实上设计模式将一个完整的理念根植于程序中,所以它可能出现在分析阶段或是更高层的设计阶段。很有趣的是因为设计模式的具体体现是程序代码,因此可能会让你认为它不会在具体实现阶段之前出现(事实上在进入具体实现阶段之前你都没有意识到正在使用具体的设计模式)。
可以通过程序设计的基本概念来理解模式:增加一个抽象层。抽象一个事物就是隔离任何具体细节,这么做的目的是为了将那些不变的核心部分从其他细节中分离出来。当你发现你程序中的某些部分经常因为某些原因改动,而你不想让这些改动的部分引发其他部分的改动,这时候你就需要思考那些不会变动的设计方法了。这么做不仅会使代码可维护性更高,而且会让代码更易于理解,从而降低开发成本。
这里列举了三种最基本的设计模式:
创建模式,提供实例化的方法,为适合的状况提供相应的对象创建方法。
结构化模式,通常用来处理实体之间的关系,使得这些实体能够更好地协同工作。
行为模式,用于在不同的实体建进行通信,为实体之间的通信提供更容易,更灵活的通信方法。
创建型
1. Factory Method(工厂方法)
2. Abstract Factory(抽象工厂)
3. Builder(建造者)
4. Prototype(原型)
5. Singleton(单例)
结构型
6. Adapter Class/Object(适配器)
7. Bridge(桥接)
8. Composite(组合)
9. Decorator(装饰)
10. Facade(外观)
11. Flyweight(享元)
12. Proxy(代理)
行为型
13. Interpreter(解释器)
14. Template Method(模板方法)
15. Chain of Responsibility(责任链)
16. Command(命令)
17. Iterator(迭代器)
18. Mediator(中介者)
19. Memento(备忘录)
20. Observer(观察者)
21. State(状态)
22. Strategy(策略)
23. Visitor(访问者)
意图:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
适用性:
当一个类不知道它所必须创建的对象的类的时候。
当一个类希望由它的子类来指定它所创建的对象的时候。
当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
实现:****
意图:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
适用性:
一个系统要独立于它的产品的创建、组合和表示时。
一个系统要由多个产品系列中的一个来配置时。
当你要强调一系列相关的产品对象的设计以便进行联合使用时。
当你提供一个产品类库,而只想显示它们的接口而不是实现时。
意图:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用性:
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
当构造过程必须允许被构造的对象有不同的表示时。
意图:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用性:
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
意图:
为其他对象提供一种代理以控制对这个对象的访问。
适用性:
在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。下面是一 些可以使用Proxy 模式常见情况:
当第一次引用一个持久对象时,将它装入内存。
在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
意图:
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
适用性:
一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke 和Johnson所描述过的“重分解以一般化”的一个很好的例子[ OJ93 ]。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
控制子类扩展。模板方法只在特定点调用“hook ”操作(参见效果一节),这样就只允许在这些点进行扩展。
意图:
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。
适用性:
抽象出待执行的动作以参数化某对象,你可用过程语言中的回调(call back)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。Command 模式是回调机制的一个面向对象的替代品。
在不同的时刻指定、排列和执行请求。一个Command对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程并在那儿实现该请求。
支持取消操作。Command的Excute 操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command 接口必须添加一个Unexecute操作,该操作取消上一次Execute调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用Unexecute和Execute来实现重数不限的“取消”和“重做”。
支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。在Command接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用Execute操作重新执行它们。
用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务( transaction)的信息系统中很常见。一个事务封装了对数据的一组变动。Command模式提供了对事务进行建模的方法。Command有一个公共的接口,使得你可以用同一种方式调用所有的事务。同时使用该模式也易于添加新事务以扩展系统。
我该怎么开始介绍重构(refactoring)呢?按照传统作法,一开始介绍某个东西时,首先应该大致讲讲它的历史、主要原理等等。可是每当有人在会场上介绍这些东西,总是诱发我的瞌睡虫。我的思绪开始游荡,我的眼神开始迷离,直到他或她拿出实例,我才能够提起精神。实例之所以可以拯救我于太虚之中,因为它让我看见事情的真正行进。谈厚理,很容易流于泛泛,又很难说明如何实际应用。给出一个实例, 却可以帮助我把事情认识清楚。
所以我决定以一个实例作为本书起点。在此过程中我将告诉你很多重构原理,并且让你对重构过程有一点感觉。然后我才能向你提供普通惯见的原理介绍。
但是,面对这个介绍性实例,我遇到了一个大问题。如果我选择一个大型程序,对程序自身的描述和对重构过程的描述就太复杂了,任何读者都将无法掌握(我试了 一下,哪怕稍微复杂一点的例子都会超过100页)。如果我选择一个够小以至于容易理解的程序,又恐怕看不出重构的价值。
和任何想要介绍「应用于真实世界中的有用技术」的人一样,我陷入了一个十分典型的两难困境。我将带引你看看如何在一个我所选择的小程序中进行重构,然而坦白说,那个程序的规模根本不值得我们那么做。但是如果我给你看的代码是大系统的一部分,重构技术很快就变得重要起来。所以请你一边观赏这个小例子,一边想像它身处于一个大得多的系统。
我不想把重构说成治百病的万灵丹,它绝对不是所谓的「银弹」[1]。不过它的确很有价值,虽不是一颗银子弹却是一把「银钳子」,可以帮助你始终良好地控制自己的代码。重构是个工具,它可以(并且应该)为了以下数个目的而被运用。
[1]译注:「银弹」(silver bullet)是美国家喻户晓的比喻。美国民间流传月圆之夜狼人 出没,只有以纯银子弹射穿狼人心脏,才能制服狼人。
「重构」改进软件设计
如果没有重构,程序的设计会逐渐腐败变质。当人们只为短期目的,或是在完全理解整体设计之前,就贸然修改代码,程序将逐渐失去自己的结构,程序员愈来愈难通过阅读源码而理解原本设计。重构很像是在整理代码,你所做的就是让所有东西回到应该的位置上。代码结构的流失是累积性的。愈难看出代码所代表的设计意涵,就愈难保护其中设计,于是该设计就腐败得愈快。经常性的重构可以帮助代码维持自己该有的形态。
同样完成一件事,设计不良的程序往往需要更多代码,这常常是因为代码在不同的地方使用完全相同的语句做同样的事。因此改进设计的一个重要方向就是消除重复代码(Duplicate Code)。这个动作的重要性着眼于未来。代码数量减少并不会使系统运行更快,因为这对程序的运行轨迹几乎没有任何明显影响。然而代码数量减少将使未来可能的程序修改动作容易得多。代码愈多,正确的修改就愈困难,因为有更多代码需要理解。你在这儿做了点修改,系统却不如预期那样工作,因为你未曾修改另一处——那儿的代码做着几乎完全一样的事情,只是所处环境略有不同。 如果消除重复代码,你就可以确定代码将所有事物和行为都只表述一次,惟一一次,这正是优秀设计的根本。
「重构」使软件更易被理解
从许多角度来说,所谓程序设计,便是与计算机交谈。你编写代码告诉计算机做什么事,它的响应则是精确按照你的指示行动。你得及时填补「想要它做什么」和「告 诉它做什么」之间的缝隙。这种编程模式的核心就是「准确说出吾人所欲」。除了计算机外,你的源码还有其他读者:数个月之后可能会有另一位程序员尝试读懂你的代码并做一些修改。我们很容易忘记这第二位读者,但他才是最重要的。计算机是否多花了数个钟头进行编译,又有什么关系呢?如果一个程序员花费一周时间来修改某段代码,那才关系重大——如果他理解你的代码,这个修改原本只需一小时。
问题在于,当你努力让程序运转的时候,你不会想到未来出现的那个开发者。是的,是应该改变一下我们的开发节奏,对代码做适当修改,让代码变得更易理解。重构可以帮助我们让代码更易读。一开始进行重构时,你的代码可以正常运行,但结构不够理想。在重构上花一点点时间,就可以让代码更好地表达自己的用途。这种编程模式的核心就是「准确说出你的意思」。
关于这一点,我没必要表现得如此无私。很多时候那个「未来的开发者」就是我自己。此时重构就显得尤其重要了。我是个很懒惰的程序员,我的懒惰表现形式之一就是:总是记不住自己写过的代码。事实上对于任何立可查阅的东西我都故意不去记它,因为我怕把自己的脑袋塞爆。我总是尽量把该记住的东西写进程序里头,这样我就不必记住它了。这么一来我就不必太担心Old Peculier(译注:一种有名的麦芽酒〉[Jackson]杀光我的脑细胞。
这种可理解性还有另一方面的作用。我利用重构来协助我理解不熟悉的代码。当我看到不熟悉的代码,我必须试着理解其用途。我先看两行代码,然后对自己说:『噢, 是的,它做了这些那些……』。有了重构这个强大武器在手,我不会满足于这么一点脑中体会。我会真正动手修改代码,让它更好地反映出我的理解,然后重新执行,看它是否仍然正常运作,以此检验我的理解是否正确。
一开始我所做的重构都像这样停留在细枝末节上。随着代码渐趋简洁,我发现自己可以看到一些以前看不到的设计层面的东西。如果不对代码做这些修改,也许我永远看不见它们,因为我的聪明才智不足以在脑子里把这一切都想像出来。Ralph Johnson把这种「早期重构」描述为「擦掉窗户上的污垢,使你看得更远」。研究代码时我发现,重构把我带到更高的理解层次上。如果没有重构,我达不到这种层次。
现在,对于「重构如何运作」,你已经有了相当好的理解。但是知道How不代表 知道When。决定何时重构、何时停止和知道重构机制如何运转是一样重要的。
难题来了!解释「如何删除一个instance变量」或「如何产生一个class hierarchy(阶层体系)」很容易,因为这些都是很简单的事情。但要解释「该在什么时候做这些动作」就没那么顺理成章了。除了露几手含混的编程美学(说实话,这就是咱 这些顾问常做的事),我还希望让某些东西更具说服力一些。
去苏黎士拜访Kent Beck的时候,我正在为这个微妙的问题大伤脑筋。也许是因为受到刚出生的女儿的气味影响吧,他提出「用味道来形容重构时机」。『味道』,他说,『听起来是不是比含混的美学理论要好多了?』啊,是的。我们看过很多很 多代码,它们所属的项目从大获成功到奄奄一息都有。观察这些代码时,我们学会了从中找寻某些特定结构,这些结构指出(有时甚至就像尖叫呼喊)重构的可能性。(本章主词换成「我们」,是为了反映一个事实:Kent和我共同撰写本章。你应该可以看出我俩的文笔差异——插科打诨的部分是我写的,其余都是他的。〕
我们并不试图给你一个「重构为时晚矣」的精确衡量标准。从我们的经验看来,没有任何量度规矩比得上一个见识广博者的直觉。我们只会告诉你一些迹象,它会指出「这里有一个可使用重构解决的问题」。你必须培养出自己的判断力,学会判断一个class内有多少instance变量算是太大、一个函数内有多少行代码才算太长。
如果你无法确定该进行哪一种重构手法,请阅读本章内容和封底内页表格来寻找灵感。你可以阅读本章(或快速浏览封底内页表格〕来判断自己闻到的是什么味道, 然后再看看我们所建议的重构手法能否帮助你。也许这里所列的「臭味条款」和你所检测的不尽相符,但愿它们能够为你指引正确方向。
如果你想进行重构(refactoring),首要前提就是拥有一个可靠的测试环境。就算你够幸运,有一个可以自动进行重构的工具,你还是需要测试。而且短时间内不可能有任何工具可以为我们自动进行所有可能的重构。
我并不把这视为缺点。我发现,编写优良的测试程序,可以极大提高我的编程速度,即使不进行重构也一样如此。这让我很吃惊,也违反许多程序员的直觉,所以我有 必要解释一下这个现象。
你所需要知道的关于 Django 的一切。
**1. ******
**2. ******
**3. ******
**4. ******
**5. ******
**6. ******
Django 有丰富的文档。一份高度概述的文档会告诉你在哪里找到特定的东西:
教程 通过手把手地方式教你一步步的创建一个 Web 应用。如果你初学 Django 或编程,请从这里开始。也请看看下面的 “快速入门”。
专题指南 在相当高的层次上介绍关键主题和概念,并提供有用的背景信息和解释。
参考指南 包含 API 和 Django 各个工作机制方面的技术参考。它们介绍了 Django 是如何工作,如何被使用的。不过,你得先对关键字的概念有一定理解。
How-to 指南 是目录。它们以排列好的关键问题和用例的方式指导你。它们比教程更加深入,且需要你先了解一些关于 Django 是如何工作的知识。
您是刚学 Django 或是初学编程? 这就是你开始学习的地方!
从零开始: 概要 | 安装
入门教程*** 第1节: 请求和响应 | 第2节: 模型和 admin 站点 | 第3节: 视图和模板 | 第4节: 表单和通用视图 | 第5节: 测试 | 第6节: 静态文件 | 第7节: 自定义 admin 站点
进阶教程** * 如何编写可复用的应用 | 提交你的第一个 Django 补丁
Django 提供了一个抽象的模型 (“models”) 层,为了构建和操纵你的Web应用的数据。阅读下面内容了解更多:
模型*** 模型介绍 | 字段类型 | 索引 | Meta 选项 | Model 类
QuerySet: 执行查询 | QuerySet 方法参考 | 查询表达式
Model 实例: 实例方法 | 访问关联的对象
迁移*** 迁移概述 | 操作参考 | SchemaEditor | 编写迁移
高级*** 管理员 | 原始 SQL | 事务 | 聚合 | 搜索 | 自定义字段 | 多个数据库 | 自定义查询 | 查询表达式 | 条件表达式 | 数据库函数
其它: 支持的数据库 | 旧数据库 | 提供初始化数据 | 优化数据库访问 | PostgreSQL 的特定功能
Django 具有 “视图” 的概念,负责处理用户的请求并返回响应。通过以下链接查找所有你需要知道的有关视图的信息:
基础: URL配置 | 视图函数 | 便捷工具 | 装饰器
参考: 内置视图 | Request/response 对象 | TemplateResponse 对象
文件上传: 概览 | 文件对象 | 存储 API | 管理文件 | 自定义存储
基于类的视图: 概览 | 内置显示视图 | 内置编辑视图 | 使用混入 | API 参考 | 扁平化索引
高级: 生成 CSV | 生成 PDF
中间件: 概览 | 内建的中间件类
模板层提供了一个对设计者友好的语法用于渲染向用户呈现的信息。学习如何使用语法(面向设计者)以及如何扩展(面向程序员):
基础: 概述
对于设计者*** 语法概述 | 内建标签及过滤器(filters) | 人性化
针对程序员*** 模板 API | 自定义标签(tags)和过滤器(filters)
Django 提供了一个丰富的框架来帮助创建表单和处理表单数据。
基础*** 概览 | 表单 API | 内建字段 | 内建 widgets
进阶*** 针对模型的表单 | 整合媒体 | 表单集 | 自定义验证
学习众多的组件及工具,来帮助你开发和测试 Django 应用:
设置: 概览 | 完整的设置列表
应用程序: 概览
异常: 概览
django-admin.py 和 manage.py: 概览 | 添加自定义命令
测试: 介绍 | 书写并运行测试 | 包含的测试工具 | 高级主题
部署: 概览 | WSGI 服务器 | 部署静态文件 | 用 email 跟踪代码错误
找到所有你想知道的,关于自动化管理界面的知识,Django 最受欢迎的特性之一:
管理站点
管理动作
管理文档生成器
在 Web 应用的发展中,安全是最重要主题,Django 提供了多种保护手段和机制。
安全概览
在 Django 中披露的安全问题
点击劫持保护
跨站请求伪造 CSRF 保护
登录加密
安全中间件
Django 提供了一个强大的国际化和本地化的框架, 以帮助您在多语言和世界各地区进行应用程序的开发:
概览 | 国际化 | 本地化 | 给 Web 界面及表单输入进行本地化
时区
有各种各样的技术和工具,可以帮助你的代码的运行更高效,更快和使用更少的系统资源.
性能和优化概述
GeoDjango 想要成为一个世界级的地理 Web 框架。尽可能简化构建 GIS Web 应用程序的流程,和利用空间化数据的能力是它的目标。
Django 提供了多种开发 Web 应用程序所需的常用工具:
认证*** 概述 | 使用认证系统 | 密码管理 | 自定义认证 | API 参考
缓存
日志
发送邮件
资讯聚合 (RSS/Atom)
分页
消息框架
序列化
会话
站点地图
静态文件管理
数据验证
了解更多 Django 框架的其他核心功能 :
有条件的内容处理
内容类型和通用关系
简单页面
重定向
信号
系统检查框架
站点框架
Django 中的 Unicode
了解 Django 项目本身的开发进程以及您如何为 Django 做贡献:
社区: 如何参与其中 | 发布进程 | 团队组织 | Django 源代码仓库 | 安全政策 | 邮件列表
设计哲学: 概览
文档: 关于本文档
第三方发行*** 概览
**Django **时间线: API 稳定性 | 发行说明和升级说明 | 过时时间表
C/C++
Java
matlab