天天看點

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儲存為臨時檔案,然後調用解釋器

文法

  1. 以 : 結尾的語句被視為一個代碼塊

    條件判斷

    ​ if 條件:

    ​ 操作語句

    ​ elif 條件:

    ​ else:

    循環

    ​ for l in list:

    ​ while 條件:

    range(x,y,z)生成一個整數序列,可以通過list()轉換成list資料類型;x代表開始數字,y代表結束數字,z代表隔z個數字取

  2. 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))
           
  1. 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即可。

  1. 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']的差別

  1. 動态生成清單:
    a=[x for x in range(1,11,1)]  
    print(a) #[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]      
               
  2. 清單的複制 :

    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)   
               
  3. 陣列的操作,比如列提取,行統計
    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      
               
  4. 統計陣列的總數:
    #統計這個陣列的總數  
    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會在以下路徑中搜尋它想要尋找的子產品:

  1. 程式所在的檔案夾
  2. 标準庫的安裝路徑
  3. 作業系統環境變量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()
           
ฅ平平庸庸的普通人ฅ