python基础学习
简介
龟叔 Guido van rossum
胶水语言
提供了非常完善的代码库
运行速度慢,大小写敏感
是解释型语言,也就是说,发布项目时相当于发布源代码
编译型语言,如C语言,运行以后生成exe文件,不能从exe文件反推出C语言代码
Python语言解释器:
CPython 使用广泛 >>>
IPython 基于CPython,在交互方式上有增强 In[序号]
PyPy 注重于执行速度,动态编译Python
JPython 编译为java字节码
IronPython 编译为.NET字节码
Python代码运行助手
让浏览器运行Python代码
源码:c:\codes\python\learning.py
方法:
先将python保存为临时文件,然后调用解释器
语法
-
以 : 结尾的语句被视为一个代码块
条件判断
if 条件:
操作语句
elif 条件:
操作语句
else:
操作语句
循环
for l in list:
操作语句
while 条件:
操作语句
range(x,y,z)生成一个整数序列,可以通过list()转换成list数据类型;x代表开始数字,y代表结束数字,z代表隔z个数字取
-
print("your name is %s" % name)
占位符
%s 字符串
%d 整数
%f 浮点数
%x 十六进制整数
%%=%
整数、浮点数没有大小限制,浮点数超出一定范围直接表示为inf(无限大)、
list与tuple数组
tuple和list的主要区别在于,一旦建立,tuple的各个元素不可再变更,而list的各个元素可以再变更
tuple 元组/定值表
>>>s1 = (2, 1.3, 'love', 5.6, 9, 12, False) # s1是一个tuple
>>>s2 = [True, 5, 'smile'] # s2是一个list
list可变数组 list=[]
list.append(str) 追加元素
list.insert(index,str) 将字符串插入到指定索引位置
list.pop() 删除list末尾元素
list.pop(index) 删除指定索引位置的元素
list里最后一个字符,索引位置的使用 -1,len(list)-1
tuple数组定长 tuple=()
当tuple数组里只有一个元素时,t=(1,)
范围引用: 基本样式[下限:上限:步长]
>>>print(s1[:5]) # 从开始到下标4 (下标5的元素 不包括在内)
>>>print(s1[2:]) # 从下标2到最后
>>>print(s1[0:5:2]) # 从下标0到下标4 (下标5不包括在内),每隔2取一个元素 (下标为0,2,4的元素)
>>>print(s1[2:0:-1]) # 从下标2到下标1
从上面可以看到,在范围引用的时候,如果写明上限,那么这个上限本身不包括在内。
尾部元素引用
>>>print(s1[-1]) # 序列最后一个元素
>>>print(s1[-3]) # 序列倒数第三个元素
同样,如果s1[0:-1], 那么最后一个元素不会被引用 (再一次,不包括上限元素本身)
词典
词典和表类似的地方,是包含有多个元素,每个元素以逗号分隔。但词典的元素包含有两部分,键和值,常见的是以字符串来表示键,也可以使用数字或者真值来表示键(不可变的对象可以作为键)。值可以是任意对象。键和值两者一一对应。
与表不同的是,词典的元素没有顺序。你不能通过下标引用元素。词典是通过键来引用。
dic = {'tom':11, 'sam':57,'lily':100}
#在词典中增添一个新元素的方法:
>>>dic['lilei'] = 99
>>>print dic
#词典的循环调用
dic = {'lilei': 90, 'lily': 100, 'sam': 57, 'tom': 90}
for key in dic:
print dic[key]
#常用方法
>>>print dic.keys() # 返回dic所有的键
>>>print dic.values() # 返回dic所有的值
>>>print dic.items() # 返回dic所有的元素(键值对)
>>>dic.clear() # 清空dic,dict变为{}
#另外有一个很常用的用法:
>>>del dic['tom'] # 删除 dic 的‘tom’元素
#del是Python中保留的关键字,用于删除对象。
#与表类似,你可以用len()查询词典中的元素总数。
>>>print(len(dic))
- sorted函数按key值对字典排序
先来基本介绍一下sorted函数,sorted(iterable,key,reverse),sorted一共有iterable,key,reverse这三个参数。
其中iterable表示可以迭代的对象,例如可以是 dict.items()、dict.keys()等,key是一个函数,用来选取参与比较的元素,reverse则是用来指定排序是倒序还是顺 序,reverse=true则是倒序,reverse=false时则是顺序,默认时reverse=false。
要按key值对字典排序,则可以使用如下语句:
d={"a":1,"f":3,"e":1,"b":7}
sorted(d.keys()) #["a","b","e","f"]
直接使用sorted(d.keys())就能按key值对字典排序,这里是按照顺序对key值排序的,如果想按照倒序排序的话,则只要将reverse置为true即可。
- sorted函数按value值对字典排序
要对字典的value排序则需要用到key参数,在这里主要提供一种使用lambda表达式的方法,如下:
d={"a":1,"f":3,"e":1,"b":7}
sorted(d.items(),key=lambda item:item[1]) #[("a",1),("b",7),("e",1),("f",3)]
这里的d.items()实际上是将d转换为可迭代对象,迭代对象的元素为 ("a",1),("b",7),("e",1),("f",3),items()方法将字典的元素 转化为了元组,而这里key参数对应的lambda表达式的意思则是选取元组中的第二个元素作为比较参数(如果写作key=lambda item:item[0]的话则是选取第一个元素作为比较对象,也就是key值作为比较对象。lambda x:y中x表示输出参数,y表示lambda 函数的返回值),所以采用这种方法可以对字典的value进行排序。注意排序后的返回值是一个list,而原字典中的名值对被转换为了list中的元组。
列表、阵列
color=['red','green','blue']
print(color[0])
color.append('orange') # red,green,blue,orange
c=color.pop(1); # 索引为1的元素弹出,并将其赋值给c
print(c) # green
print(color)
color.append(['white','black']) # 添加一项类型为列表的元素['white','black']
print(color) # ['red', 'blue', 'orange', ['white', 'black']]
del color[3] # 将其删除
print(color) #['red', 'blue', 'orange']
color=color+['white','black'] # color列表与['white','black'] 合并并重新赋值给color
print(color) #['red', 'blue', 'orange', 'white', 'black']
请注意上面的color.append(['white','black'])和color=color+['white','black']的区别
-
动态生成列表:
a=[x for x in range(1,11,1)] print(a) #[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-
列表的复制 :
newcopy=data[:]
列表中套列表即可形成阵列(听起来是不是很牛X?),比如下面, r就是阵列:
r1=[1,2,3,4,5] r2=[10,20,30,40,50] r3=[100,200,300,400,500] arr=[r1,r2,r3] for row in arr: print(row)
-
阵列的操作,比如列提取,行统计
m=[[1,2,3],[2,3,50],[33,20,30]] n=[row[1]for row in m if row[2]>10] print(n) r1=[ 1, 2, 3, 4, 5] r2=[ 10, 20, 30, 40, 50] r3=[100,200,300,400,500] arr=[r1,r2,r3] for row in arr: print(row) #阵列操作 c1=[row[0] for row in arr] #将arr阵列的每行的第1个单元取出,并组成列表后赋值给c1 ,c1是列表类型 print(c1) #[1, 10, 100] c11=[row[0]+50 for row in arr] #将arr阵列的每行的第1个单元取出后分别加50后的值组成列表后赋给c11 ,c11是列表类型 print(c11) c2=[row[1] for row in arr if row[1]>=20] #添加条件的取值: 每行第2个元素>=20才考虑 print (c2) # [20, 200] #统计阵列中每行的总数 s=(sum(row) for row in arr) #创建一个逐行统计的生成器 i=0 while (i<len(arr)): print ("Line %d, sum=%d" %(i , next(s))) i=i+1
-
统计阵列的总数:
#统计这个阵列的总数 line_sum=list(map(sum,arr)) #将arr阵列每行统计数转换成一个列表赋值给line_sum print(line_sum)# [15, 150, 1500] print(sum(line_sum)) #再将每行的统计数再汇总输出:1665
内置函数
秉承着一切皆对象的理念,我们再次回头来看函数(function)。函数也是一个对象,具有属性(可以使用dir()查询)。作为对象,它还可以赋值给其它对象名,或者作为参数传递。
lambda函数
在展开之前,我们先提一下lambda函数。可以利用lambda函数的语法,定义函数。lambda例子如下:
func = lambda x,y: x + y
print func(3,4)
lambda生成一个函数对象。该函数参数为x,y,返回值为x+y。函数对象赋给func。func的调用与正常函数无异。
以上定义可以写成以下形式:
def func(x, y):
return x + y
函数作为参数传递
函数可以作为一个对象,进行参数传递。函数名(比如func)即该对象。比如说:
def test(f, a, b):
print 'test'
print f(a, b)
test(func, 3, 5)
test函数的第一个参数f就是一个函数对象。将func传递给f,test中的f()就拥有了func()的功能。
我们因此可以提高程序的灵活性。可以使用上面的test函数,带入不同的函数参数。比如:
test((lambda x,y: x**2 + y), 6, 9)
map()函数
map()是Python的内置函数。它的第一个参数是一个函数对象。
re = map((lambda x: x+3),[1,3,5,6])
这里,map()有两个参数,一个是lambda所定义的函数对象,一个是包含有多个元素的表。map()的功能是将函数对象依次作用于表的每一个元素,每次作用的结果储存于返回的表re中。map通过读入的函数(这里是lambda函数)来操作数据(这里“数据”是表中的每一个元素,“操作”是对每个数据加3)。
在Python 3.X中,map()的返回值是一个循环对象。可以利用list()函数,将该循环对象转换成表。
如果作为参数的函数对象有多个参数,可使用下面的方式,向map()传递函数参数的多个参数:
re = map((lambda x,y: x+y),[1,2,3],[6,7,9])
map()将每次从两个表中分别取出一个元素,带入lambda所定义的函数。
filter()函数
filter函数的第一个参数也是一个函数对象。它也是将作为参数的函数对象作用于多个元素。如果函数对象返回的是True,则该次的元素被储存于返回的表中。filter通过读入的函数来筛选数据。同样,在Python 3.X中,filter返回的不是表,而是循环对象。
filter函数的使用如下例:
def func(a):
if a > 100:
return True
else:
return False
print filter(func,[10,56,101,500])
reduce()函数
reduce函数的第一个参数也是函数,但有一个要求,就是这个函数自身能接收两个参数。reduce可以累进地将函数作用于各个参数。如下例:
print reduce((lambda x,y: x+y),[1,2,5,7,9])
reduce的第一个参数是lambda函数,它接收两个参数x,y, 返回x+y。
reduce将表中的前两个元素(1和2)传递给lambda函数,得到3。该返回值(3)将作为lambda函数的第一个参数,而表中的下一个元素(5)作为lambda函数的第二个参数,进行下一次的对lambda函数的调用,得到8。依次调用lambda函数,每次lambda函数的第一个参数是上一次运算结果,而第二个参数为表中的下一个元素,直到表中没有剩余元素。
上面例子,相当于(((1+2)+5)+7)+9
reduce()函数在3.0里面不能直接用的,它被定义在了functools包里面,需要引入包。
range()
在Python中,for循环后的in跟随一个序列的话,循环每次使用的序列元素,而不是序列的下标。
之前我们已经使用过range()来控制for循环。现在,我们继续开发range的功能,以实现下标对循环的控制:
S = 'abcdefghijk'
for i in range(0,len(S),2):
print S[i]
在该例子中,我们利用len()函数和range()函数,用i作为S序列的下标来控制循环。在range函数中,分别定义上限,下限和每次循环的步长。这就和C语言中的for循环相类似了。
enumerate()
利用enumerate()函数,可以在每次循环中同时得到下标和元素:
S = 'abcdefghijk'
for (index,char) in enumerate(S):
print index
print char
实际上,enumerate()在每次循环中,返回的是一个包含两个元素的定值表(tuple),两个元素分别赋予index和char
zip()
如果你多个等长的序列,然后想要每次循环时从各个序列分别取出一个元素,可以利用zip()方便地实现:
ta = [1,2,3]
tb = [9,8,7]
tc = ['a','b','c']
for (a,b,c) in zip(ta,tb,tc):
print(a,b,c)
每次循环时,从各个序列分别从左到右取出一个元素,合并成一个tuple,然后tuple的元素赋予给a,b,c
zip()函数的功能,就是从多个列表中,依次各取出一个元素。每次取出的(来自不同列表的)元素合成一个元组,合并成的元组放入zip()返回的列表中。zip()函数起到了聚合列表的功能。
我们可以分解聚合后的列表,如下:
ta = [1,2,3]
tb = [9,8,7]
# cluster
zipped = zip(ta,tb)
print(zipped)# decomposena, nb = zip(*zipped)print(na, nb)
abs()&hex()
abs()返回绝对值
hex()返回十六进制数
转换数据类型:int() float() str() bool()
在解释器中,导入一个python文件里的函数
from abstest import my_abs
rag_abs(-5)
较小的精度
基础
浮点数是用机器上浮点数的本机双精度(64 bit)表示的。提供大约17位的精度和范围从-308到308的指数。和C语言里面的double类型相同。Python不支持32bit的单精度浮点数。如果程序需要精确控制区间和数字精度,可以考虑使用numpy扩展库。
Python 3.X对于浮点数默认的是提供17位数字的精度。
关于单精度和双精度的通俗解释:
-
单精度型和双精度型,其类型说明符为float 单精度说明符
-
double 双精度说明符。在Turbo C中单精度型占4个字节(32位)内存空间,其数值范围为3.4E-38~3.4E+38,只能提供七位有效数字。
-
双精度型占8 个字节(64位)内存空间,其数值范围为1.7E-308~1.7E+308,可提供16位有效数字。
要求较小的精度
将精度高的浮点数转换成精度低的浮点数。
round()内置方法
这个是使用最多的,刚看了round()的使用解释,也不是很容易懂。round()不是简单的四舍五入的处理方式。
For the built-in types supporting round(), values are rounded to the closest multiple of 10 to the power minus ndigits; if two multiples are equally close, rounding is done toward the even choice (so, for example, both round(0.5) and round(-0.5) are 0, and round(1.5) is 2).
>>> round(2.5)
2
>>> round(1.5)
2
>>> round(2.675)
3
>>> round(2.675, 2)
2.67
round()如果只有一个数作为参数,不指定位数的时候,返回的是一个整数,而且是最靠近的整数(这点上类似四舍五入)。但是当出现.5的时候,两边的距离都一样,round()取靠近的偶数,这就是为什么round(2.5) = 2。当指定取舍的小数点位数的时候,一般情况也是使用四舍五入的规则,但是碰到.5的这样情况,如果要取舍的位数前的小树是奇数,则直接舍弃,如果偶数这向上取舍。看下面的示例:
>>> round(2.635, 2)
2.63
>>> round(2.645, 2)
2.65
>>> round(2.655, 2)
2.65
>>> round(2.665, 2)
2.67
>>> round(2.675, 2)
2.67
使用格式化
效果和round()是一样的。
>>> a = ("%.2f" % 2.635)
>>> a
'2.63'
>>> a = ("%.2f" % 2.645)
>>> a
'2.65'
>>> a = int(2.5)
>>> a
2
要求超过17位的精度分析
python默认的是17位小数的精度,但是这里有一个问题,就是当我们的计算需要使用更高的精度(超过17位小数)的时候该怎么做呢?
使用格式化(不推荐)
>>> a = "%.30f" % (1/3)
>>> a
'0.333333333333333314829616256247'
可以显示,但是不准确,后面的数字往往没有意义。
decimal+getcontext
高精度使用decimal模块,配合getcontext
>>> from decimal import *
>>> print(getcontext())
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>> getcontext().prec = 50
>>> b = Decimal(1)/Decimal(3)
>>> b
Decimal('0.33333333333333333333333333333333333333333333333333')
>>> c = Decimal(1)/Decimal(17)
>>> c
Decimal('0.058823529411764705882352941176470588235294117647059')
>>> float(c)
0.058823529411764705
默认的context的精度是28位,可以设置为50位甚至更高,都可以。这样在分析复杂的浮点数的时候,可以有更高的自己可以控制的精度。其实可以留意下context里面的这rounding=ROUND_HALF_EVEN 参数。ROUND_HALF_EVEN, 当half的时候,靠近even.
小数和取整
既然说到小数,就必然要说到整数。一般取整会用到这些函数:
round()
这个不说了,前面已经讲过了。一定要注意它不是简单的四舍五入,而是ROUND_HALF_EVEN的策略。
math模块的ceil(x)
取大于或者等于x的最小整数。
math模块的floor(x)
去小于或者等于x的最大整数.
>>> from math import ceil, floor
>>> round(2.5)
2
>>> ceil(2.5)
3
>>> floor(2.5)
2
>>> round(2.3)
2
>>> ceil(2.3)
3
>>> floor(2.3)
2
>>>
生成器
生成器(generator)的主要目的是构成一个用户自定义的循环对象。
生成器的编写方法和函数定义类似,只是在return的地方改为yield。生成器中可以有多个yield。当生成器遇到一个yield时,会暂停运行生成器,返回yield后面的值。当再次调用生成器的时候,会从刚才暂停的地方继续运行,直到下一个yield。生成器自身又构成一个循环器,每次循环使用一个yield返回的值。
下面是一个生成器:
def gen():
a = 100
yield a
a = a*8
yield a
yield 1000
该生成器共有三个yield, 如果用作循环器时,会进行三次循环。
for i in gen():
print i
再考虑如下一个生成器:
def gen():
for i in range(4):
yield i
它又可以写成生成器表达式(Generator Expression):
G = (x for x in range(4))
生成器表达式是生成器的一种简便的编写方式。
测试代码:
def gen():
for i in range(4):
yield i
for i in gen():
print i
G = (x for x in range(4))
for i in G:
print(i)
表推导
表推导(list comprehension)是快速生成表的方法。它的语法简单,很有实用价值。
假设我们生成表L:
L = []
for x in range(10):
L.append(x**2)
以上产生了表L,但实际上有快捷的写法,也就是表推导的方式:
L = [x**2 for x in range(10)]
这与生成器表达式类似,只不过用的是中括号。
自定义函数
对于基本数据类型的变量,变量传递给函数后,函数会在内存中复制一个新的变量,从而不影响原来的变量。(我们称此为值传递)
但是对于表来说,表传递给函数的是一个指针,指针指向序列在内存中的位置,在函数中对表的操作将在原有内存中进行,从而影响原有变量。 (我们称此为指针传递)
自定义函数
def 函数名(参数列表):
函数内容
return = return none
空函数:
def pop():
pass #占位符,让程序先运行起来
函数可以返回多个值,这多个值作为一个tuple返回,这个tuple也可以按照位置赋值给多个变量
数据类型检查
def my_abs(x):
if not isinstance(x,(int,float)): #isinstance(x,数据类型)判断x是否为该数据类型
raise TypeError('bad operand type')
if x>0:
return x
if x<0:
return -x
函数的参数列表:
- 正常定义的必选参数
- 默认参数
- 可变参数
- 关键字参数
- 命名关键字参数
默认参数
设置参数的默认值 如:def power(x,y=2)
必选参数在前;变化大的在前
调用默认参数:
正常传参,顺序调用;
不按顺序调用,要用参数名 如
enroll('Adam','M',city='tianjin')
def enroll(name,sex,age=18,city='beijing')
多次调用默认参数函数时的异常情况:
def app_end(L=[]):
L.append('END')
return L #多次调用后L=['END','END',...]
def app_end(L=None):
if L is None:
L=[]
L.append('END')
return L #多次调用后L=['END']
可变参数
def calc(numbers):
调用 calc(1,2,3,4) #调用时可任意传参,甚至0个参数
def calc(numbers):
调用 calc([1,2,3]) calc((1,2,3)) #只能传入list/tuple
如果将list/tuple作为参数 calc(list/*tuple)
def func(**dict):
print type(dict)
print dict
func(a=1,b=9)
func(m=2,n=1,c=11)关键字参数
可变参数
传入0/多个参数,传入时自动组装成tuple
关键字参数
传入0/多个含参数名的参数,自动组装成dictionary
命名关键字参数
可以有缺省值
def person(name,age,,city,job)
def person(name,age,city,job)
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*
了
* 分隔符,*后均为命名关键字参数,限制传入的关键字参数;
可替换为一个可变参数
解包裹
*和**,也可以在调用的时候使用,即解包裹(unpacking), 下面为例:
def func(a,b,c):
print a,b,cargs = (1,3,4)func(*args)
在这个例子中,所谓的解包裹,就是在传递tuple时,让tuple的每一个元素对应一个位置参数。在调用func时使用*,是为了提醒Python:我想要把args拆成分散的三个元素,分别传递给a,b,c。(设想一下在调用func时,args前面没有*会是什么后果?)
相应的,也存在对词典的解包裹,使用相同的func定义,然后:
dict = {'a':1,'b':2,'c':3}
func(**dict)
在传递词典dict时,让词典的每个键值对作为一个关键字传递给func。
参数定义的顺序:必选,默认,可变,命名关键字,关键字
递归函数
函数调用是通过栈(stack)这种数据结构实现的
调用一次,栈加一层栈帧
函数返回,栈减一层栈帧
递归次数过多,有可能栈溢出,可以用尾递归方法(尚未理解尾递归)
for循环遍历list/tuple 相当于迭代函数
判断是否为可迭代对象:
- from collections import Iterable
- isinstance('abc',Iterable)
可以用enumerate(list) 将list转换为索引元素
for i,value in enumerate([1,2,3]):
print(i,value)
输出结果:0 1 1 2 2 3
特殊方法
_init_()
_init_()是一个特殊方法(special method)。Python有一些特殊方法。Python会特殊的对待它们。特殊方法的特点是名字前后有两个下划线。
如果你在类中定义了_init_()这个方法,创建对象时,Python会自动调用这个方法。这个过程也叫初始化。
class happyBird(Bird):
def __init__(self,more_words):
print 'We are happy birds.',more_words
summer = happyBird('Happy,Happy!')
dir()&help()
两个内置函数,dir()和help()
dir()用来查询一个类或者对象所有属性。你可以尝试一下
>>>print dir(list)
help()用来查询的说明文档。你可以尝试一下
>>>print help(list)
(list是Python内置的一个类,对应于我们之前讲解过的列表)
类和对象
Python使用类(class)和对象(object),进行面向对象(object-oriented programming,简称OOP)的编程。
面向对象的最主要目的是提高程序的重复使用性。
class Bird(object):
have_feather = True
way_of_reproduction = 'egg'
def move(self, dx, dy):
position = [0,0]
position[0] = position[0] + dx
position[1] = position[1] + dy
return position
summer = Bird()
print 'after move:',summer.move(5,8)
class Chicken(Bird):
way_of_move = 'walk'
possible_in_KFC = True
class Oriole(Bird):
way_of_move = 'fly'
possible_in_KFC = False
summer = Chicken()
print summer.have_feather
print summer.move(5,8)
在类定义时,括号里为了Bird。这说明,Chicken是属于鸟类(Bird)的一个子类,即Chicken继承自Bird。自然而然,Bird就是Chicken的父类。Chicken将享有Bird的所有属性
class Bird(object):
have_feather = True
way_of_reproduction = 'egg'
括号中的object,当括号中为object时,说明这个类没有父类(到头了)
文件读写
Python具有基本的文本文件读写功能。Python的标准库提供有更丰富的读写功能。
文本文件的读写主要通过open()所构建的文件对象来实现。
f = open(文件名,模式)
最常用的模式有:
"r" # 只读
"w" # 写入
>>>f = open("test.txt","r")
读取:
content = f.read(N) # 读取N bytes的数据
content = f.readline() # 读取一行
content = f.readlines() # 读取所有行,储存在列表中,每个元素是一行。
写入:
f.write('I like apple') # 将'I like apple'写入文件
关闭文件:
f.close()
模块
引入模块后,可以通过模块.对象的方式来调用引入模块中的某个对象。
Python中还有其它的引入方式,
import a as b # 引入模块a,并将模块a重命名为b
from a import function1 # 从模块a中引入function1对象。调用a中对象时,我们不用再说明模块,即直接使用function1,而不是a.function1。
from a import * # 从模块a中引入所有对象。调用a中对象时,我们不用再说明模块,即直接使用对象,而不是a.对象。
搜索路径
Python会在以下路径中搜索它想要寻找的模块:
- 程序所在的文件夹
- 标准库的安装路径
- 操作系统环境变量PYTHONPATH所包含的路径
如果你有自定义的模块,或者下载的模块,可以根据情况放在相应的路径,以便Python可以找到。
模块包
可以将功能相似的模块放在同一个文件夹(比如说this_dir)中,构成一个模块包。通过
import this_dir.module
引入this_dir文件夹中的module模块。
该文件夹中必须包含一个__init__.py的文件,提醒Python,该文件夹为一个模块包。_init_.py可以是一个空文件。
异常处理
处理异常
在项目开发中,异常处理是不可或缺的。异常处理帮助人们debug,通过更加丰富的信息,让人们更容易找到bug的所在。异常处理还可以提高程序的容错性。
我们之前在讲循环对象的时候,曾提到一个StopIteration的异常,该异常是在循环对象穷尽所有元素时的报错。
我们以它为例,来说明基本的异常处理。
一个包含异常的程序:
re = iter(range(5))
for i in range(100):
print re.next()
print 'HaHaHaHa'
首先,我们定义了一个循环对象re,该循环对象将进行5次循环,每次使用序列的一个元素。
在随后的for循环中,我们手工调用next()函数。当循环进行到第6次的时候,re.next()不会再返回元素,而是抛出(raise)StopIteration的异常。整个程序将会中断。
我们可以修改以上异常程序,直到完美的没有bug。但另一方面,如果我们在写程序的时候,知道这里可能犯错以及可能的犯错类型,我们可以针对该异常类型定义好”应急预案“。
re = iter(range(5))
try:
for i in range(100):
print re.next()
except StopIteration:
print 'here is end ',i
print 'HaHaHaHa'
在try程序段中,我们放入容易犯错的部分。我们可以跟上except,来说明如果在try部分的语句发生StopIteration时,程序该做的事情。如果没有发生异常,则except部分被跳过。
随后,程序将继续运行,而不是彻底中断。
完整的语法结构如下:
try:
...
except exception1:
...
except exception2: ...except:
...
else:
...
finally:
...
如果try中有异常发生时,将执行异常的归属,执行except。异常层层比较,看是否是exception1, exception2...,直到找到其归属,执行相应的except中的语句。如果except后面没有任何参数,那么表示所有的exception都交给这段程序处理。比如:
try:
print(a*2)
except TypeError:
print("TypeError")
except:
print("Not Type Error & Error noted")
由于a没有定义,所以是NameError。异常最终被except:部分的程序捕捉。
如果无法将异常交给合适的对象,异常将继续向上层抛出,直到被捕捉或者造成主程序报错。比如下面的程序
def test_func():
try:
m = 1/0
except NameError:
print("Catch NameError in the sub-function")
try:
test_func()
except ZeroDivisionError:
print("Catch error in the main program")
子程序的try...except...结构无法处理相应的除以0的错误,所以错误被抛给上层的主程序。
如果try中没有异常,那么except部分将跳过,执行else中的语句。
finally是无论是否有异常,最后都要做的一些事情。
流程如下,
try->异常->except->finally
try->无异常->else->finally
抛出异常
我们也可以自己写一个抛出异常的例子:
print 'Lalala'
raise StopIteration
print 'Hahaha'
这个例子不具备任何实际意义。只是为了说明raise语句的作用。
StopIteration是一个类。抛出异常时,会自动有一个中间环节,就是生成StopIteration的一个对象。Python实际上抛出的,是这个对象。当然,也可以自行生成对象:
raise StopIteration()