簡介
龜叔 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個參數
調用 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()
ฅ平平庸庸的普通人ฅ