基础学习——python基础

1.基础

建议遵照如下约定:javascript

使用 4 个空格来缩进
永远不要混用空格和制表符
在函数之间空一行
在类之间空两行
字典,列表,元组以及参数列表中,在 , 后添加一个空格。对于字典,: 后面也添加一个空格
在赋值运算符和比较运算符周围要有空格(参数列表中除外),可是括号里则不加空格:a = f(1, 2) + g(3, 4)css

 

注释是一些文本用来解释此处代码是作什么的,写注释是为了使他人更容易理解代码。html

Python 的注释以 # 字符开始的,在 # 字符到行尾之间的全部东西都被程序忽略为注释,也就说对程序没有任何影响。java

>>> # 这是一个注释
>>> # 下面这一行是求两数之和
>>> a = 12 + 34
>>> print(a) # 这里也是注释 :)
注释主要面向的人群是开发人员和维护代码库的人员,因此若是你写了一些复杂代码,你应该写足够的注释在里面,以便让其余任何人可以经过你的注释读懂代码。你应该老是在 # 后跟一个空格,而后再写注释。你也能够作一些标准的注释,像下面这样。node

 

模块是包含了咱们能复用的代码的文件,包含了不一样的函数定义,变量。模块文件一般以 .py 为扩展名。python

Python 自己在默认安装时就带有大量的模块。咱们以后将会用到其中的一部分。在使用模块前先导入它。git

 

2.变量和数据类型

下列的标识符是 Python3 的关键字,而且不能用于一般的标识符。关键字必须彻底按照下面拼写:程序员

False               def                 if                  raise
None                del                 import              return
True                elif                in                  try
and                 else                is                  while
as                  except              lambda              with
assert              finally             nonlocal            yield
break               for                 not                 
class               from                or                  
continue            global              passweb

方式:help()->keywordsredis

input() 有一个用于打印在屏幕上的可选字符串参数,返回用户输入的字符串。

x op= expression 为简写运算的语法形式。其等价于 x = x op expression

2.6 类型转换
咱们能够手动的执行类型转换。

类型转换函数    转换路径
float(string)    字符串 -> 浮点值
int(string)    字符串 -> 整数值
str(integer)    整数值 -> 字符串
str(float)    浮点值 -> 字符串

 

3.控制流 If-else

检测真值的优雅方式是这样的:

if x:
    pass
不要像下面这样作:

if x == True:
    pass

它能够写做中括号之间的一列逗号分隔的值。列表的元素没必要是同一类型:

切片并不会改变正在操做的列表,切片操做返回其子列表,这意味着下面的切片操做返回列表一个新的(栈)拷贝副本:

切片的索引有很是有用的默认值;省略的第一个索引默认为零,省略的第二个索引默认为切片的字符串的大小:

Python 中有关下标的集合都知足左闭右开原则,切片中也是如此,也就是说集合左边界值能取到,右边界值不能取到。

Python 可以优雅地处理那些没有意义的切片索引:一个过大的索引值(即大于列表实际长度)将被列表实际长度所代替,当上边界比下边界大时(即切片左值大于右值)就返回空列表:

若是你想要检查列表是否为空,请这样作:

if list_name: # 列表不为空
    pass
else: # 列表为空
    pass

经过 for 语句咱们可使用 for 循环。Python 里的 for 循环与 C 语言中的不一样。这里的 for 循环遍历任何序列(好比列表和字符串)中的每个元素。下面给出示例:

>>> a = ['ShiYanLou', 'is', 'powerful']
>>> for x in a:
...     print(x)
...
ShiYanLou
is
powerful

 

range() 函数
若是你须要一个数值序列,内置函数 range() 会很方便,它生成一个等差数列(并非列表):

如同 break ,咱们能够在循环中使用另外一个语句 continue。它会跳过其后的代码回到循环开始处执行。

 

咱们能够在循环后面使用可选的 else 语句。它将会在循环完毕后执行,除非有 break 语句终止了循环。

>>> for i in range(0, 5):
...     print(i)
... else:
...     print("Bye bye")
...
0
1
2
3
4
Bye bye

5.数据结构

2、列表
>>> a = [23, 45, 1, -3434, 43624356, 234]
>>> a.append(45)
>>> a
[23, 45, 1, -3434, 43624356, 234, 45]
首先咱们创建了一个列表 a。而后调用列表的方法 a.append(45) 添加元素 45 到列表末尾。你能够看到元素 45 已经添加到列表的末端了。有些时候咱们须要将数据插入到列表的任何位置,这时咱们可使用列表的 insert() 方法。

>>> a.insert(0, 1) # 在列表索引 0 位置添加元素 1
>>> a
[1, 23, 45, 1, -3434, 43624356, 234, 45]
>>> a.insert(0, 111) # 在列表索引 0 位置添加元素 111
>>> a
[111, 1, 23, 45, 1, -3434, 43624356, 234, 45]
列表方法 count(s) 会返回列表元素中 s 的数量。咱们来检查一下 45 这个元素在列表中出现了多少次。

>>> a.count(45)
2
若是你想要在列表中移除任意指定值,你须要使用 remove() 方法。

>>> a.remove(234)
>>> a
[111, 1, 23, 45, 1, -3434, 43624356, 45]
如今咱们反转整个列表。

>>> a.reverse()
>>> a
[45, 43624356, -3434, 1, 45, 23, 1, 111]
怎样将一个列表的全部元素添加到另外一个列表的末尾呢,可使用列表的 extend() 方法。

>>> b = [45, 56, 90]
>>> a.extend(b) # 添加 b 的元素而不是 b 自己
>>> a
[45, 43624356, -3434, 1, 45, 23, 1, 111, 45, 56, 90]
给列表排序,咱们使用列表的 sort() 方法,排序的前提是列表的元素是可比较的。

>>> a.sort()
>>> a
[-3434, 1, 1, 23, 45, 45, 45, 56, 90, 111, 43624356]
你也能使用 del 关键字删除指定位置的列表元素。

>>> del a[-1]
>>> a
[-3434, 1, 1, 23, 45, 45, 45, 56, 90, 111]
2.1 将列表用做栈和队列
栈是咱们一般所说的一种 LIFO (Last In First Out 后进先出)数据结构。它的意思是最后进入的数据第一个出来。一个最简单的例子往一端封闭的管道放入一些弹珠而后取出来,若是你想把弹珠取出来,你必须从你最后放入弹珠的位置挨个拿出来。用代码实现此原理:

>>> a = [1, 2, 3, 4, 5, 6]
>>> a
[1, 2, 3, 4, 5, 6]
>>> a.pop()
6
>>> a.pop()
5
>>> a.pop()
4
>>> a.pop()
3
>>> a
[1, 2]
>>> a.append(34)
>>> a
[1, 2, 34]
上面的代码中咱们使用了一个新方法 pop()。传入一个参数 i 即 pop(i) 会将第 i 个元素弹出。

在咱们的平常生活中会常常遇到队列,好比售票窗口、图书馆、超市的结帐出口。队列 是一种在末尾追加数据以及在开始弹出数据的数据结构。与栈不一样,它是 FIFO (First In First Out 先进先出)的数据结构。

>>> a = [1, 2, 3, 4, 5]
>>> a.append(1)
>>> a
[1, 2, 3, 4, 5, 1]
>>> a.pop(0)
1
>>> a.pop(0)
2
>>> a
[3, 4, 5, 1]
咱们使用 a.pop(0) 弹出列表中第一个元素。

2.2 列表推导式
列表推导式为从序列中建立列表提供了一个简单的方法。若是没有列表推导式,通常都是这样建立列表的:经过将一些操做应用于序列的每一个成员并经过返回的元素建立列表,或者经过知足特定条件的元素建立子序列。

假设咱们建立一个 squares 列表,能够像下面这样建立。

>>> squares = []
>>> for x in range(10):
...     squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
注意这个 for 循环中的被建立(或被重写)的名为 x 的变量在循环完毕后依然存在。使用以下方法,咱们能够计算 squares 的值而不会产生任何的反作用:。

squares = list(map(lambda x: x**2, range(10)))
等价于下面的列表推导式。

squares = [x**2 for x in range(10)]
上面这个方法更加简明且易读。

列表推导式由包含一个表达式的中括号组成,表达式后面跟随一个 for 子句,以后能够有零或多个 for 或 if 子句。结果是一个列表,由表达式依据其后面的 for 和 if 子句上下文计算而来的结果构成。

例如,以下的列表推导式结合两个列表的元素,若是元素之间不相等的话:

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
等同于:

>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,4]:
...         if x != y:
...             combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
值得注意的是在上面两个方法中的 for 和 if 语句的顺序。

列表推导式也能够嵌套。

>>> a=[1,2,3]
>>> z = [x + 1 for x in [x ** 2 for x in a]]
>>> z
[2, 5, 10]
3、元组
元组是由数个逗号分割的值组成。

>>> a = 'Fedora', 'ShiYanLou', 'Kubuntu', 'Pardus'
>>> a
('Fedora', 'ShiYanLou', 'Kubuntu', 'Pardus')
>>> a[1]
'ShiYanLou'
>>> for x in a:
...     print(x, end=' ')
...
Fedora ShiYanLou Kubuntu Pardus
你能够对任何一个元组执行拆封操做并赋值给多个变量,就像下面这样:

>>> divmod(15,2)
(7, 1)
>>> x, y = divmod(15,2)
>>> x
7
>>> y
1
元组是不可变类型,这意味着你不能在元组内删除或添加或编辑任何值。若是你尝试这些操做,将会出错:

>>> a = (1, 2, 3, 4)
>>> del a[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object doesn't support item deletion
要建立只含有一个元素的元组,在值后面跟一个逗号。

>>> a = (123)
>>> a
123
>>> type(a)
<class 'int'>
>>> a = (123, )
>>> b = 321,
>>> a
(123,)
>>> b
(321,)
>>> type(a)
<class 'tuple'>
>>> type(b)
<class 'tuple'>
经过内建函数 type() 你能够知道任意变量的数据类型。还记得咱们使用 len() 函数来查询任意序列类型数据的长度吗?

>>> type(len)
<class 'builtin_function_or_method'>
4、集合
集合是一个无序不重复元素的集。基本功能包括关系测试和消除重复元素。集合对象还支持 union(联合),intersection(交),difference(差)和 symmetric difference(对称差集)等数学运算。

大括号或 set() 函数能够用来建立集合。注意:想要建立空集合,你必须使用 set() 而不是 {}。后者用于建立空字典,咱们在下一节中介绍的一种数据结构。

下面是集合的常见操做:

>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket)                      # 你能够看到重复的元素被去除
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket
True
>>> 'crabgrass' in basket
False

>>> # 演示对两个单词中的字母进行集合操做
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  # a 去重后的字母
{'a', 'r', 'b', 'c', 'd'}
>>> a - b                              # a 有而 b 没有的字母
{'r', 'd', 'b'}
>>> a | b                              # 存在于 a 或 b 的字母
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b                              # a 和 b 都有的字母
{'a', 'c'}
>>> a ^ b                              # 存在于 a 或 b 但不一样时存在的字母
{'r', 'd', 'b', 'm', 'z', 'l'}
从集合中添加或弹出元素:

>>> a = {'a','e','h','g'}
>>> a.pop()
'h'
>>> a.add('c')
>>> a
{'c', 'e', 'g', 'a'}
5、字典
字典是是无序的键值对(key:value)集合,同一个字典内的键必须是互不相同的。一对大括号 {} 建立一个空字典。初始化字典时,在大括号内放置一组逗号分隔的键:值对,这也是字典输出的方式。咱们使用键来检索存储在字典中的数据。

>>> data = {'kushal':'Fedora', 'kart_':'Debian', 'Jace':'Mac'}
>>> data
{'kushal': 'Fedora', 'Jace': 'Mac', 'kart_': 'Debian'}
>>> data['kart_']
'Debian'
建立新的键值对很简单:

>>> data['parthan'] = 'Ubuntu'
>>> data
{'kushal': 'Fedora', 'Jace': 'Mac', 'kart_': 'Debian', 'parthan': 'Ubuntu'}
使用 del 关键字删除任意指定的键值对:

>>> del data['kushal']
>>> data
{'Jace': 'Mac', 'kart_': 'Debian', 'parthan': 'Ubuntu'
使用 in 关键字查询指定的键是否存在于字典中。

>>> 'ShiYanLou' in data
False
必须知道的是,字典中的键必须是不可变类型,好比你不能使用列表做为键。

dict() 能够从包含键值对的元组中建立字典。

>>> dict((('Indian','Delhi'),('Bangladesh','Dhaka')))
{'Indian': 'Delhi', 'Bangladesh': 'Dhaka'}
若是你想要遍历一个字典,使用字典的 items() 方法。

>>> data
{'Kushal': 'Fedora', 'Jace': 'Mac', 'kart_': 'Debian', 'parthan': 'Ubuntu'}
>>> for x, y in data.items():
...     print("{} uses {}".format(x, y))
...
Kushal uses Fedora
Jace uses Mac
kart_ uses Debian
parthan uses Ubuntu
许多时候咱们须要往字典中的元素添加数据,咱们首先要判断这个元素是否存在,不存在则建立一个默认值。若是在循环里执行这个操做,每次迭代都须要判断一次,下降程序性能。

咱们可使用 dict.setdefault(key, default) 更有效率的完成这个事情。

>>> data = {}
>>> data.setdefault('names', []).append('Ruby')
>>> data
{'names': ['Ruby']}
>>> data.setdefault('names', []).append('Python')
>>> data
{'names': ['Ruby', 'Python']}
>>> data.setdefault('names', []).append('C')
>>> data
{'names': ['Ruby', 'Python', 'C']}
试图索引一个不存在的键将会抛出一个 keyError 错误。咱们可使用 dict.get(key, default) 来索引键,若是键不存在,那么返回指定的 default 值。

>>> data['foo']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'foo'
>>> data.get('foo', 0)
0
若是你想要在遍历列表(或任何序列类型)的同时得到元素索引值,你可使用 enumerate()。

>>> for i, j in enumerate(['a', 'b', 'c']):
...     print(i, j)
...
0 a
1 b
2 c
你也许须要同时遍历两个序列类型,你可使用 zip() 函数。

>>> a = ['Pradeepto', 'Kushal']
>>> b = ['OpenSUSE', 'Fedora']
>>> for x, y in zip(a, b):
...     print("{} uses {}".format(x, y))
...
Pradeepto uses OpenSUSE
Kushal uses Fedora

6.字符串

2、字符串表示
能够经过几种不一样的方式表示字符串。如单引号('...')或双引号("...")。下面的例子能帮助你更好的理解字符串。

>>> s = "I am Chinese"
>>> s
'I am Chinese'
>>> s = 'I am Chinese'
>>> s = "Here is a line \
... split in two lines"
>>> s
'Here is a line split in two lines'
>>> s = "Here is a line \n split in two lines"
>>> s
'Here is a line \n split in two lines'
>>> print(s)
Here is a line
 split in two lines
若是你想要分几行输入字符串,而且但愿行尾的换行符自动包含到字符串当中,可使用三对引号:"""...""" 或 '''...''' 。

>>> print("""\
... Usage: thingy [OPTIONS]
...      -h                        Display this usage message
...      -H hostname               Hostname to connect to
... """)
Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to

3、字符串的方法
每个字符串对象都有几个可用的内建方法,咱们已经使用过一些了,好比 s.split()。

>>> s = "shi yan lou"
>>> s.title()
'Shi Yan Lou'
方法 title() 返回字符串的标题版本,即单词首字母大写其他字母小写。

>>> z = s.upper()
>>> z
'SHI YAN LOU'
>>> z.lower()
'shi yan lou'
方法 upper() 返回字符串所有大写的版本,反之 lower() 返回字符串的所有小写版本。

>>> s = "I am A pRoGraMMer"
>> s.swapcase()
'i AM a PrOgRAmmER'
方法 swapcase() 返回字符串大小写交换后的版本 :)

>>> s = "jdwb 2323bjb"
>>> s.isalnum()
False
>>> s = "jdwb2323bjb"
>>> s.isalnum()
True
方法 isalnum() 检查全部字符是否只有字母和数字,上面的代码中第一行的字符串 s 中包含空格字符,因此返回 False。

>>> s = "SankarshanSir"
>>> s.isalpha()
True
>>> s = "Sankarshan Sir"
>>> s.isalpha()
False
方法 isalpha() 检查字符串之中是否只有字母。

>>> s = "1234"
>>> s.isdigit() # 检查字符串是否全部字符为数字
True
>>> s = "ShiYanLou is coming"
>>> s.islower() # 检查字符串是否全部字符为小写
False
>>> s = "Shiyanlou Is Coming"
>>> s.istitle() # To 检查字符串是否为标题样式
True
>>> s = "CHINA"
>>> s.isupper() # 检查字符串是否全部字符为大写
True
咱们可使用 split() 分割任意字符串,split() 容许有一个参数,用来指定字符串以什么字符分隔(默认为 " "),它返回一个包含全部分割后的字符串的列表。

>>> s = "We all love Python"
>>> s.split()
['We', 'all', 'love', 'Python']
>>> x = "shiyanlou:is:waiting"
>>> x.split(':')
['shiyanlou', 'is', 'waiting']
相反的,方法 join() 使用指定字符链接多个字符串,它须要一个包含字符串元素的列表做为输入而后链接列表内的字符串元素。

>>> "-".join("GNU/Linux is great".split())
'GNU/Linux-is-great'
在上面的例子中,咱们基于空格 " " 分割字符串 "GNU/Linux is great",而后用 "-" 链接它们。

4、字符串剥离
字符串有几个进行剥离操做的方法。最简单的一个是 strip(chars),用来剥离字符串首尾中指定的字符,它容许有一个字符串参数,这个参数为剥离哪些字符提供依据。不指定参数则默认剥离掉首尾的空格和换行符,代码以下:

>>> s = "  a bc\n "
>>> s.strip()
'a bc'
你可使用 lstrip(chars) 或 rstrip(chars) 只对字符串左或右剥离。

>>> s = "www.foss.in" 
>>> s.lstrip("cwsd.") #删除在字符串左边出现的'c','w','s','d','.'字符
'foss.in'
>>> s.rstrip("cnwdi.") #删除在字符串右边出现的'c','n','w','d','i','.'字符
'www.foss'
5、文本搜索
字符串有一些方法可以帮助你搜索字符串里的文本或子字符串。下面给出示例:

>>> s = "faulty for a reason"
>>> s.find("for")
7
>>> s.find("fora")
-1
>>> s.startswith("fa") # 检查字符串是否以 fa 开头
True
>>> s.endswith("reason") # 检查字符串是否以 reason 结尾
True
find() 能帮助你找到第一个匹配的子字符串,没有找到则返回 -1。

在 Python 里咱们使用文档字符串(docstrings)来讲明如何使用代码,这在交互模式很是有用,也能用于自动建立文档。下面咱们来看看使用文档字符串的例子。

#!/usr/bin/env python3
import math

def longest_side(a, b):
    """
    Function to find the length of the longest side of a right triangle.

    :arg a: Side a of the triangle
    :arg b: Side b of the triangle

    :return: Length of the longest side c as float
    """
    return math.sqrt(a*a + b*b)

if __name__ == '__main__':
    print(longest_side.__doc__)
    print(longest_side(4,5))

 

9.文件处理

2.3 文件读取
使用 read() 方法一次性读取整个文件。

>>> fobj = open("sample.txt")
>>> fobj.read()
'I love Python\nI love shiyanlou\n'
>>> fobj.close()
若是你再一次调用 read(),它会返回空字符串由于它已经读取完整个文件。

read(size) 有一个可选的参数 size,用于指定字符串长度。若是没有指定 size 或者指定为负数,就会读取并返回整个文件。当文件大小为当前机器内存两倍时,就会产生问题。反之,会尽量按比较大的 size 读取和返回数据。

readline() 能帮助你每次读取文件的一行。

>>> fobj = open("sample.txt")
>>> fobj.readline()
'I love Python\n'
>>> fobj.readline()
'I love shiyanlou\n'
>>> fobj.close()
使用 readlines() 方法读取全部行到一个列表中。

>>> fobj = open('sample.txt')
>>> fobj.readlines()
['I love Python\n', 'I love shiyanlou\n']
>>> fobj.close()
你能够循环遍历文件对象来读取文件中的每一行。

>>> fobj = open('sample.txt')
>>> for x in fobj:
...     print(x, end = '')
... 
I love Python
I love shiyanlou
>>> fobj.close()
让咱们写一个程序,这个程序接受用户输入的字符串做为将要读取的文件的文件名,而且在屏幕上打印文件内容。

#!/usr/bin/env python3
name = input("Enter the file name: ")
fobj = open(name)
print(fobj.read())
fobj.close()
运行程序:

$ cd /home/shiyanlou
$ chmod +x test.py
$ ./test.py
Enter the file name: sample.txt
I love Python
I love shiyanlou
2.4 文件写入
让咱们经过 write() 方法打开一个文件而后咱们随便写入一些文本。

>>> fobj = open("ircnicks.txt", 'w')
>>> fobj.write('powerpork\n')
>>> fobj.write('indrag\n')
>>> fobj.write('mishti\n')
>>> fobj.write('sankarshan')
>>> fobj.close()
如今读取咱们刚刚建立的文件。

>>> fobj = open('ircnicks.txt')
>>> s = fobj.read()
>>> fobj.close()
>>> print(s)
powerpork
indrag
mishti
sankarshan

sys.argv 的第一个值是命令自身的名字,下面这个程序打印命令行参数。

#!/usr/bin/env python3
import sys
print("First value", sys.argv[0])
print("All values")
for i, x  in enumerate(sys.argv):
    print(i, x)

在实际状况中,咱们应该尝试使用 with 语句处理文件对象,它会在文件用完后会自动关闭,就算发生异常也不要紧。它是 try-finally 块的简写:

>>> with open('sample.txt') as fobj:
...     for line in fobj:
...         print(line, end = '')
... 
I love Python
I love shiyanlou

 

10.异常

它以以下方式工做:

首先,执行 try 子句 (在 try 和 except 关键字之间的部分)。

若是没有异常发生,except 子句 在 try 语句执行完毕后就被忽略了。

若是在 try 子句执行过程当中发生了异常,那么该子句其他的部分就会被忽略。

若是异常匹配于 except 关键字后面指定的异常类型,就执行对应的 except 子句。而后继续执行 try 语句以后的代码。

若是发生了一个异常,在 except 子句中没有与之匹配的分支,它就会传递到上一级 try 语句中。

若是最终仍找不到对应的处理语句,它就成为一个 未处理异常,终止程序运行,显示提示信息。

try 语句还有另外一个可选的 finally 子句,目的在于定义在任何状况下都必定要执行的功能

 

11.类

类定义了 __init__() 方法的话,类的实例化操做会自动为新建立的类实例调用 __init__() 方法。因此在下例中,能够这样建立一个新的实例:

x = MyClass()
固然,出于弹性的须要,__init__() 方法能够有参数。事实上,参数经过__init__() 传递到类的实例化操做上

2.7 装饰器
你可能想要更精确的调整控制属性访问权限,你可使用 @property 装饰器,@property 装饰器就是负责把一个方法变成属性调用的。

 

12.模块

在 Python 中咱们使用模块来到达这些目的。模块是包括 Python 定义和声明的文件。文件名就是模块名加上 .py 后缀。

你能够由全局变量 __name__ 获得模块的模块名(一个字符串)

含有 __init__.py 文件的目录能够用来做为一个包,目录里的全部 .py 文件都是这个包的子模块。

若是 __init__.py 文件内有一个名为 __all__ 的列表,那么只有在列表内列出的名字将会被公开。

所以若是 mymodule 内的 __init__.py 文件含有如下内容:

from mymodule.bars import simplebar
__all__ = [simplebar, ]
那么导入时将只有 simplebar 可用。

4.1 os 模块
os 模块提供了与操做系统相关的功能。你可使用以下语句导入它:

>>> import os
getuid() 函数返回当前进程的有效用户 id。

>>> os.getuid()
500
getpid() 函数返回当前进程的 id。getppid() 返回父进程的 id。

>>> os.getpid()
16150
>>> os.getppid()
14847
uname() 函数返回识别操做系统的不一样信息,在 Linux 中它返回的详细信息能够从 uname -a 命令获得。uname() 返回的对象是一个元组,(sysname, nodename, release, version, machine)。

>>> os.uname()
('Linux', 'd80', '2.6.34.7-56.fc13.i686.PAE', '#1 SMP Wed Sep 15 03:27:15 UTC 2010', 'i686')
getcwd() 函数返回当前工做目录。chdir(path) 则是更改当前目录到 path。在例子中咱们首先看到当前工做目录是 /home/shiyanlou,而后咱们更改当前工做目录到 /Code 并再一次查看当前工做目录。

>>> os.getcwd()
'/home/shiyanlou'
>>> os.chdir('Code')
>>> os.getcwd()
'/home/shiyanlou/Code'
因此如今让咱们使用 os 模块提供的另外一个函数来建立一个本身的函数,它将列出给定目录下的全部文件和目录。

def view_dir(path='.'):
    """
    这个函数打印给定目录中的全部文件和目录
    :args path: 指定目录,默认为当前目录
    """
    names = os.listdir(path)
    names.sort()
    for name in names:
        print(name, end =' ')
    print()
使用例子中的 view_dir() 函数。

>>> view_dir('/')
.bashrc .dockerenv .profile bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
os 模块还有许多很是有用的函数,你能够在这里阅读相关内容。

4.2 Requests 模块
Requests 是一个第三方 Python 模块,其官网的介绍以下:

Requests 惟一的一个非转基因的 Python HTTP 库,人类能够安全享用。

警告:非专业使用其余 HTTP 库会致使危险的反作用,包括:安全缺陷症、冗余代码症、从新发明轮子症、啃文档症、抑郁、头疼、甚至死亡。

第三方模块并非默认的模块,意味着你须要安装它,咱们使用 pip3 安装它。

首先要安装 pip3:

$ sudo apt-get update
$ sudo apt-get install python3-pip
而后用 pip3 安装 requests

$ sudo pip3 install requests
上面的命令会在你的系统中安装 Python3 版本的 Requests 模块。

你可能已经注意到了 if __name__ == '__main__': 这条语句,它的做用是,只有在当前模块名为 __main__ 的时候(即做为脚本执行的时候)才会执行此 if 块内的语句。换句话说,当此文件以模块的形式导入到其它文件中时,if 块内的语句并不会执行

 

13.PEP8 代码风格指南
 

 

风格指南是关于一致性的。风格一致对于本指南来讲是重要的,对一个项目来讲是更重要的,对于一个模块或者方法来讲是最重要的。

可是最最重要的是:知道何时应该破例–有时候这份风格指南就是不适用。有疑问时,用你最好的判断力,对比其它的例子来肯定这是否是最好的状况,而且不耻下问。

特别说明:不要为了遵照这份风格指南而破坏代码的向后兼容性。

这里有一些好的理由去忽略某个风格指南:

当应用风格指南的时候使代码更难读了,对于严格依循风格指南的约定去读代码的人也是不该该的。
为了保持和风格指南的一致性同时也打破了现有代码的一致性(多是历史缘由)–虽然这也是一个整理混乱代码的机会(现实中的 XP 风格)。
由于问题代码的历史比较久远,修改代码就没有必要性了。
当代码须要与旧版本的 Python 保持兼容,而旧版 Python 又不支持风格指南中提到的特性的时候。
2.3 代码排版
2.3.1 缩进
每层缩进使用4个空格。

续行要么与圆括号、中括号、花括号这样的被包裹元素保持垂直对齐,要么放在 Python 的隐线(注:应该是相对于def的内部块)内部,或者使用悬挂缩进。使用悬挂缩进的注意事项:第一行不能有参数,用进一步的缩进来把其余行区分开。

好的:

# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# More indentation included to distinguish this from the rest.
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

# Hanging indents should add a level.
foo = long_function_name(
    var_one, var_two,
    var_three, var_four)
很差的:

# Arguments on first line forbidden when not using vertical alignment.
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# Further indentation required as indentation is not distinguishable.
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)
4空格规则是可选的:

# Hanging indents *may* be indented to other than 4 spaces.
foo = long_function_name(
  var_one, var_two,
  var_three, var_four)
当 if 语句的条件部分足够长,须要将它写入到多个行,值得注意的是两个连在一块儿的关键字(i.e. if),添加一个空格,给后续的多行条件添加一个左括号造成天然地4空格缩进。若是和嵌套在 if 语句内的缩进代码块产生了视觉冲突,也应该被天然缩进4个空格。这份加强建议书对于怎样(或是否)把条件行和 if 语句的缩进块在视觉上区分开来是没有明确规定的。可接受的状况包括,但不限于:

# No extra indentation.
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# Add a comment, which will provide some distinction in editors
# supporting syntax highlighting.
if (this_is_one_thing and
    that_is_another_thing):
    # Since both conditions are true, we can frobnicate.
    do_something()

# Add some extra indentation on the conditional continuation line.
if (this_is_one_thing
        and that_is_another_thing):
    do_something()
在多行结构中的右圆括号、右中括号、右大括号应该放在最后一行的第一个非空白字符的正下方,以下所示:

my_list = [
    1, 2, 3,
    4, 5, 6,
    ]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
    )
或者放在多行结构的起始行的第一个字符正下方,以下:

my_list = [
    1, 2, 3,
    4, 5, 6,
]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
)
2.3.2 制表符仍是空格?
空格是首选的缩进方法。

制表符(Tab)应该被用在那些之前就使用了制表符缩进的地方。

Python 3 不容许混合使用制表符和空格来缩进代码。

混合使用制表符和空格缩进的 Python 2 代码应该改成只使用空格。

当使用-t选项来调用 Python 2 的命令行解释器的时候,会对混合使用制表符和空格的代码发出警告。当使用-tt选项的时候,这些警告会变成错误。这些选项是强烈推荐的!

2.3.3 每行最大长度
限制每行的最大长度为79个字符。

对于那些约束不多的文本结构(文档字符串或注释)的长块,应该限制每行长度为72个字符。

限制编辑窗口的宽度使并排打开两个窗口成为可能,使用经过代码审查工具时,也能很好的经过相邻列展示不一样代码版本。

一些工具的默认换行设置打乱了代码的可视结构,使其更难理解。限制编辑器窗口宽为80来避免自动换行,即便有些编辑工具在换行的时候会在最后一列放一个标识符。一些基于 Web 的工具可能根本就不提供动态换行。

一些团队更倾向于长的代码行。对于达成了一致意见来统一代码的团队而言,把行提高到80~100的长度是可接受的(实际最大长度为99个字符),注释和文档字符串的长度仍是建议在72个字符内。

Python 标准库是很是专业的,限制最大代码长度为79个字符(注释和文档字符串最大长度为72个字符)。

首选的换行方式是在括号(小中大)内隐式换行(非续行符\)。长行应该在括号表达式的包裹下换行。这比反斜杠做为续行符更好。

反斜杠有时仍然适用。例如,多个很长的with语句不能使用隐式续行,所以反斜杠是可接受的。

with open('/path/to/some/file/you/want/to/read') as file_1, \
     open('/path/to/some/file/being/written', 'w') as file_2:
    file_2.write(file_1.read())
(见前面关于多行if语句的讨论来进一步思考这种多行with语句该如何缩进)

另外一种使用反斜杠续行的案例是assert语句。

确保续行的缩进是恰到好处的。遇到二元操做符,首选的断行位置是操做符的后面而不是前面。这有一些例子:

class Rectangle(Blob):

    def __init__(self, width, height,
                 color='black', emphasis=None, highlight=0):
        if (width == 0 and height == 0 and
                color == 'red' and emphasis == 'strong' or
                highlight > 100):
            raise ValueError("sorry, you lose")
        if width == 0 and height == 0 and (color == 'red' or
                                           emphasis is None):
            raise ValueError("I don't think so -- values are %s, %s" %
                             (width, height))
        Blob.__init__(self, width, height,
                      color, emphasis, highlight)
2.3.4 空行
顶级函数和类定义上下使用两个空行分隔。

类内的方法定义使用一个空行分隔。

可使用额外的空行(有节制的)来分隔相关联的函数组。在一系列相关联的单行代码中空行能够省略(e.g. 一组虚拟的实现)。

在函数中使用空白行(有节制的)来代表逻辑部分。

Python 接受使用换页符(i.e. Ctrl+L)做为空格;许多工具都把Ctrl+L做为分页符,所以你能够用它们把你的文件中类似的章节分页。注意,一些编辑器和基于 Web 的代码查看工具可能不把Ctrl+L看作分页符,而是在这个位置放一个其它的符号。

2.3.5 源文件编码
在核心 Python 发布版中的代码应该老是使用UTF-8编码(或者在 Python 2 中使用ASCII)。

使用ASCII(Python 2)或UTF-8(Python 3)的文件不须要有编码声明(注:它们是默认的)。

在标准库中,非缺省的编码应该仅仅用于测试目的,或者注释或文档字符串中的做者名包含非ASCII码字符;不然,优先使用\x、\u、\U或者\N来转义字符串中的非ASCII数据。

对于 Python 3.0 和以后的版本,如下是有关标准库的政策(见PEP 3131):全部 Python 标准库中的标识符必须使用只含ASCII的标识,而且应该使用英语单词只要可行(在多数状况下,缩略语和技术术语哪一个不是英语)。此外,字符串和注释也必须是ASCII。仅有的例外是:(a)测试用例测试非ASCII特性时,(b)做者名。做者的名字不是基于拉丁字母的必须提供他们名字的拉丁字母音译。

面向全球用户的开源项目,鼓励采起类似的政策。

2.3.6. 导入包
import不一样的模块应该独立一行,如:

好的:

import os
import sys
很差的:

import sys, os
这样也是可行的:

from subprocess import Popen, PIPE
import语句应该老是放在文件的顶部,在模块注释和文档字符串之下,在模块全局变量和常量以前。

import语句分组顺序以下:

导入标准库模块
导入相关第三方库模块
导入当前应用程序/库模块
每组之间应该用空行分开。

而后用__all__声明本文件内的模块。

绝对导入是推荐的,它们一般是更可读的,而且在错误的包系统配置(如一个目录包含一个以os.path结尾的包)下有良好的行为倾向(至少有更清晰的错误消息):

import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example
固然,相对于绝对导入,相对导入是个可选替代,特别是处理复杂的包结构时,绝对导入会有没必要要的冗余:

from . import sibling
from .sibling import example
标准库代码应该避免复杂的包结构,而且永远使用绝对导入。

应该从不使用隐式的相对导入,并且在 Python 3 中已经被移除。

从一个包含类的模块导入类时,这样写一般是可行的:
from myclass import MyClass
from foo.bar.yourclass import YourClass
若是上面的方式会本地致使命名冲突,则这样写:

import myclass
import foo.bar.yourclass
以myclass.MyClass和foo.bar.yourclass.YourClass这样的方式使用。

应该避免通配符导入(from import *),这会使名称空间里存在的名称变得不清晰,迷惑读者和自动化工具。这里有一个可辩护的通配符导入用例,,从新发布一个内部接口做为公共 API 的一部分(例如,使用纯 Python 实现一个可选的加速器模块的接口,但并不能预知这些定义会被覆盖)。

当以这种方式从新发布名称时,下面关于公共和内部接口的指南仍然适用。

2.4 字符串引号
在 Python 里面,单引号字符串和双引号字符串是相同的。这份指南对这个不会有所建议。选择一种方式并坚持使用。一个字符串同时包含单引号和双引号字符时,用另一种来包裹字符串,而不是使用反斜杠来转义,以提升可读性。

对于三引号字符串,老是使用双引号字符来保持与文档字符串约定的一致性(PEP 257)。

2.5 表达式和语句中的空格
2.5.1 不能忍受的状况
避免在下列状况中使用多余的空格:

与括号保持紧凑(小括号、中括号、大括号):
Yes: spam(ham[1], {eggs: 2})
No:  spam( ham[ 1 ], { eggs: 2 } )
与后面的逗号、分号或冒号保持紧凑:
Yes: if x == 4: print x, y; x, y = y, x
No:  if x == 4 : print x , y ; x , y = y , x
切片内的冒号就像二元操做符同样,任意一侧应该被等同对待(把它当作一个极低优先级的操做)。在一个可扩展的切片中,冒号两侧必须有相同的空格数量。例外:切片参数省略时,空格也省略。

好的:

ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]
很差的:

ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]
函数名与其后参数列表的左括号应该保持紧凑:
Yes: spam(1)
No:  spam (1)
与切片或索引的左括号保持紧凑:
Yes: dct['key'] = lst[index]
No:  dct ['key'] = lst [index]
在赋值操做符(或其它)的两侧保持多余一个的空格:

好的:

x = 1
y = 2
long_variable = 3
很差的:

x             = 1
y             = 2
long_variable = 3
2.5.2 其余建议
老是在这些二元操做符的两侧加入一个空格:赋值(=),增量赋值(+=, -= etc.),比较(==, <, >, !=, <>, <=, >=, in, not in, is, is not),布尔运算(and, or, not)。

在不一样优先级之间,考虑在更低优先级的操做符两侧插入空格。用你本身的判断力;但不要使用超过一个空格,而且在二元操做符的两侧有相同的空格数。

好的:

i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
很差的:

i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
不要在关键值参数或默认值参数的等号两边加入空格。

好的:

def complex(real, imag=0.0):
    return magic(r=real, i=imag)
很差的:

def complex(real, imag = 0.0):
    return magic(r = real, i = imag)
【注:Python 3】带注释的函数定义中的等号两侧要各插入空格。此外,在冒号后用一个单独的空格,也要在代表函数返回值类型的->左右各插入一个空格。

好的:

def munge(input: AnyStr):
def munge(sep: AnyStr = None):
def munge() -> AnyStr:
def munge(input: AnyStr, sep: AnyStr = None, limit=1000):
很差的:

def munge(input: AnyStr=None):
def munge(input:AnyStr):
def munge(input: AnyStr)->PosInt:
打消使用复合语句(多条语句在同一行)的念头。

好的:

if foo == 'blah':
    do_blah_thing()
do_one()
do_two()
do_three()
宁肯不:

if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()
有时候把 if/for/while 和一个小的主体放在同一行也是可行的,千万不要在有多条语句的状况下这样作。此外,还要避免折叠,例如长行。

宁肯不:

if foo == 'blah': do_blah_thing()
for x in lst: total += x
while t < 10: t = delay()
绝对不:

if foo == 'blah': do_blah_thing()
else: do_non_blah_thing()

try: something()
finally: cleanup()

do_one(); do_two(); do_three(long, argument,
                             list, like, this)

if foo == 'blah': one(); two(); three()
2.6 注释
与代码相矛盾的注释不如没有。注释老是随着代码的变动而更新。

注释应该是完整的句子。若是注释是一个短语或语句,第一个单词应该大写,除非是一个开头是小写的标识符(从不改变标识符的大小写)。

若是注释很短,末尾的句点能够省略。块注释一般由一个或多个有完整句子的段落组成,而且每一个句子应该由句点结束。

你应该在一个句子的句点后面用两个空格。

写英语时,遵循《Strunk and White》(注:《英文写做指南》,参考维基百科)。

来自非英语国家的程序员:请用英语写注释,除非你120%肯定你的代码永远不会被那些不说你的语言的人阅读。

2.6.1 块注释
块注释一般用来讲明跟随在其后的代码,应该与那些代码有相同的缩进层次。块注释每一行以#起头,而且#后要跟一个空格(除非是注释内的缩进文本)。

2.6.2 行内注释
有节制的使用行内注释。

一个行内注释与语句在同一行。行内注释应该至少与语句相隔两个空格。以#打头,#后接一个空格。

无谓的行内注释若是状态明显,会转移注意力。不要这样作:

x = x + 1                 # Increment x
但有的时候,这样是有用的:

x = x + 1                 # Compensate for border
2.6.3 文档字符串
编写良好的文档字符串(a.k.a “docstring”)的约定常驻在 PEP 257

为全部的公共模块、函数、类和方法编写文档字符串。对于非公共的方法,文档字符串是没必要要的,可是也应该有注释来讲明代码是干什么的。这个注释应该放在方法声明的下面。
PEP 257 描述了良好的文档字符串的约定。注意,文档字符串的结尾"""应该放在单独的一行,例如:
"""Return a foobang

Optional plotz says to frobnicate the bizbaz first.
"""
对于单行的文档字符串,把结尾"""放在同一行。
2.7 版本注记
若是必需要 Subversion,CVS 或 RCS 标记在你的源文件里,像这样作:

__version__ = "$Revision$"
# $Source$
这几行应该在模块的文档字符串后面,其它代码的前面,上下由一个空行分隔。

2.8 命名约定
Python 库的命名规则有点混乱,所以咱们永远也不会使其彻底一致的 – 不过,这里有一些当前推荐的命名标准。新的模块和包(包括第三方框架)应该按照这些标准来命名,可是已存在库有不一样的风格,内部一致性是首选。

2.8.1 覆盖原则
API 里对用户可见的公共部分应该遵循约定,反映的是使用而不是实现。

2.8.2 规定:命名约定
有许多不一样的命名风格。这有助于识别正在使用的命名风格,独立于它们的用途。

下面的命名风格一般是有区别的:

b (一个小写字母)
B (一个大写字母)
lowercase
lower_case_with_underscores
UPPERCASE
UPPER_CASE_WITH_UNDERSCORES
CapitalizedWords (又叫 CapWords,或者 CamelCase(骆驼命名法) – 如此命名由于字母看起来崎岖不平[3]。有时候也叫 StudlyCaps。
注意:在 CapWords 使用缩略语时,全部缩略语的首字母都要大写。所以HTTPServerError比HttpServerError要好。

mixedCase (和上面不一样的是首字母小写)
Capitalized_Words_With_Underscores (丑陋无比!)
也有种风格用独一无二的短前缀来将类似的命名分组。在 Python 里用的不是不少,可是为了完整性被说起。例如,os.stat()函数返回一个元组,一般有像st_mode,st_size,st_mtime等名字。(强调与 POSIX 系统调用的字段结构一致,有助于程序员对此更熟悉)

X11 库的全部公共函数都用 X 打头。在 Python 中这种风格被认为是不重要的,由于属性和方法名的前缀是一个对象,函数名的前缀为一个模块名。

此外,下面的特许形式用一个前导或尾随的下划线进行识别(这些一般能够和任何形式的命名约定组合):

_single_leading_underscore :仅内部使用的标识,如from M import *不会导入像这样一下划线开头的对象。
single_trailing_underscore_ : 一般是为了不与 Python 规定的关键字冲突,如Tkinter.Toplevel(master, class_='ClassName')。
double_leading_underscore : 命名一个类属性,调用的时候名字会改变(在类FooBar中,`boo变成了_FooBar__boo`;见下)。
double_leading_and_trailing_underscore :”魔术”对象或属性,活在用户控制的命名空间里。例如,__init__,__import__和__file__。永远不要像这种方式命名;只把它们做为记录。
2.8.3 规定:命名约定
2.8.3.1 应该避免的名字
永远不要使用单个字符l(小写字母 el),O(大写字母 oh),或I(大写字母 eye)做为变量名。

在一些字体中,这些字符是没法和数字1和0区分开的。试图使用l时用L代替。

2.8.3.2 包和模块名
模块名应该短,且全小写。若是能改善可读性,可使用下划线。Python 的包名也应该短,所有小写,可是不推荐使用下划线。

由于模块名就是文件名,而一些文件系统是大小写不敏感的,而且自动截断长文件名,因此给模块名取一个短小的名字是很是重要的 – 在 Unix 上这不是问题,可是把代码放到老版本的 Mac, Windows,或者 DOS 上就可能变成一个问题了。

用 C/C++ 给 Python 写一个高性能的扩展(e.g. more object oriented)接口的时候,C/C++ 模块名应该有一个前导下划线。

2.8.3.3 类名
类名一般使用 CapWords 约定。

The naming convention for functions may be used instead in cases where the interface is documented and used primarily as a callable.

注意和内建名称的区分开:大多数内建名称是一个单独的单词(或两个单词一块儿),CapWords 约定只被用在异常名和内建常量上。

2.8.3.4 异常名
由于异常应该是类,因此类名约定在这里适用。可是,你应该用Error做为你的异常名的后缀(异常其实是一个错误)。

2.8.3.5 全局变量名
(咱们但愿这些变量仅仅在一个模块内部使用)这个约定有关诸如此类的变量。

若被设计的模块能够经过from M import *来使用,它应该使用__all__机制来代表那些能够可导出的全局变量,或者使用下划线前缀的全局变量代表其是模块私有的。

2.8.3.6 函数名
函数名应该是小写的,有必要的话用下划线来分隔单词提升可读性。

混合大小写仅仅在上下文都是这种风格的状况下容许存在(如thread.py),这是为了维持向后兼容性。

2.8.3.7 函数和方法参数
老是使用self做为实例方法的第一个参数。

老是使用cls做为类方法的第一个参数。

若是函数参数与保留关键字冲突,一般最好在参数后面添加一个尾随的下划线,而不是使用缩写或胡乱拆减。所以class_比clss要好。(或许避免冲突更好的方式是使用近义词)

2.8.3.8 方法名和实例变量
用函数名的命名规则:所有小写,用下划线分隔单词提升可读性。

用一个且有一个前导的下划线来代表非公有的方法和实例变量。

为了不与子类变量或方法的命名冲突,用两个前导下划线来调用 Python 的命名改编规则。

Python 命名改编经过添加一个类名:若是类Foo有一个属性叫__a,它不能被这样Foo.__a访问(执着的人能够经过这样Foo._Foo__a来访问)一般,双前导的下划线应该仅仅用来避免与其子类属性的命名冲突。

注意:这里有一些争议有关__names的使用(见下文)。

2.8.3.9 常量
常量一般是模块级的定义,所有大写,单词之间如下划线分隔。例如MAX_OVERFLOW和TOTAL。

2.8.3.10 继承的设计
老是决定一个类的方法和变量(属性)是应该公有仍是非公有。若是有疑问,选择非公有;相比把共有属性变非公有,非公有属性变公有会容易得多。

公有属性是你指望给那些与你的类无关的客户端使用的,你应该保证不会出现不向后兼容的改变。非公有的属性是你不打算给其它第三方使用的;你不须要保证非公有的属性不会改变甚至被移除也是能够的。

咱们这里不适用“私有”这个术语,由于在 Python 里没有真正的私有属性(通常没有没必要要的工做量)。

另外一种属性的分类是“子类 API”的一部分(一般在其它语言里叫作“Protected”)。一些类被设计成被继承的,要么扩展要么修改类的某方面行为。设计这样一个类的时候,务必作出明确的决定,哪些是公有的,其将会成为子类 API 的一部分,哪些仅仅是用于你的基类的。

处于这种考虑,给出 Pythonic 的指南:

共有属性不该该有前导下划线。
若是你的公有属性与保留关键字发生冲突,在你的属性名后面添加一个尾随的下划线。这比使用缩写或胡乱拆减要好。(尽管这条规则,已知某个变量或参数多是一个类状况下,cls是首选的命名,特别是做为类方法的第一个参数)
注意一:见上面推荐的类方法参数命名方式。

对于简单的公有数据属性,最好的方式是暴露属性名,不要使用复杂的访问属性/修改属性的方法。记住,Python 提供了捷径去提高特性,若是你发现简单的数据属性须要增长功能行为。在这种状况下,使用properties把功能实现隐藏在简单的数据属性访问语法下面。
注意一:properties仅仅在新式类下工做。   注意二:尽可能保持功能行为无边际效应,然而如缓存有边际效应也是好的。   注意三:避免为计算开销大的操做使用properties;属性标记使调用者相信这样来访问(相对来讲)是开销很低的。

若是你的类是为了被继承,你有不想让子类使用的属性,给属性命名时考虑给它们加上双前导下划线,不要加尾随下划线。这会调用 Python 的名称重整算法,把类名加在属性名前面。避免了命名冲突,当子类不当心命名了和父类属性相同名称的时候。
注意一:注意只是用了简单的类名来重整名字,所以若是子类和父类同名的时候,你仍然有能力避免冲突。

注意二:命名重整有肯定的用途,例如调试和__getattr__(),就不太方便。命名重整算法是有据可查的,易于手动执行。

注意三:不是每一个人都喜欢命名重整。尽可能平衡名称的命名冲突与面向高级调用者的潜在用途。

2.9 公共和内部接口
保证全部公有接口的向后兼容性。用户能清晰的区分公有和内部接口是重要的。

文档化的接口考虑公有,除非文档明确的说明它们是暂时的,或者内部接口不保证其的向后兼容性。全部的非文档化的应该被假设为非公开的。

为了更好的支持内省,模块应该用__all__属性来明确规定公有 API 的名字。设置__all__为空list代表模块没有公有 API。

甚至与__all__设置至关,内部接口(包、模块、类、函数、属性或者其它的名字)应该有一个前导的下划线前缀。

被认为是内部的接口,其包含的任何名称空间(包、模块或类)也被认为是内部的。

导入的名称应始终视做一个实现细节。其它模块不能依赖间接访问这些导入的名字,除非它们是包含模块的 API 明确记载的一部分,例如os.path或一个包的__init__模块暴露了来自子模块的功能。

2.10 程序编写建议
代码的编写方式不能对其它 Python 的实现(PyPy、Jython、IronPython、Cython、Psyco,诸如此类的)不利。

例如,不要依赖于 CPython 在字符串拼接时的优化实现,像这种语句形式a += b和a = a + b。即便是 CPython(仅对某些类型起做用) 这种优化也是脆弱的,不是在全部的实现中都不使用引用计数。在库中性能敏感的部分,用''.join形式来代替。这会确保在全部不一样的实现中字符串拼接是线性时间的。

比较单例,像None应该用is或is not,从不使用==操做符。

当你的真正用意是if x is not None的时候,小心if x这样的写法 – 例如,测试一个默认值为None的变量或参数是否设置成了其它值,其它值多是那些布尔值为 false 的类型(如空容器)。

用is not操做符而不是not ... is。虽然这两个表达式是功能相同的,前一个是更可读的,是首选。

好的:

if foo is not None:
很差的:

if not foo is None:
用富比较实现排序操做的时候,实现全部六个比较操做符( __eq__ 、 __ne__ 、 __lt__ , __le__, __gt__ , __ge__)是更好的,而不是依赖其它仅仅运用一个特定比较的代码

为了最大限度的减小工做量,functools.total_ordering()装饰器提供了一个工具去生成缺乏的比较方法。

PEP 207 说明了 Python 假定的全部反射规则。所以,解释器可能交换y > x与x < y,y >= x与x <= y,也可能交换x == y和x != y。sort()和min()操做确定会使用<操做符,max()函数确定会使用>操做符。固然,最好是六个操做符都实现,以便不会在其它上下文中有疑惑。

始终使用def语句来代替直接绑定了一个lambda表达式的赋值语句。

好的:

def f(x): return 2*x
很差的:

f = lambda x: 2*x
第一种形式意味着函数对象的 __name__属性值是 'f'而不是 '<lambda>' 。一般这对异常追踪和字符串表述是更有用的。使用赋值语句消除的惟一好处,lambda表达式能够提供一个显示的def语句不能提供的,如,lambda能镶嵌在一个很长的表达式里。

异常类应派生自Exception而不是BaseException。直接继承自BaseException是为Exception保留的,若是从BaseException继承,捕获到的错误老是错的。

设计异常结构层次,应基于那些可能出现异常的代码,而不是在出现异常后的。编码的时候,以回答“出了什么问题?”为目标,而不是仅仅指出“这里出现了问题”(见 PEP 3151 一个内建异常结构层次的例子)。

类的命名约定适用于异常,若是异常类是一个错误,你应该给异常类加一个后缀Error。用于非本地流程控制或者其余形式的信号的非错误异常不须要一个特殊的后缀。

适当的使用异常链。在 Python 3 里,raise X from Y用于代表明确的替代者,不丢失原有的回溯信息。

有意替换一个内部的异常时(在 Python 2 用raise X,Python 3.3+ 用raise X from None),确保相关的细节所有转移给了新异常(例如,把KeyError变成AttributeError时保留属性名,或者把原始异常的错误信息嵌在新异常里)。

在 Python 2 里抛出异常时,用raise ValueError('message')代替旧式的raise ValueError, 'message'。

在 Python 3 以后的语法里,旧式的异常抛出方式是非法的。

使用括号形式的异常意味着,当你传给异常的参数过长或者包含字符串格式化时,你就不须要使用续行符了,这要感谢括号!

捕获异常时,尽量使用明确的异常,而不是用一个空的except:语句。

例如,用:

try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None
一个空的except:语句将会捕获到SystemExit和KeyboardInterrupt异常,很难区分程序的中断究竟是Ctrl+C仍是其余问题引发的。若是你想捕获程序的全部错误,使用except Exception:(空except:等同于except BaseException)。

一个好的经验是限制使用空except语句,除了这两种状况:

若是异常处理程序会打印出或者记录回溯信息;至少用户意识到错误的存在。
若是代码须要作一些清理工做,但后面用raise向上抛出异常。try .. finally是处理这种状况更好的方式。
绑定异常给一个名字时,最好使用 Python 2.6 里添加的明确的名字绑定语法:
try:
    process_data()
except Exception as exc:
    raise DataProcessingFailedError(str(exc))
Python 3 只支持这种语法,避免与基于逗号的旧式语法产生二义性。

捕获操做系统错误时,最好使用 Python 3.3 里引进的明确的异常结构层次,而不是自省的errno值。

此外,对于全部的try/except语句来讲,限制try里面有且仅有绝对必要的代码。在强调一次,这能避免屏蔽错误。

好的:

try:
    value = collection[key]
except KeyError:
    return key_not_found(key)
else:
    return handle_value(value)
很差的:

try:
    # Too broad!
    return handle_value(collection[key])
except KeyError:
    # Will also catch KeyError raised by handle_value()
    return key_not_found(key)
当资源是本地的特定代码段,用with语句确保其在使用后被当即干净的清除了,try/finally也是也接受的。

当它们作一些除了获取和释放资源以外的事的时候,上下文管理器应该经过单独的函数或方法调用。例如:

好的:

with conn.begin_transaction():
    do_stuff_in_transaction(conn)
很差的:

with conn:
    do_stuff_in_transaction(conn)
第二个例子没有提供任何信息来代表__enter__和__exit__方法在完成一个事务后作了一些除了关闭链接之外的其它事。在这种状况下明确是很重要的。

坚持使用return语句。函数内的return语句都应该返回一个表达式,或者None。若是一个return语句返回一个表达式,另外一个没有返回值的应该用return None清晰的说明,而且在一个函数的结尾应该明确使用一个return语句(若是有返回值的话)。

好的:

def foo(x):
    if x >= 0:
        return math.sqrt(x)
    else:
        return None

def bar(x):
    if x < 0:
        return None
    return math.sqrt(x)
很差的:

def foo(x):
    if x >= 0:
        return math.sqrt(x)

def bar(x):
    if x < 0:
        return
    return math.sqrt(x)
用字符串方法代替字符串模块。

字符串方法老是更快,与 unicode 字符串共享 API。若是须要向后兼容性覆盖这个规则,须要 Python 2.0 以上的版本。

用''.startswith()和''.endswith()代替字符串切片来检查前缀和后缀。

startswith()和endswith()是更简洁的,不容易出错的。例如:

Yes: if foo.startswith('bar'):
No:  if foo[:3] == 'bar':
对象类型的比较应该始终使用isinstance()而不是直接比较。
Yes: if isinstance(obj, int):

No:  if type(obj) is type(1):
当比较一个对象是否是字符串时,记住它有可能也是一个 unicode 字符串!在 Python 2 里面,str和unicode有一个公共的基类叫basestring,所以你能够这样作:

if isinstance(obj, basestring):
注意,在 Python 3 里面,unicode和basestring已经不存在了(只有str),byte对象再也不是字符串的一种(被一个整数序列替代)。

对于序列(字符串、列表、元组)来讲,空的序列为False:

好的:

if not seq:
if seq:
很差的:

if len(seq):
if not len(seq):
不要让字符串对尾随的空格有依赖。这样的尾随空格是视觉上没法区分的,一些编辑器(or more recently, reindent.py)会将其裁剪掉。
不要用==比较True和False。
Yes:   if greeting:
No:    if greeting == True:
Worse: if greeting is True:
Python 标准库将再也不使用函数标注,以致于给特殊的标注风格给一个过早的承若。代替的,这些标注是留给用户去发现和体验的有用的标注风格。

建议第三方实验的标注用相关的修饰符指示标注应该如何被解释。

早期的核心开发者尝试用函数标注显示不一致、特别的标注风格。例如:

[str]是很含糊的,它可能表明一个包含字符串的列表,也可能表明一个为字符串或为空的值。
open(file:(str,bytes))可能用来表示file的值能够是一个str或者bytes,也可能用来表示file的值是一个包含str和bytes的二元组。
标注seek(whence:int)体现了一个过于明确又不够明确的混合体:int太严格了(有__index__的应该被容许),又不够严格(只有0,1,2是被容许的)。一样的,标注write(b: byte)太严格了(任何支持缓存协议的都应该被容许)。
像read1(n: int=None)这样的标注自我矛盾,由于None不是int。像source_path(self, fullname:str) -> object标注是迷惑人的,返回值究竟是应该什么类型?
除了上面以外,在具体类型和抽象类型的使用上是不一致的:int对integral(整数),set/fronzenset对MutableSet/Set。
不正确的抽象基类标注规格。例如,集合之间的操做须要另外一个对象是集合的实例,而不仅是一个可迭代序列。
另外一个问题是,标注成为了规范的一部分,但却没有经受过考验。
在大多数状况下,文档字符串已经包括了类型规范,比函数标注更清晰。在其他的状况下,一旦标注被移除,文档字符串应该被完善。
观察到的函数标注太标新立异了,相关的系统不能一致的处理自动类型检查和参数验证。离开这些标注的代码之后很难作出更改,使自动化工具能够支持。

 

 

14.迭代器、生成器、装饰器



Python 迭代器(Iterators)对象在遵照迭代器协议时须要支持以下两种方法。

__iter__(),返回迭代器对象自身。这用在 for 和 in 语句中。

__next__(),返回迭代器的下一个值。若是没有下一个值能够返回,那么应该抛出 StopIteration 异常。

class Counter(object):
    def __init__(self, low, high):
        self.current = low
        self.high = high

    def __iter__(self):
        return self

    def __next__(self):
        #返回下一个值直到当前值大于 high
        if self.current > self.high:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1
如今咱们能把这个迭代器用在咱们的代码里。

>>> c = Counter(5,10)
>>> for i in c:
...   print(i, end=' ')
...
5 6 7 8 9 10
请记住迭代器只能被使用一次。这意味着迭代器一旦抛出 StopIteration,它会持续抛出相同的异常。

>>> c = Counter(5,6)
>>> next(c)
5
>>> next(c)
6
>>> next(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in next
StopIteration
>>> next(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in next
StopIteration
咱们已经看过在 for 循环中使用迭代器的例子了,下面的例子试图展现迭代器被隐藏的细节:

>>> iterator = iter(c)
>>> while True:
...     try:
...         x = iterator.__next__()
...         print(x, end=' ')
...     except StopIteration as e:
...         break
...
5 6 7 8 9 10
3、生成器
在这一节咱们学习有关 Python 生成器(Generators)的知识。生成器是更简单的建立迭代器的方法,这经过在函数中使用 yield 关键字完成:

>>> def my_generator():
...     print("Inside my generator")
...     yield 'a'
...     yield 'b'
...     yield 'c'
...
>>> my_generator()
<generator object my_generator at 0x7fbcfa0a6aa0>
在上面的例子中咱们使用 yield 语句建立了一个简单的生成器。咱们能在 for 循环中使用它,就像咱们使用任何其它迭代器同样。

>>> for char in my_generator():
...     print(char)
...
Inside my generator
a
b
c
在下一个例子里,咱们会使用一个生成器函数完成与 Counter 类相同的功能,而且把它用在 for 循环中。

>>> def counter_generator(low, high):
...     while low <= high:
...        yield low
...        low += 1
... 
>>> for i in counter_generator(5,10):
...     print(i, end=' ')
... 
5 6 7 8 9 10
在 While 循环中,每当执行到 yield 语句时,返回变量 low 的值而且生成器状态转为挂起。在下一次调用生成器时,生成器从以前冻结的地方恢复执行而后变量 low 的值增一。生成器继续 while 循环而且再次来到 yield 语句...

当你调用生成器函数时它返回一个生成器对象。若是你把这个对象传入 dir() 函数,你会在返回的结果中找到 __iter__ 和 __next__ 两个方法名。

咱们一般使用生成器进行惰性求值。这样使用生成器是处理大数据的好方法。若是你不想在内存中加载全部数据,你可使用生成器,一次只传递给你一部分数据。

os.path.walk() 函数是最典型的这样的例子,它使用一个回调函数和当前的 os.walk 生成器。使用生成器实现节约内存。

咱们可使用生成器产生无限多的值。如下是一个这样的例子。

>>> def infinite_generator(start=0):
...     while True:
...         yield start
...         start += 1
...
>>> for num in infinite_generator(4):
...     print(num, end=' ')
...     if num > 20:
...         break
...
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
若是咱们回到 my_generator() 这个例子,咱们会发现生成器的一个特色:它们是不可重复使用的。

>>> g = my_generator()
>>> for c in g:
...     print(c)
...
Inside my generator
a
b
c
>>> for c in g:
...     print(c)
...
一个建立可重复使用生成器的方式是不保存任何状态的基于对象的生成器。任何一个生成数据的含有 __iter__ 方法的类均可以用做对象生成器。在下面的例子中咱们从新建立了 counter 生成器。

>>> class Counter(object):
...     def __init__(self, low, high):
...         self.low = low
...         self.high = high
...     def __iter__(self):
...          counter = self.low
...          while self.high >= counter:
...              yield counter
...              counter += 1
...
>>> gobj = Counter(5, 10)
>>> for num in gobj:
...     print(num, end=' ')
...
5 6 7 8 9 10
>>> for num in gobj:
...     print(num, end=' ')
...
5 6 7 8 9 10
上面的 gobj 并非生成器或迭代器,由于它不具备 __next__ 方法,只是一个可迭代对象,生成器是必定不能重复循环的。 若是想要使类的实例变成迭代器,能够用 __iter__ + __next__ 方法实现:

>>> from collections import Iterator
>>> class Test():
...:     def __init__(self, a, b):
...:         self.a = a
...:         self.b = b
...:     def __iter__(self):
...:         return self
...:     def __next__(self):
...:         self.a += 1
...:         if self.a > self.b:
...:             raise StopIteration()
...:         return self.a
...:     

>>> test = Test(5, 10)

>>> isinstance(test, Iterator)
True
4、生成器表达式
在这一节咱们学习生成器表达式(Generator expressions),生成器表达式是列表推导式和生成器的一个高性能,内存使用效率高的推广。

举个例子,咱们尝试对 1 到 9 的全部数字进行平方求和。

>>> sum([x*x for x in range(1,10)])
这个例子实际上首先在内存中建立了一个平方数值的列表,而后遍历这个列表,最终求和后释放内存。你能理解一个大列表的内存占用状况是怎样的。

咱们能够经过使用生成器表达式来节省内存使用。

>>> sum(x*x for x in range(1,10))
生成器表达式的语法要求其老是直接在在一对括号内,而且不能在两边有逗号。这基本上意味着下面这些例子都是有效的生成器表达式用法示例:

>>> sum(x*x for x in range(1,10))
285
>>> g = (x*x for x in range(1,10))
>>> g
<generator object <genexpr> at 0x7fc559516b90>
咱们能够把生成器和生成器表达式联系起来,在下面的例子中咱们会读取文件 '/var/log/cron' 而且查看任意指定任务(例中咱们搜索 'anacron' )是否成功运行。

咱们能够用 shell 命令 tail -f /etc/crontab |grep anacron 完成一样的事(按 Ctrl + C 终止命令执行)。

>>> jobtext = 'anacron'
>>> all = (line for line in open('/etc/crontab', 'r') )
>>> job = ( line for line in all if line.find(jobtext) != -1)
>>> text = next(job)
>>> text
'25 6\t* * *\troot\ttest -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )\n'
>>> text = next(job)
>>> text 
'47 6\t* * 7\troot\ttest -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )\n'
>>> text = next(job)
>>> text
'52 6\t1 * *\troot\ttest -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )\n'
你能够写一个 for 循环遍历全部行。

5、闭包
闭包(Closures)是由另一个函数返回的函数。咱们使用闭包去除重复代码。在下面的例子中咱们建立了一个简单的闭包来对数字求和。

>>> def add_number(num):
...     def adder(number):
...         #adder 是一个闭包
...         return num + number
...     return adder
...
>>> a_10 = add_number(10)
>>> a_10(21)
31
>>> a_10(34)
44
>>> a_5 = add_number(5)
>>> a_5(3)
8
adder 是一个闭包,把一个给定的数字与预约义的一个数字相加。

6、装饰器
装饰器(Decorators)用来给一些对象动态的添加一些新的行为,咱们使用过的闭包也是这样的。

咱们会建立一个简单的示例,将在函数执行先后打印一些语句。

>>> def my_decorator(func):
...     def wrapper(*args, **kwargs):
...         print("Before call")
...         result = func(*args, **kwargs)
...         print("After call")
...         return result
...     return wrapper
...
>>> @my_decorator
... def add(a, b):
...     #咱们的求和函数
...     return a + b
...
>>> add(1, 3)
Before call
After call
4

 

16.virtualenv

虚拟的 Python 环境(简称 venv) 是一个能帮助你在本地目录安装不一样版本的 Python 模块的 Python 环境,你能够再也不须要在你系统中安装全部东西就能开发并测试你的代码。

2、安装 virtualenv
首先安装 pip3,打开 xfce 终端输入下面的命令:

$ sudo apt-get update
$ sudo apt-get install python3-pip
用以下命令安装 virtualenv:

$ sudo pip3 install virtualenv

3、用法
咱们会建立一个叫作 virtual 的目录,在里面咱们会有两个不一样的虚拟环境。

$ cd /home/shiyanlou
$ mkdir virtual
下面的命令建立一个叫作 virt1 的环境。

$ cd virtual
$ virtualenv virt1

如今咱们激活这个 virt1 环境。

$ source virt1/bin/activate
(virt1)shiyanlou:~/$
提示符的第一部分是当前虚拟环境的名字,当你有多个环境的时候它会帮助你识别你在哪一个环境里面。

如今咱们将安装 redis 这个 Python 模块。

(virt1)$ sudo pip3 install redis

使用 deactivate 命令关闭虚拟环境。

(virt1)$ deactivate
$

17.测试

在计算机编程中,单元测试(英语:Unit Testing)又称为模块测试, 是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工做。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

单元测试模块
在 Python 里咱们有 unittest 这个模块来帮助咱们进行单元测试

阶乘计算程序
在这个例子中咱们将写一个计算阶乘的程序 /home/shiyanlou/factorial.py:

import sys

def fact(n):
    """
    阶乘函数

    :arg n: 数字
    :returns: n 的阶乘

    """
    if n == 0:
        return 1
    return n * fact(n -1)

def div(n):
    """
    只是作除法
    """
    res = 10 / n
    return res


def main(n):
    res = fact(n)
    print(res)

if __name__ == '__main__':
    if len(sys.argv) > 1:
        main(int(sys.argv[1]))
运行程序:

$ python3 factorial.py 5
3.1 第一个测试用例
测试哪一个函数?
正如你所看到的, fact(n) 这个函数执行全部的计算,因此咱们至少应该测试这个函数。

编辑 /home/shiyanlou/factorial_test.py 文件,代码以下:

import unittest
from factorial import fact

class TestFactorial(unittest.TestCase):
    """
    咱们的基本测试类
    """

    def test_fact(self):
        """
        实际测试
        任何以 `test_` 开头的方法都被视做测试用例
        """
        res = fact(5)
        self.assertEqual(res, 120)


if __name__ == '__main__':
    unittest.main()
运行测试:

$ python3 factorial_test.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
说明

咱们首先导入了 unittest 模块,而后测试咱们须要测试的函数。

测试用例是经过子类化 unittest.TestCase 建立的。

如今咱们打开测试文件而且把 120 更改成 121,而后看看会发生什么?

3.2 各种 assert 语句
Method    Checks that    New in
assertEqual(a, b)    a == b    
assertNotEqual(a, b)    a != b    
assertTrue(x)    bool(x) is True    
assertFalse(x)    bool(x) is False    
assertIs(a, b)    a is b    2.7
assertIsNot(a, b)    a is not b    2.7
assertIsNone(x)    x is None    2.7
assertIsNotNone(x)    x is not None    2.7
assertIn(a, b)    a in b    2.7
assertNotIn(a, b)    a not in b    2.7
assertIsInstance(a, b)    isinstance(a, b)    2.7
assertNotIsInstance(a, b)    not isinstance(a, b)    2.7

 

3.3 异常测试
若是咱们在 factorial.py 中调用 div(0),咱们能看到异常被抛出。

咱们也能测试这些异常,就像这样:

self.assertRaises(ZeroDivisionError, div, 0)
完整代码:

import unittest
from factorial import fact, div

class TestFactorial(unittest.TestCase):
    """
    咱们的基本测试类
    """

    def test_fact(self):
        """
        实际测试
        任何以 `test_` 开头的方法都被视做测试用例
        """
        res = fact(5)
        self.assertEqual(res, 120)

    def test_error(self):
        """
        测试由运行时错误引起的异常
        """
        self.assertRaises(ZeroDivisionError, div, 0)

if __name__ == '__main__':
    unittest.main()
3.4 mounttab.py
mounttab.py 中只有一个 mount_details() 函数,函数分析并打印挂载详细信息。

import os


def mount_details():
    """
    打印挂载详细信息
    """
    if os.path.exists('/proc/mounts'):
        fd = open('/proc/mounts')
        for line in fd:
            line = line.strip()
            words = line.split()
            print('{} on {} type {}'.format(words[0],words[1],words[2]), end=' ')
            if len(words) > 5:
                print('({})'.format(' '.join(words[3:-2])))
            else:
                print()
        fd.close()


if __name__ == '__main__':
    mount_details()

3.5 测试覆盖率
测试覆盖率是找到代码库未经测试的部分的简单方法。它并不会告诉你的测试好很差。

在 Python 中咱们已经有了一个不错的覆盖率工具来帮助咱们。你能够在实验楼环境中安装它:

$ sudo pip3 install coverage

覆盖率示例
$ coverage3 run mounttest.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.013s

OK
$ coverage3 report -m
Name           Stmts   Miss  Cover   Missing
--------------------------------------------
mounttab2.py      22      7    68%   16, 25-3

0, 34
mounttest.py      14      0   100%
--------------------------------------------
TOTAL             36      7    81%
咱们还可使用下面的命令以 HTML 文件的形式输出覆盖率结果,而后在浏览器中查看它。

$ coverage3 html

 

18.项目结构

阐述了一个完整的 Python 项目结构,你可使用什么样的目录布局以及怎样发布软件到网络上

2、建立 Python 项目
咱们的实验项目名为 factorial,放到 /home/shiyanlou/factorial 目录:

$ cd /home/shiyanlou
$ mkdir factorial
$ cd factorial/
咱们给将要建立的 Python 模块取名为 myfact,所以咱们下一步建立 myfact 目录。

$ mkdir myfact
$ cd myfact/
主代码将在 fact.py 文件里面。

"myfact module"

def factorial(num):
    """
    返回给定数字的阶乘值

    :arg num: 咱们将计算其阶乘的整数值

    :return: 阶乘值,若传递的参数为负数,则为 -1
    """
    if num >= 0:
        if num == 0:
            return 1
        return num * factorial(num -1)
    else:
        return -1
咱们还有模块的 __init__.py 文件,内容以下:

from fact import factorial
__all__ = [factorial, ]
咱们还在 factorial 目录下添加了一个 README.rst 文件。所以,目录结构看起来像下面这样:

2.1 MANIFEST.in
如今咱们要写一个 /home/shiyanlou/factorial/MANIFEST.in 文件,它用来在使用 sdist 命令的时候找出将成为项目源代码压缩包一部分的全部文件。

include *.py
include README.rst
若是你想要排除某些文件,你能够在这个文件中使用 exclude 语句。

2.2 安装 python-setuptools 包
咱们使用 virtualenv(这里不示范步骤)。

$ sudo pip3 install setuptools
2.3 setup.py
最终咱们须要写一个 /home/shiyanlou/factorial/setup.py,用来建立源代码压缩包或安装软件。

#!/usr/bin/env python3
"""Factorial project"""
from setuptools import find_packages, setup

setup(name = 'factorial',
    version = '0.1',
    description = "Factorial module.",
    long_description = "A test module for our book.",
    platforms = ["Linux"],
    author="ShiYanLou",
    author_email="support@shiyanlou.com",
    url="https://www.shiyanlou.com/courses/596",
    license = "MIT",
    packages=find_packages()
    )
name 是项目名称,version 是发布版本,description 和 long_description 分别是项目介绍,项目长描述。platforms 是此模块的支持平台列表。find_packages() 是一个能在你源目录下找到全部模块的特殊函数,packaging docs。

2.3.1. setup.py 用例
要建立一个源文件发布版本,执行如下命令。

$ python3 setup.py sdist
执行完毕会返回相似下面的信息:

running sdist
running egg_info
creating factorial.egg-info
writing factorial.egg-info/PKG-INFO
writing top-level names to factorial.egg-info/top_level.txt
writing dependency_links to factorial.egg-info/dependency_links.txt
writing manifest file 'factorial.egg-info/SOURCES.txt'
reading manifest file 'factorial.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'factorial.egg-info/SOURCES.txt'
running check
creating factorial-0.1
creating factorial-0.1/factorial.egg-info
creating factorial-0.1/myfact
making hard links in factorial-0.1...
hard linking MANIFEST.in -> factorial-0.1
hard linking README.rst -> factorial-0.1
hard linking setup.py -> factorial-0.1
hard linking factorial.egg-info/PKG-INFO -> factorial-0.1/factorial.egg-info
hard linking factorial.egg-info/SOURCES.txt -> factorial-0.1/factorial.egg-info
hard linking factorial.egg-info/dependency_links.txt -> factorial-0.1/factorial.egg-info
hard linking factorial.egg-info/top_level.txt -> factorial-0.1/factorial.egg-info
hard linking myfact/__init__.py -> factorial-0.1/myfact
hard linking myfact/fact.py -> factorial-0.1/myfact
Writing factorial-0.1/setup.cfg
creating dist
Creating tar archive
removing 'factorial-0.1' (and everything under it)
咱们能在 dist 目录下看到一个 tar 压缩包。

$ ls dist/
factorial-0.1.tar.gz
记住尝试安装代码时使用 virtualenv。

执行下面的命令从源代码安装。

$ sudo python3 setup.py install
学习更多可前往 packaging.python.org。

2.4 Python Package Index (PyPI)
你还记得咱们常用的 pip 命令吗?有没有想过这些包是从哪里来的?答案是 PyPI。这是 Python 的软件包管理系统。

为了实验,咱们会使用 PyPI 的测试服务器 https://testpypi.python.org/pypi。

2.4.1 建立帐号
首先在这个连接注册帐号。你会收到带有连接的邮件,点击这个连接确认你的注册。

建立 ~/.pypirc 文件,存放你的帐号详细信息,其内容格式以下:

[distutils]
index-servers = pypi
    testpypi

[pypi]
repository: https://upload.pypi.org/legacy/
username: <username>
password: <password>

[testpypi]
repository:https://test.pypi.org/legacy/
username: <username>
password: <password>

替换 <username> 和 <password> 为您新建立的账户的详细信息。在这里,因为咱们是到 testpypi的网页上去注册帐号,即将相应的服务上传到 testpypi,因此在这里,你只需修改[testpypi]的用户名和密码

记得在 setup.py 中更改项目的名称为其它的名字来测试下面的指令,在接下来的命令中我将项目名称修改成factorial2,为了避免重复,须要自行修改至其它名称。

2.4.2 上传到 TestPyPI 服务
下一步咱们会将咱们的项目到 TestPyPI 服务。这经过 twine 命令完成。

咱们也会使用 -r 把它指向测试服务器。

$ sudo pip3 install twine
$ twine upload dist/* -r testpypi
执行完毕会返回相似下面的信息,

Uploading distributions to https://test.pypi.org/legacy/
Uploading factorial2-0.1.tar.gz
在这里你也可使用下面的命令上传到 PyPI 服务上,但这里须要注意,在 ~/.pypirc 里面,你须要到 https://pypi.python.org页面,按照上面的步骤去注册一个帐号,而后到~/.pypirc 的 [pypi] 下填写相应的用户名和密码。testpypi 和 pypi 的帐号密码并不通用。

$ twine upload dist/* -r testpypi
如今若是你浏览这个页面,你会发现你的项目已经准备好被别人使用了。

 

19.Flask介绍

什么是 Flask?
Flask 是一个 web 框架。也就是说 Flask 为你提供工具,库和技术来容许你构建一个 web 应用程序。这个 web 应用程序能够是一些 web 页面、博客、wiki、基于 web 的日历应用或商业网站。

Flask 属于微框架(micro-framework)这一类别,微架构一般是很小的不依赖于外部库的框架。这既有优势也有缺点,优势是框架很轻量,更新时依赖少,而且专一安全方面的 bug,缺点是,你不得不本身作更多的工做,或经过添加插件增长本身的依赖列表。Flask 的依赖以下:

Werkzeug 一个 WSGI 工具包
jinja2 模板引擎

Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口)。自从WSGI被开发出来之后,许多其它语言中也出现了相似接口。

3、"Hello World" 应用
咱们将使用 flask 完成一个很是基础的应用。

安装 flask
$ sudo pip3 install flask
建立项目结构
$ cd /home/shiyanlou
$ mkdir -p hello_flask/{templates,static}
这是你的 web 应用的基本结构:

$ tree hello_flask/
hello_flask
|-- static
`-- templates

2 directories, 0 files
templates 文件夹是存放模板的地方,static 文件夹存放 web 应用所需的静态文件(images, css, javascript)。

 

3、"Hello World" 应用
咱们将使用 flask 完成一个很是基础的应用。

安装 flask
$ sudo pip3 install flask
建立项目结构
$ cd /home/shiyanlou
$ mkdir -p hello_flask/{templates,static}
这是你的 web 应用的基本结构:

$ tree hello_flask/
hello_flask
|-- static
`-- templates

2 directories, 0 files
templates 文件夹是存放模板的地方,static 文件夹存放 web 应用所需的静态文件(images, css, javascript)。

建立应用文件
$ cd hello_flask
$ vim hello_flask.py
hello_flask.py 文件里编写以下代码:

#!/usr/bin/env python3

import flask


# Create the application.
APP = flask.Flask(__name__)


@APP.route('/')
def index():
    """ 显示可在 '/' 访问的 index 页面
    """
    return flask.render_template('index.html')


if __name__ == '__main__':
    APP.debug=True
    APP.run()
建立模板文件 index.html
$ vim templates/index.html
index.html 文件内容以下:

<!DOCTYPE html>
<html lang='en'>
<head>
  <meta charset="utf-8" />
  <title>Hello world!</title>
  <link type="text/css" rel="stylesheet"
        href="{{ url_for('static',
              filename='hello.css')}}" />
</head>
<body>

It works!

</body>
</html>

 

运行 flask 应用程序
$ python3 hello_flask.py
访问 http://127.0.0.1:5000/,这应该只是显示黑字白底的 "It works!" 文本,以下图:

4、Flask 中使用参数
在本节中咱们将要看到如何根据用户使用的 URL 返回网页。

为此咱们更新 hello_flask.py 文件。

在 hello_flask.py 文件中添加如下条目
@APP.route('/hello/<name>/')
def hello(name):
    """ Displays the page greats who ever comes to visit it.
    """
    return flask.render_template('hello.html', name=name)
建立下面这个模板 hello.html
<!DOCTYPE html>
<html lang='en'>
<head>
    <meta charset="utf-8" />
    <title>Hello</title>
    <link type="text/css" rel="stylesheet"
          href="{{ url_for('static',
               filename='hello.css')}}" />
</head>
<body>

      Hello {{name}}

</body>
</html>

运行 flask 应用
$ python3 hello_flask.py
访问 http://127.0.0.1:5000/ ,这应该只是显示黑字白底的 "It works!" 文本。

访问http://127.0.0.1:5000/hello/you,这应该返回文本 "Hello you",见下图:

不管你在 URL 中 /hello/ 后填写的什么,都会出如今返回的网页中。

这是你第一次使用模板,咱们在 hello_flask.py 中创建了 name 变量(参见 hello 函数的 return 行)。经过语法 {{name}},name 变量以后在页面中显示其自身。

5、额外工做
目前,对于每个页面咱们都建立了一个模板,其实这是很差的作法,咱们应该作的是建立一个主模板而且在每一个页面使用它。

建立模板文件 master.html。
<!DOCTYPE html>
<html lang='en'>
<head>
  <meta charset="utf-8" />
  <title>{% block title %}{% endblock %} - Hello Flask!</title>
  <link type="text/css" rel="stylesheet"
       href="{{ url_for('static',
                        filename='hello.css')}}" />
</head>
<body>

{% block body %}{% endblock %}

</body>
</html>

调整模板 index.html。
{% extends "master.html" %}

{% block title %}Home{% endblock %}

{% block body %}
It works!
{% endblock %}
正如你所看到的,在 master.html 模板中咱们定义了两部分,名为 title 和 body 的 blocks。

在模板 index.html 中,咱们声明这个模板扩展自 master.html 模板,而后咱们定义了内容来放在这两个部分中(blocks)。在第一个 block title 中,咱们放置了 Home 单词,在第二个 block body 中咱们定义了咱们想要在页面的 body 中有的东西。

做为练习,更改其余模板 hello.html,一样要使用 master.html。
在 hello 页面添加首页连接。
调整模板 hello.html,添加到首页的连接。

<a href="{{ url_for('index') }}"><button>Home</button></a> 做为你的任务,在首页添加到 hello 页面的连接。