本文轉載於星過無痕的博客http://www.cnblogs.com/linxiangpeng/p/6403991.html
在此表達對原創作者的感激之情,多謝星過無痕的分享!謝謝!
Python學習的個人筆記
題外話:
我是一個大二的計算機系的學生,這份python學習個人筆記是趁寒假這一周在慕課網,w3cschool,還有借鑒了一些博客,資料整理出來的,用於自己方便的時候查閱,一開始保存在word上,代碼不是很好看,於是決定復制到博客里面,可能有復制過程中出錯的,或者我本身在理解方面有出錯的地方,希望能得到指正,謝謝
后續的內容我會繼續學習……
python下載地址 www.python.org
Python 分為2.7和3.3兩個版本,3.3有些庫不兼容,因此用2.7版本
Python解釋型語言 代碼量小,可移植性高,執行速度慢
配置環境變量 Path ;C:\Python27;
對縮進要求嚴格,四個空格,不建議用Tab,不同平台上縮進不一樣
要輸出中文在開頭寫上 # -*- coding: utf-8 -*-
我使用的IDE為Pycharm,注釋為ctrl + /
官方文檔 http://docs.python.org/2/
一張圖概況Python學習(轉自W3Cschool)
目錄:
Python基礎語法:
(1)打印出hello world
(2)注釋
(3)數據類型
(4)運算
(5)輸入
(6)輸出
(7)序列
(8)相等比較
(9)選擇
(10)循環
(11)字典(dict)
(12)集合(set)(不常用)
(14)pass,del,exec,eval
(15)內建函數
Python進階語法:
(1)文件
(2)錯誤和異常
(3)模塊和包
(4)作用域
(5)高階函數
(6)裝飾器
(7)參數
(8)面向對象
(9)定制類(魔術方法)
(10)日期和時間
(11)數學與隨機數
(12)正則表達式
Python基礎語法:
(1)打印出hello world
# -*- coding: utf-8 -*- print "hello world" print "你好 世界"
(2)注釋
1、(單行注釋)以 # 開頭,后面的文字直到行尾都算注釋
2、(多行注釋)多行注釋是用三引號''' '''包含的(單引號和雙引號均可)
3、(中文注釋)要注釋中文必須程序開頭先寫上# -*- coding: utf-8 -*-,不然會報錯
(3)數據類型
1、變量不需要聲明,可以直接輸入,大小寫敏感
2、這里的var = xxxx不叫變量賦值,而叫變量綁定,一個符號可以綁定任意類型的值。
3、內置函數type(), 用以查詢變量的類型
var = 1 print var #1 print type(var) #整數類型 # <type 'int'> var = 1.1 print var # 1.1 print type(var) #浮點數類型 # <type 'float'> var = 'hello' print var # hello print type(var) #字符串 # <type 'str'> var = (1==1) print var # True print type(var) #布爾型 # <type 'bool'> var = None print var # None print type(var) #空值 # <type 'NoneType'> var = 1+1j 或者complex(a,b)表示, 復數的實部a和虛部b都是浮點型 print var # (1+1j) print type(var) #復數類型 # <type 'complex'>
4、字符串以''或" "括起來的任意文本
5、布爾型(True, False,可以用and, or, not運算,而不是C語言的&&和||)
6、多變量賦值
a = b = c = 1 a, b, c = 1, 2, "john" #等號兩邊都是元組,建議加上括號,增加可讀性 x,y = y,x #兩值交換,不需要temp,更加簡潔
7、賦值語句不可以返回值,對象是通過引用傳遞的
y = (x = x + 1) #這是非法的
(4)運算
1、加、減、乘、除、求余、乘方
2、乘方 ** 右結合
2**3=8
2**2**3=256
(2**2)**3=64
3、整數運算結果仍然是整數,浮點數運算結果仍然是浮點數。
但是整數和浮點數混合運算的結果就變成浮點數了。
4、字符串與整數相乘
print 'abc' * 3 結果 abcabcabc
5、因為Python把0、空字符串''和None看成 False,其他數值和非空字符串都看成 True
True and 'a=T' 計算結果是 'a=T'
繼續計算 'a=T' or 'a=F' 計算結果還是 'a=T'
要解釋上述結果,又涉及到 and 和 or 運算的一條重要法則:短路計算。
①在計算 a and b 時,如果 a 是 False,則根據與運算法則,整個結果必定為 False,因此返回 a;如果 a 是 True,則整個計算結果必定取決與 b,因此返回 b。
②在計算 a or b 時,如果 a 是 True,則根據或運算法則,整個計算結果必定為 True,因此返回 a;如果 a 是 False,則整個計算結果必定取決於 b,因此返回 b。
所以Python解釋器在做布爾運算時,只要能提前確定計算結果,它就不會往后算了,直接返回結果。
6、不支持自加(i++)和自減(i--)
7、地板除(//)除法不管操作數何種數值類型,總是舍去小數部分,返回數字序列中比真正的商小的最接近的數字。
print 5//3 #1 print 1.0//2.0 #0.0 print -1/2 #-1
(5)輸入
x = input() #1+2 print type(x) #<type 'int'> y = raw_input() #1+2 print type(y) # <type 'str'>
1、由此可見, input() 在對待純數字輸入返回所輸入的數字的類型(int,float)
而raw_input() 將所有輸入作為字符串看待,返回字符串類型。
為了避免類型發生錯誤,一般情況下使用 raw_input() 來與用戶交互。
(6)輸出
1、Python2 里面print可以直接接字符串或者運算。
2、Python3 里面print變成了一個函數,上面的寫法不支持了,必須用一個括號括起來,否則會報告語法錯誤。
3、>>>是Python解釋器的提示符,不是代碼的一部分。
4、print語句也可以跟上多個字符串,用逗號“,”隔開,遇到逗號“,”會輸出一個空格:
print '1+2=',1+2 #1+2= 3
5、多行輸出使用三個引號和使用換行符\n一致
print '''哈 哈 哈''' print "哈\n哈\n哈" # 輸出結果 # 哈 # 哈 # 哈 # 哈 # 哈 # 哈
6、轉義
print r'C:\log.txt' print 'C:\\log.txt' # C:\log.txt # C:\log.txt
7、print 語句與字符串格式運算符( % )結合使用,可實現字符串替換功能
print "%s is number %d!" % ("Python", 1)
%s表示由一個字符串來替換,%d表示由一個整數來替換,%f表示由一個浮點數來替換。
Python 非常靈活,即使將數字傳遞給 %s,也不會像其他要求嚴格的語言一樣引發嚴重后果。
(7)序列
1、序列有兩種: list (可變列表) 和tuple(不可變元組)
2、定義:序列是一組有順序的元素的集合,可以包含一個或多個元素,也可以沒有任何元素。
list = [0,1,2,3,4,5] #列表用中括號,可改變,理解為數組 tuple = (0,1,2,3,4,5) #元祖用小括號,不可改變
由於Python是動態語言,所以list中包含的元素並不要求都必須是同一種數據類型
2、序列的通用操作
seq = "0123456789" print seq[0] #序列元素的下標從0開始。注意不要越界 print seq[-1] #倒序索引,-1代表倒數第一。 print seq[1:5] #支持切片操作,seq[start:end],start包含在結果中,end不包含在結果中。 print range(1,101)[0:10]#從第1個數元素開始取,到第11元素結束 print seq[7:] #seq[start:end]中的end可以省略。 print seq[-3:] #分片也支持負數。 print seq[:3] #seq[start:end]中的start也可以省略。 print seq[:] #全部省略會復制整個序列。 print seq[::2] #支持步長。 print seq[::-1] #逆序輸出。 print seq[9:1:-1] #支持負步長。 print range(1,101)[2::3]#從第三元素開始取,每隔2個取一個元素,即3的倍數 print range(1,101)[4:50:5]#從第五個取,每隔4個取一個,50以內5的倍數 print [1, 2, 3] + [4, 5, 6] # 序列支持相加,這解釋了為啥字符串可以相加。 print [1, 2, 3] * 3 #序列支持相乘,這解釋了為啥字符串可以相稱。 print [None] * 10 #生成一個空序列。 print 1 in [1, 2, 3] #成員判斷。 print range(1,101)[4::5][-10:] #切片可以嵌套,最后10個5的倍數,先獲得5的倍數再取后10個
記住倒數第一個元素的索引是-1。倒序切片包含起始索引,不包含結束索引。
3、可變的列表(list)
list = [0,1,2,3,4,5] list.append(7) #append()總是把新元素添加到list的尾部 print list # [0, 1, 2, 3, 4, 5, 7] list.insert(0,-1) #insert()接受兩個參數,第一個是索引,第二個是元素 print list # [-1, 0, 1, 2, 3, 4, 5, 7] list.insert(-1,6) #insert(-1)是最后一個元素之前,即倒數第二個元素,因為insert()前插 print list # [-1, 0, 1, 2, 3, 4, 5, 6, 7] list.pop() #pop()方法總是刪掉最后一個元素 print list # [-1, 0, 1, 2, 3, 4, 5, 6] list.pop(0) #參數為索引 print list # [0, 1, 2, 3, 4, 5, 6] list[6]=7 #對list中的某一個索引賦值,就可以直接用新的元素替換掉原來的元素 print list # [0, 1, 2, 3, 4, 5, 7] list[0],list[-1]=list[-1],list[0] #第一與最后位置調換 print list # [7, 1, 2, 3, 4, 5, 0]
在使用可變對象的方法如 sort(),extend()和 reverse()的時候要注意,這些操作會在列表
中原地執行操作,也就是說現有的列表內容會被改變,但是沒有返回值!
4、不可變的元組(tuple)
字符串是一種特殊的元組
沒有 append()方法,也沒有insert()和pop()方法,也不能賦值
Tuple 比 list 操作速度快.如果您定義了一個值的常量集,並且唯一要用它做的是不斷地遍歷它,請使用 tuple 代替 list.如月份,星期。
print (1,) #一個元素的元祖。
因為()既可以表示tuple,又可以作為括號表示運算時的優先級,結果 (1) 被Python解釋器計算出結果 1,導致我們得到的不是tuple,而是整數 1。正是因為用()定義單元素的tuple有歧義,所以 Python 規定,單元素 tuple 要多加一個逗號“,”,這樣就避免了歧義。
可變的元組:tuple的元素指向list,而list內的值可變
5、序列解包
x, y, z = 1, 2, 3 print x, y, z (x, y, z) = (1, 2, 3) print x, y, z (x, y, z) = [1, 2, 3] print x, y, z
(8)相等比較
#== 和 is的差別,==比較的是值,is比較的是引用。 x = [1, 2, 3] y = x z = [1, 2, 3] print(x == y) #True print(x == z) #True print(x is y) #True print(x is z) #False
(9)選擇
1、Python代碼的縮進規則。具有相同縮進的代碼被視為代碼塊
2、縮進請嚴格按照Python的習慣寫法:4個空格,不要使用Tab,更不要混合Tab和空格
3、格式
if 條件1: statement elif 條件2: statement elif 條件3: statement else: statement
If后面不需要括號,但是條件后面需要冒號
elif 即 else if
4、三元運算符
x, y = 4, 3 if x < y: result = x else: result = y print result #等價於 result = x if x < y else y print result
(10)循環
1、for循環依次把list或tuple的每個元素迭代出來
格式
for 元素 in 序列: statement
name 這個變量是在 for 循環中定義的,意思是,依次取出list中的每一個元素,並把元素賦值給 name,然后執行for循環體(就是縮進的代碼塊)
L = ['Adam', 'Lisa', 'Bart'] for name in L: print name
這樣一來,遍歷一個list或tuple就非常容易了。
2、while循環,不會迭代 list 或 tuple 的元素,而是根據表達式判斷循環是否結束。
while 條件: statement
3、中斷循環 break和continue
4、range()的用法
range(1,5) #代表從1到5(不包含5) [1, 2, 3, 4]
range(1,5,2) #代表從1到5,間隔2(不包含5) [1, 3]
range(5) #代表從0到5(不包含5) [0, 1, 2, 3, 4]
5、Python中,迭代永遠是取出元素本身,而非元素的索引。
對於有序集合,元素確實是有索引的。使用enumerate() 函數拿到索引
L = ['Adam', 'Lisa', 'Bart', 'Paul'] for index, name in enumerate(L): print index, '-', name #結果 # 0 - Adam # 1 - Lisa # 2 - Bart # 3 - Paul
使用 enumerate() 函數,我們可以在for循環中同時綁定索引index和元素name。但是,這不是 enumerate() 的特殊語法。實際上,enumerate() 函數把:
['Adam', 'Lisa', 'Bart', 'Paul']
變成了類似:
[(0, 'Adam'), (1, 'Lisa'), (2, 'Bart'), (3, 'Paul')]
因此,迭代的每一個元素實際上是一個tuple:
6、好用的zip()方法
for x, y in zip(range(1, 10), range(1, 10)): print(x, y) # 結果 # (1, 1) # (2, 2) # (3, 3) # (4, 4) # (5, 5) # (6, 6) # (7, 7) # (8, 8) # (9, 9)
7、列表生成式,非常簡潔
要生成[1x1, 2x2, 3x3, ..., 10x10]
print [x * x for x in range(1, 11)] #[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
列表生成式的 for 循環后面還可以加上 if 判斷。例如:
print [x * x for x in range(1, 11) if x % 2 == 0] # [4, 16, 36, 64, 100]
8、迭代器
它為類序列對象提供了一個類序列的接口。
迭代非序列集合(例如映射和文件)時, 可以創建更簡潔可讀的代碼。
myTuple = (123, 'xyz', 45.67) i = iter(myTuple) print i.next() #123 print i.next() #xyz print i.next() #45.67 i.next() #報錯
(11)字典(dict)
1、字典的元素沒有順序。你不能通過下標引用元素。字典是通過鍵來引用,用大括號
查找速度快,無論dict有10個元素還是10萬個元素,查找速度都一樣。而list的查找速度隨着元素增加而逐漸下降。
dict的缺點是占用內存大,還會浪費很多內容
dict是按 key 查找,所以,在一個dict中,key不能重復
作為 key 的元素必須不可變
2、已知兩個列表,一個是名字,一個是成績,要根據名字找到對應的成績用兩個list不方便,如果把名字和分數關聯起來,組成類似的查找表,即 Python中的dict
用 dict 表示“名字”-“成績”的查找表如下:
dic = {'tom':11, 'sam':57,'lily':100} print type(dic) #<type 'dict'> d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 } print d #{'Lisa': 85, 'Adam': 95, 'Bart': 59}
3、我們把名字稱為key,對應的成績稱為value,dict就是通過 key 來查找 value。
4、花括號 {} 表示這是一個dict,然后按照 key: value, 寫出來即可。最后一個 key: value 的逗號可以省略。
5、由於dict也是集合,len()函數可以計算任意集合的大小:
print len(d) #運算結果為3
一個 key-value 算一個,因此,dict大小為3。
6、可以簡單地使用 d[key] 的形式來查找對應的 value,這和 list 很像,不同之處是,list 必須使用索引返回對應的元素,而dict使用key:
print d['Adam'] #95
注意: 通過 key 訪問 dict 的value,只要 key 存在,dict就返回對應的value。如果key不存在,會直接報錯:KeyError。
要避免 KeyError 發生,有兩個辦法:
一是先判斷一下 key 是否存在,用 in 操作符:
二是使用dict本身提供的一個 get 方法,在Key不存在的時候,返回None:
print d.get('Bart') #59 print d.get('Paul') #None
7、在字典中增添一個新元素的方法:
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 } print d #{'Lisa': 85, 'Adam': 95, 'Bart': 59} d['lilei'] = 99 print d #{'lilei': 99, 'Lisa': 85, 'Adam': 95, 'Bart': 59}
8、循環調用
for key in d: #或for key in d.keys() print d[key] # 結果 # 99 # 85 # 95 # 59
9、字典的常用方法
print d.keys() # 返回d所有的鍵
print d.values() # 返回d所有的值
print d.items() # 返回d所有的元素(鍵值對)
d.clear() # 清空d,dict變為{}
del d[‘xxx’] # 刪除 d 的‘xxx’元素
for key, value in d.items(): print key, ':', value
10、cmp()比較
(1)先比較字典長度
(2)再比較字典的鍵
(3)最后比較字典的值
(4)都一樣就相等
(12)集合(set)(不常用)
1、dict的作用是建立一組 key 和一組 value 的映射關系,dict的key是不能重復的。
有的時候,我們只想要 dict 的 key,不關心 key 對應的 value,目的就是保證這個集合的元素不會重復,這時,set就派上用場了。
2、set 持有一系列元素,這一點和 list 很像,但是set的元素沒有重復,而且是無序的,這點和 dict 的 key很像。
3、創建 set 的方式是調用 set() 並傳入一個 list,list的元素將作為set的元素:
s = set(['A', 'B', 'C']) print s #set(['A', 'C', 'B'])
4、添加、刪除:
s.add('D') print s #set(['A', 'C', 'B', 'D']) s.add('D') #已存在不會報錯 s.remove('D') print s #set(['A', 'C', 'B']) s.remove('D') #報錯,需要先判斷
(13)函數
1、定義一個函數要使用 def 語句,依次寫出函數名、括號、括號中的參數和冒號:,然后,在縮進塊中編寫函數體,函數的返回值用 return 語句返回。
def say_b(): print "b" say_b() #調用函數,打印出b
2、如果沒有return語句,函數執行完畢后也會返回結果,只是結果為 None。
3、函數返回多個值
import math def move(x, y, step, angle): nx = x + step * math.cos(angle) ny = y - step * math.sin(angle) return nx, ny x, y = move(100, 100, 60, math.pi / 6) print x, y #151.961524227 70.0 r = move(100, 100, 60, math.pi / 6) print r #(151.96152422706632, 70.0)
其實這只是一種假象,Python函數返回的仍然是單一值,是一個tuple:
但是,在語法上,返回一個tuple可以省略括號,而多個變量可以同時接收一個tuple,按位置賦給對應的值,所以,Python的函數返回多值其實就是返回一個tuple,但寫起來更方便。
4、在函數內部,可以調用其他函數。如果一個函數在內部調用自身本身,這個函數就是遞歸函數。
def fact(n): if n==1: return 1 return n * fact(n - 1) print fact(10) #計算10的階乘
5、定義函數的時候,還可以有默認參數。
def power(x, n=2): s = 1 while n > 0: n = n - 1 s = s * x return s print power(2) #默認計算2的平方 print power(2,3) #計算2的三次方
由於函數的參數按從左到右的順序匹配,所以默認參數只能定義在必需參數的后面:
6、一個函數能接受任意個參數,我們就可以定義一個可變參數:
def fn(*args): print args fn('a') #('a',) fn('a', 'b') #('a', 'b') fn('a', 'b', 'c') #('a', 'b', 'c')
可變參數的名字前面有個 * 號,我們可以傳入0個、1個或多個參數給可變參數:
7、基本數據類型的參數:值傳遞
表作為參數:指針傳遞
(14)pass,del,exec,eval
1、pass語句
pass代表該語句什么都不做,因為python中空代碼是非法的,比如一個if語句要求什么內容都不做,我們就可以使用pass語句。
2、del語句
一般來說python會刪除那些不在使用的對象(因為使用者不會再通過任何變量或者數據結構引用它們)
3、exec語句(運行字符串中的程序)
exec "print 'hello world'" #hello world
4、eval函數(會計算python表達式(以字符串形式書寫),並且返回結果)
print eval('2+ 2') #4 print eval(raw_input("please input number:")) #輸入2+2 得4
(15)內建函數
1、cmp(obj1, obj2) 比較 obj1 和 obj2, 根據比較結果返回整數 i:
if obj1 < obj2 返回i < 0
if obj1 > obj2 返回i > 0
if obj1 == obj2 返回i == 0
如果是用戶自定義對象, cmp()會調用該類的特殊方法__cmp__()
2、str() 強制轉換成字符串
3、type() :詳見(3)數據類型 - 3、
4、help():通過用函數名作為 help()的參數就能得到相應的幫助信息
5、isinstance(變量名,類型): 判斷是否是這個類型的元素,可以用if語句
6、abs():取絕對值
7、enumerate():詳見(10)循環 - 5、
8、len(seq):返回seq的長度
9、sorted(iter):排序,會調用cmp()
10、zip(a1,a2……):詳見(10)循環 - 6、
11、range():詳見(10)循環 - 4、
12、string.lower():轉換字符串中所有大寫字符為小寫
13、string.upper():轉換字符串中所有小寫字符為大寫
14、string.strip():刪去字符串開頭和結尾的空格
15、string.capitalize():把字符串第一個字符大寫
16、string.title():所有單詞都以大寫開頭
17、max()和min():找出最大和最小值
18、sum():求和
19、reversed():倒序輸出
Python進階語法:
(1)文件
1、文件也是一個對象。
2、打開文件
f = open(文件名,模式)
文件名可以是相對路徑或者絕對路徑
模式有:"r" 只讀、“w” 寫入、“a” 追加,“r+/w+”讀寫
使用 'r' 或 'U' 模式打開的文件必須是已經存在的。 使用 'w' 模式打開的文件若存在則首先清空, 然后(重新)創建。 以 'a' 模式打開的文件是為追加數據作准備的, 所有寫入的數據都將追加到文件的末尾。 即使你 seek 到了其它的地方。 如果文件不存在, 將被自動創建, 類似以 'w'模式打開文件。
test = open("test.txt", "w")
3、屬性
test = open("test.txt", "w") print "文件名: ", test.name #文件名: test.txt print "是否已關閉 : ", test.closed #是否已關閉 : False print "訪問模式 : ", test.mode #訪問模式 : w
4、關閉 close()
test = open("test.txt", "w") test.close() print "是否已關閉 : ", test.closed #是否已關閉 : True
5、寫入write()
write()方法可將任何字符串寫入一個打開的文件。
write()方法不在字符串的結尾不添加換行符('\n'):
test = open("test.txt", "w") test.write("this is a test\n this is a test again \n") test.close() #可以在文件中看到 # this is a test # this is a test again
主動調用close()寫緩存同步到磁盤,或者寫入數據量大於或等於寫緩存,寫緩存同步到磁盤
6、讀取 read()、readline()、readlines()
read(size)方法從一個打開的文件中讀取一個字符串。size若不填則為盡量多的字符串,若填了則為結束位置。
readline()讀取當前行,允許有參數
readlines()讀取剩余行,返回一個字符串列表
test = open ("test.txt", "w") test.write("python is a language \npython is a great language ") test.close() test = open("test.txt", "r") str = test.read() print str test.close() #python is a language #python is a great language
7、文件位置
tell()方法告訴你文件內的當前位置;即下一次的讀寫會發生在文件開頭這么多字節之后:
seek(offset [,from])方法改變當前文件的位置。Offset變量表示要移動的字節數。From變量指定開始移動字節的參考位置。如果from被設為0,這意味着將文件的開頭作為移動字節的參考位置。如果設為1,則使用當前的位置作為參考位置。如果它被設為2,那么該文件的末尾將作為參考位置。
test = open ("test.txt", "w") test.write("python is a language \npython is a great language ") test.close() test = open("test.txt", "r") str = test.read() print "the first input:\n",str #輸出 # the first input: # python is a language # python is a great language # 查找當前位置 position = test.tell() print position #50 # 把指針再次重新定位到文件開頭 position = test.seek(0, 0) str2 = test.read(10) print "the second input:\n", str2 # the second input: # python is test.close()
8、重命名
Python的os模塊提供了幫你執行文件處理操作的方法,必須先導入它,才可以調用。
os.rename(當前文件名,新的文件名)
9、刪除文件
同樣需要導入os模塊,才可以調用。
os.remove(文件名)
(2)錯誤和異常
1、錯誤類型
OverflowError數值運算超出最大限制
ZeroDivisionError 除(或取模)零 (所有數據類型)
AttributeError對象沒有這個屬性
IOError 輸入/輸出操作失敗
IndexError 序列中沒有此索引(index)
NameError 未聲明/初始化對象 (沒有屬性)
SyntaxError Python 語法錯誤
TypeError 對類型無效的操作
ValueError 傳入無效的參數
2、try-except處理異常
try-except語句用來檢測try語句塊中的錯誤,從而讓except語句捕獲異常信息並處理。
try: <語句> #運行別的代碼 except Exception1,e:#Exception是錯誤類型名,e是儲存錯誤,可以調用 <語句> #如果在try部份引發了'名字'異常 except Exception2,e: <語句> #如果引發了'名字'異常,獲得附加的數據 else: <語句> #如果沒有異常發生
當try后的語句執行時發生異常,python就跳回到try並執行第一個匹配該異常的except子句,異常處理完畢,控制流就通過整個try語句(除非在處理異常時又引發新的異常)。如果在try子句執行時沒有發生異常,python將執行else語句后的語句(如果有else的話),然后控制流通過整個try語句。
try: fh = open("testfile", "w") fh.write("This is my test file for exception handling!!") except IOError: print "Error: can\'t find file or read data" else: print "Written content in the file successfully" #Written content in the file successfully try: fh = open("testfile", "r") #只讀文件不能寫入 fh.write("This is my test file for exception handling!!") except IOError,e: print "Error: can\'t find file or read data" print "catch error:",e else: print "Written content in the file successfully" #Error: can't find file or read data #atch error: File not open for writing
except若不帶任何異常類型,即捕獲所有發生的異常。但是不能捕獲語法錯誤異常,如if a,因為是運行前錯誤而不是運行時錯誤
也可帶多種類型except(Exception1[, Exception2[,...ExceptionN]]]):
錯誤類型后面跟着變量e,可以print錯誤提示
案例如下
import random num = random.randint(0,100) while 1: try: guess = int(raw_input("Enter 1-100:")) except ValueError,e: print "error ! please enter 1-100" continue if guess > num: print "guess bigger:",guess elif guess < num: print "guess smaller:",guess else: print "guess right,game over" break
3、try-finally語句
語句是否發生異常都將執行最后的代碼。將異常保留下來交給系統處理,本身不處理異常。
作用:為處理異常事件提供清理機制,用來關閉文件或者釋放系統資源。
try: <語句> finally: <語句> #退出try時總會執行 raise
可以使用except語句或者finally語句,但是兩者不能同時使用。else語句也不能與finally語句同時使用。
4、try-except-finally
若try語句沒有捕獲異常,執行完try代碼段后,執行finally
若try捕獲異常,首先執行except處理異常,然后執行finally
5、try-except-else-finally
若try語句沒有捕獲異常,執行完try代碼段后,執行else代碼段,最后執行finally
若try捕獲異常,首先執行except處理錯誤,然后執行finally
6、try-finally-except
當在try塊中拋出一個異常,立即執行finally塊代碼。
finally塊中的所有語句執行后,異常被再次提出,並執行except塊代碼。
7、with語句
用來代替try-except-finally語句,使代碼更加簡潔
with context[as var]: with_suite
context表達式返回是一個對象
var用來保存context返回對象,單個返回值或元組
with_suite使用var變量對context返回對象進行操作
with open("1.text") as f: for line in f.readline(): print line
1、打開1.txt文件
2、f變量接收文件對象返回的對象
3、with中的代碼執行完成后,關閉文件
程序使用了上下文管理器 (with...as...)。上下文管理器有隸屬於它的程序塊。當隸屬的程序塊執行結束的時候(也就是不再縮進),上下文管理器自動關閉了文件
運用情況:①文件操作 ②進城之間互斥操作:例如互斥鎖 ③支持上下文的其他對象
8、raise主動拋出異常
#格式 rasie [exception[,args]] #Exception 異常類 #args 描述異常信息的元組 raise TypeError,"Test Error" #TypeError: Test Error
9、assert語句
斷言語句:assert語句是用於檢測表達式是否為真,如果為假,引發AssertionError錯誤
#格式 assert expression [,args] #expression 表達式 #args 判斷條件的描述信息
10、自定義異常
通過創建一個新的異常類,程序可以命名它們自己的異常。異常應該是典型的繼承自Exception類,通過直接或間接的方式。
自定義異常只能主動觸發。
class FileError(IOError): pass try: raise FileError,"test error" except FileError,e: print e #test error class CustomError(Exception): def __init__(self,info): Exception.__init__(self) self.errorinfo = info def __str__(self): return "CustomError:%s" %self.errorinfo try: raise CustomError("test CustomError") except CustomError,e: print "ErrorInfo:",e #ErrorInfo: CustomError:test CustomError
(3)模塊和包
1、概念介紹
#test.py 自身模塊名test
import p1.util 引用p1包的模塊util
print p1.util.f(2) 調用p1.util的函數f()
如何區分包和普通目錄 包下面有個__inti__.py文件
2、如果我們只希望導入用到的math模塊的某幾個函數,而不是所有函數,可以用下面的語句:
from math import pow, sin, log
3、可以給函數起個“別名”來避免沖突:as
from math import log from logging import log as logger # logging的log現在變成了logger print log(10) # 調用的是math的log logger(10, 'import from logging') # 調用的是logging的log
4、如果導入的模塊不存在,Python解釋器會報 ImportError 錯誤:
5、第三方模塊管理系統
-easy_install
-pip(推薦,已內置到Python2.7.9)
(4)作用域
1、函數作用域LEGB L>E>G>B
L:local 函數內部作用域
E:enclosing 函數內部與內嵌函數之間,即閉包
G:global全局作用域
B:bulid-in 內置作用域 list,tuple之類
passline = 60 #全局 def func(val): passline = 90 #函數內部 if val >= passline: print "pass" else: print "failed" def in_func(): print val #函數內部與內嵌函數之間 in_func() func(69) #failed #69
(5)高階函數
1、由於參數 x, y和 f 都可以任意傳入,如果 f 傳入其他函數,就可以得到不同的返回值。
def add(x, y, f): return f(x) + f(y) print add(-5, 9, abs) #abs(-5) + abs(9) = 14
2、map() 映射
map()是 Python 內置的高階函數,它接收一個函數f和一個list,並通過把函數f依次作用在 list的每個元素上,得到一個新的 list 並返回。
def f(x): return x*x print map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) #[1, 4, 9, 16, 25, 36, 49, 64, 81]
利用map()函數,可以把一個 list 轉換為另一個 list,只需要傳入轉換函數。
由於list包含的元素可以是任何類型,因此,map()不僅僅可以處理只包含數值的 list,事實上它可以處理包含任意類型的 list,只要傳入的函數f可以處理這種數據類型。
3、reduce()折疊
reduce()函數接收的參數和 map()類似,一個函數f,一個list,但reduce()傳入的函數f必須接收兩個參數,reduce()對list的每個元素反復調用函數f,並返回最終結果值。
def f(x, y): return x + y print reduce(f, [1, 3, 5, 7, 9]) #25 #先計算頭兩個元素:f(1, 3),結果為4; #再把結果和第3個元素計算:f(4, 5),結果為9; #再把結果和第4個元素計算:f(9, 7),結果為16; #再把結果和第5個元素計算:f(16, 9),結果為25; #由於沒有更多的元素了,計算結束,返回結果25。 #初始值100 print reduce(f, [1, 3, 5, 7, 9], 100) #125
reduce()還可以接收第3個可選參數,作為計算的初始值。
4、filter() 過濾
filter()函數接收一個函數f和一個list,這個函數 f 的作用是對每個元素進行判斷,返回 True或 False,filter()根據判斷結果自動過濾掉不符合條件的元素,返回由符合條件元素組成的新list。
def is_odd(x): #是奇數 return x % 2 == 1 print filter(is_odd, [1, 4, 6, 7, 9, 12, 17]) #[1, 7, 9, 17]
利用filter(),可以完成很多有用的功能,例如,刪除 None 或者空字符串:
def is_not_empty(s): return s and len(s.strip()) > 0 print filter(is_not_empty, ['test', None, '', 'str', ' ', 'END']) #['test', 'str', 'END']
5、sorted():對list進行排序
print sorted([36, 5, 12, 9, 21]) #[5, 9, 12, 21, 36]
但sorted()也是一個高階函數,它可以接收一個比較函數來實現自定義排序,比較函數的定義是,傳入兩個待比較的元素x, y,如果x應該排在y的前面,返回-1,如果x應該排在y的后面,返回1。如果x和y相等,返回0。
#倒序排序 def reversed_cmp(x, y): if x > y: return -1 if x < y: return 1 return 0 print sorted([36, 5, 12, 9, 21], reversed_cmp) #[36, 21, 12, 9, 5]
6、Python的函數不但可以返回int、str、list、dict等數據類型,還可以返回函數!
def f(): print 'call f()...' # 定義函數g: def g(): print 'call g()...' # 返回函數g: return g x = f() # 調用f() call f()... x # 變量x是f()返回的函數: #<function g at 0x00000000022CDA58> x() # x指向函數,因此可以調用 call g()... x=f(),x()=g()
請注意區分返回函數和返回值:
def myabs(): return abs # 返回函數 def myabs2(x): return abs(x) # 返回函數調用的結果,返回值是一個數值
在函數內部定義的函數和外部定義的函數是一樣的,只是他們無法被外部訪問:
7、閉包(closure)
內部函數中引用了外層函數的變量(enclosing作用域的變量),然后返回內層函數的情況。
閉包的作用是封裝和代碼復用。
傳遞的是參數
#如果要實現兩個功能,可以定義兩個函數。 def func_150(val): passline = 90 #150 if val >= passline: print ("pass") else: print "failed" def func_100(val): passline = 60 #150 if val >= passline: print ("pass") else: print "failed" func_100(69)#pass func_150(69)#failed #如果用閉包的話只需要定義一個函數 def set_passline(passline):#passline def cmp(val): if val >= passline: print "pass" else: print "failed" return cmp #返回值是一個函數 f_100 = set_passline(60) #f_100就是cmp,f_100()就是cmp(),而且內置一個passline=60 f_150 = set_passline(90) f_100(69)#pass f_150(69)#failed
傳遞的是函數
#不用閉包 def my_sum(*arg): if len(arg)==0: return 0 for val in arg: if not isinstance(val,int): return 0 return sum(arg) def my_average(*arg): if len(arg)==0: return 0 for val in arg: if not isinstance(val,int): return 0 return sum(arg)/len(arg) print my_sum(1,2,3,4,5) #15 print my_sum(1,2,3,4,5,'6')#0 print my_average(1,2,3,4,5)#3 print my_average()#0 #使用閉包 def my_sum(*arg): print "in my_sum" return sum(arg) def my_average(*arg): return sum(arg)/len(arg) def dec(func): def in_dec(*arg): print "in_dec()=",arg if len(arg) == 0: return 0 for val in arg: if not isinstance(val, int): return 0 return func(*arg) return in_dec #別加括號 #dec return in_dec -> my_sum #my_sum = in_dec(*arg) my_sum = dec(my_sum) my_average = dec(my_average)#同理 print my_sum(1,2,3,4,5) print my_sum(1,2,3,4,5,'6') # 結果 # in_dec()= (1, 2, 3, 4, 5) # in my_sum # 15 # in_dec()= (1, 2, 3, 4, 5, '6') # 0
正確使用閉包,就要確保引用的局部變量在函數返回后不能變。
返回函數不要引用任何循環變量,或者后續會發生變化的變量。
# 希望一次返回3個函數,分別計算1x1,2x2,3x3: def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count() print f1(),f2(),f3() #9 9 9 #當count()函數返回了3個函數時,這3個函數所引用的變量 i 的值已經變成了3。由於f1、f2、f3並沒有被調用,所以,此時他們並未計算 i*i,當 f1 被調用時,才計算i*i,但現在i的值已經變為3 #正確如下 def count(): fs = [] for i in range(1, 4): def f(j): def g(): return j*j return g r = f(i) fs.append(r) return fs f1, f2, f3 = count() print f1(), f2(), f3() #1 4 9
8、匿名函數lambda
def f(x): return x*x print map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) #[1, 4, 9, 16, 25, 36, 49, 64, 81] print map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]) #[1, 4, 9, 16, 25, 36, 49, 64, 81]
通過對比可以看出,匿名函數 lambda x: x * x 實際上就是:
def f(x):
return x * x
關鍵字lambda表示匿名函數,冒號前面的x 表示函數參數。
匿名函數有個限制,就是只能有一個表達式,不寫return,返回值就是該表達式的結果。
使用匿名函數,可以不必定義函數名,直接創建一個函數對象,很多時候可以簡化代碼:
print sorted([1, 3, 9, 5, 0], lambda x,y: -cmp(x,y)) #[9, 5, 3, 1, 0]
返回函數的時候,也可以返回匿名函數:
myabs = lambda x: -x if x < 0 else x print myabs(-1) #1 print myabs(1) #1
(6)裝飾器
1、decorator本質上就是一個高階函數,它接收一個函數作為參數,然后,返回一個新函數。
使用 decorator 用Python提供的@語法,這樣可以避免手動編寫f=decorate(f)這樣的代碼。
2、裝飾器用來裝飾函數,返回一個函數對象
被裝飾函數標識符指向返回的函數對象
def dec(func): print "call dec" def in_dec(*arg): print "in_dec()=",arg if len(arg) == 0: return 0 for val in arg: if not isinstance(val, int): return 0 return func(*arg) return in_dec #別加括號 @dec # 代替了my_sum = dec(my_sum) def my_sum(*arg): #my_sum = in_dec print "in my_sum" return sum(arg) print my_sum(1,2,3,4,5) # call dec # in_dec()= (1, 2, 3, 4, 5) # in my_sum # 15
3、傳入函數,含有兩個參數
def deco(func): def in_deco(x,y): print "in deco" func(x,y) print "call deco" return in_deco @deco #代替 bar = deco(bar) = in_deco #bar()-> in_deco()->bar() def bar(x,y): print "in bar",x+y bar(1,2) # call deco # in deco # in bar
4、@log的定義
def log(f): def fn(x): print 'call ' + f.__name__ + '()...' return f(x) return fn @log def factorial(n): return reduce(lambda x,y: x*y, range(1, n+1)) print factorial(10) # call factorial()... # 3628800
但是,對於參數不是一個的函數,調用將報錯。@log寫死了只含一個參數的返回函數。
5、要讓@log自適應任何參數定義的函數,可以利用Python的*args和**kw,保證任意個數的參數總是能正常調用:
可變參數*args表示任何多個無名參數,它是一個tuple;**kwargs表示關鍵字參數,它是一個dict。並且同時使用*args和**kwargs時,必須*args參數列要在**kwargs前。
def log(f): def fn(*args, **kw): print 'call ' + f.__name__ + '()...' return f(*args, **kw) return fn @log def add(x, y): return x + y print add(1, 2) # call add()... # 3
6、@performance,它可以打印出函數調用的時間。
計算函數調用的時間可以記錄調用前后的當前時間戳,然后計算兩個時間戳的差。
import time def performance(f): def fn(*args, **kw): t1 = time.time() r = f(*args, **kw) t2 = time.time() print 'call %s() in %fs' % (f.__name__, (t2 - t1)) return r return fn @performance def factorial(n): return reduce(lambda x,y: x*y, range(1, n+1)) print factorial(5) # call factorial() in 0.000000s # 120
7、對於被裝飾的函數,log打印的語句是不能變的(除了函數名)。
如果有的函數非常重要,希望打印出'[INFO] call xxx()...',有的函數不太重要,希望打印出'[DEBUG] call xxx()...',這時,log函數本身就需要傳入'INFO'或'DEBUG'這樣的參數:
@log('DEBUG') def my_func(): pass #把上面的定義翻譯成高階函數的調用,就是: my_func = log('DEBUG')(my_func) #上面的語句看上去還是比較繞,再展開一下: log_decorator = log('DEBUG') my_func = log_decorator(my_func) #上面的語句又相當於: log_decorator = log('DEBUG') @log_decorator def my_func(): pass
所以,帶參數的log函數首先返回一個decorator函數,再讓這個decorator函數接收my_func並返回新函數:
而且wrapper(*args, **kw)要調用外層參數prefix,所以無法拆開
def log(prefix): def log_decorator(f): def wrapper(*args, **kw): print '[%s] %s()...' % (prefix, f.__name__) return f(*args, **kw) return wrapper return log_decorator @log('DEBUG') def test(): pass print test() # [DEBUG] test()... # None
8、區別
#在沒有decorator的情況下,打印函數名: def f1(x): pass print f1.__name__ #f1 #有decorator的情況下,再打印函數名: def log(f): def wrapper(*args, **kw): print 'call...' return f(*args, **kw) return wrapper @log def f2(x): pass print f2.__name__ #wrapper
可見,由於decorator返回的新函數函數名已經不是'f2',而是@log內部定義的'wrapper'。這對於那些依賴函數名的代碼就會失效。decorator還改變了函數的__doc__等其它屬性。如果要讓調用者看不出一個函數經過了@decorator的“改造”,就需要把原函數的一些屬性復制到新函數中:
def log(f): def wrapper(*args, **kw): print 'call...' return f(*args, **kw) wrapper.__name__ = f.__name__ wrapper.__doc__ = f.__doc__ return wrapper
這樣寫decorator很不方便,因為我們也很難把原函數的所有必要屬性都一個一個復制到新函數上,所以Python內置的functools可以用來自動化完成這個“復制”的任務:
import functools def log(f): @functools.wraps(f) def wrapper(*args, **kw): print 'call...' return f(*args, **kw) return wrapper @log def f2(x): pass print f2.__name__ #f2
9、當一個函數有很多參數時,調用者就需要提供多個參數。如果減少參數個數,就可以簡化調用者的負擔。
比如,int()函數可以把字符串轉換為整數,當僅傳入字符串時,int()函數默認按十進制轉換:
但int()函數也提供額外的base參數,默認為10。如果傳入base參數,就可以做N進制轉換:
print int("10") #10 print int('10', 8) #8 print int('A', 16) #10
假設要轉換大量的二進制字符串,每次都傳入int(x, base=2)非常麻煩,於是,我們想到,可以定義一個int2()的函數,默認把base=2傳進去:
def int2(x, base=2): return int(x, base) print int2('1000000') #64 print int2('1010101') #85
functools.partial就是幫助我們創建一個偏函數的,不需要我們自己定義int2(),可以直接使用下面的代碼創建一個新的函數int2:
import functools int2 = functools.partial(int, base=2) print int2('1000000') #64 print int2('1010101') #85
所以,functools.partial可以把一個參數多的函數變成一個參數少的新函數,少的參數需要在創建時指定默認值,這樣,新函數調用的難度就降低了。
(7)參數
1、位置參數必須以在被調用函數中定義的准確順序來傳遞,參數數目必須一致。
2、所有必需的參數都要在默認參數之前。
# 位置參數 def func_with_parameters(x, y): print(x, y) func_with_parameters(1, 2) #默認值參數 def func_with_default_value_parameters(x, y, z = 3): print(x, y, z) func_with_default_value_parameters(y = 2, x = 1)
3、如果命名了參數,這里可以不按順序給出參數。
#命名參數 def func_with_named_parameters(x, y, z): print(x, y, z) func_with_named_parameters(z = 1, y = 2, x = 3)
4、變長的參數在函數聲明中不是顯式命名的,因為參數的數目在運行時之前是未知的(甚至在運行的期間,每次函數調用的參數的數目也可能是不同的),這和常規參數(位置和默認)明顯不同,常規參數都是在函數聲明中命名的。由於函數調用提供了關鍵字以及非關鍵字兩種參數類型,python 用兩種方法來支持變長參數。
func(*tuple_grp_nonkw_args, **dict_grp_kw_args)
其中的 tuple_grp_nonkw_args 是以元組形式體現的非關鍵字參數組, dict_grp_kw_args 是裝有關鍵字參數的字典。
5、可變長的參數元組必須在位置和默認參數之后。
# 收集多余的位置參數 def func_with_collection_rest_parameters(x, y=0, *rest): print(x, y) print(rest) func_with_collection_rest_parameters(1, 2, 3, 4, 5)
星號操作符之后的形參將作為元組傳遞給函數,元組保存了所有傳遞給函數的"額外"的參數(匹配了所有位置和具名參數后剩余的)。如果沒有給出額外的參數,元組為空。
6、關鍵字變量參數(Dictionary)
在我們有不定數目的或者額外集合的關鍵字的情況中, 參數被放入一個字典中,字典中鍵為參數名,值為相應的參數值。
#收集命名參數 def func_with_collection_rest_naned_parameters(*args, **kw): print(args) print(kw) func_with_collection_rest_naned_parameters(1, 2, 3, x = 4, y = 5, z = 6) func_with_collection_rest_naned_parameters([1, 2, 3], {"x": 4, "y": 4, "z": 6}) #這會導致args[0]指向第一個實參,args[1]指向第二個實參。 #([1, 2, 3], {'y': 4, 'x': 4, 'z': 6}) #{} func_with_collection_rest_naned_parameters(*[1, 2, 3], **{"x": 4, "y": 4, "z": 6}) #這里的執行相當於func_with_collection_rest_naned_parameters(1, 2, 3, x = 4, y = 5, z = 6)。
(8)面向對象
1、類通過class關鍵字定義。類名以大寫字母開頭,緊接着是(object),表示該類是從哪個類繼承下來的。
類也要細致命名,像“AddrBookEntry”,“RepairShop”等等就是很好的名字
Python 並不支持純虛函數(像 C++)或者抽象方法(如在 JAVA 中)
class Person(object): pass
2、有了Person類的定義,就可以創建出具體的xiaoming、xiaohong等實例。創建實例使用類名+(),類似函數調用的形式創建:
Python 規范推薦使用駱駝記法的下划線方式,比如,“update_phone”“update_email”。
xiaoming = Person() xiaohong = Person()
3、由於Python是動態語言,對每一個實例,都可以直接給他們的屬性賦值:
xiaoming = Person() xiaoming.name = 'Xiao Ming' xiaoming.gender = 'Male' xiaoming.birth = '1990-1-1'
4、構造函數__init__()方法
class Person(object): def __init__(self, name, gender, birth): self.name = name self.gender = gender self.birth = birth
__init__()方法的第一個參數必須是self(也可以用別的名字,但建議使用習慣用法),后續參數則可以自由指定,和定義函數沒有任何區別。
相應地,創建實例時,就必須要提供除self以外的參數:
xiaoming = Person('Xiao Ming', 'Male', '1991-1-1') xiaohong = Person('Xiao Hong', 'Female', '1992-2-2') print xiaoming.name # 輸出 'Xiao Ming' print xiaohong.birth # 輸出 '1992-2-2'
定義Person類的__init__方法,除了接受 name、gender 和 birth 外,還可接受任意關鍵字參數,並把他們都作為屬性賦值給實例。
要定義關鍵字參數,使用 **kw;
除了可以直接使用self.name = 'xxx'設置一個屬性外,還可以通過 setattr(self, 'name', 'xxx') 設置屬性。
class Person(object): def __init__(self, name, gender, birth, **kw): self.name = name self.gender = gender self.birth = birth for k, v in kw.iteritems(): setattr(self, k, v) xiaoming = Person('Xiao Ming', 'Male', '1990-1-1', job='Student') print xiaoming.name #輸出Xiao Ming print xiaoming.job #輸出Student
5、析構函數
由於 Python 具有垃圾對象回收機制(靠引用計數),這個函數要直到該實例對象所有的引用都被清除掉后才會執行。所以很少用到。
class Person(object): def __init__(self, ……):
6、Python對屬性權限的控制是通過屬性名來實現的,如果一個屬性由雙下划線開頭(__),該屬性就無法被外部訪問。
class Person(object): def __init__(self, name): self.name = name self._title = 'Mr' self.__job = 'Student' p = Person('Bob') print p.name # => Bob print p._title # => Mr print p._Person__job # => Student #所以實際上並不是嚴格的私有成員 print p.__job # => Error
但是,如果一個屬性以"__xxx__"的形式定義,那它又可以被外部訪問了,以"__xxx__"定義的屬性在Python的類中被稱為特殊屬性,有很多預定義的特殊屬性可以使用,通常我們不要把普通屬性用"__xxx__"定義。
以單下划線開頭的屬性"_xxx"可以在子類中使用,不應該被外部訪問,理解為保護成員。
"__xxx"可以理解為私有成員,但實質並不是,不建議訪問。
7、類屬性是直接綁定在類上的,所以,訪問類屬性不需要創建實例,就可以直接訪問:
class Person(object): address = 'Earth' def __init__(self, name): self.name = name p1=Person(xiaoming) print Person.address # => Earth print p1.address # => Earth # 由於Python是動態語言,類屬性也是可以動態添加和修改的: Person.address = 'China' print p1.address # => 'China'
8、在實例變量上修改類屬性
當實例屬性和類屬性重名時,實例屬性優先級高,它將屏蔽掉對類屬性的訪問。而其他不變
9、訪問類的屬性
有兩種方法。最簡單的是使用 dir()內建函數。另外是通過訪問類的字典屬性__dict__,這是所有類都具備的特殊屬性之一。
10、實例的方法。
實例的方法就是在類中定義的函數,它的第一個參數永遠是self,指向調用該方法的實例本身,其他參數和一個普通函數是完全一樣的:在其他語言中,self就是this.
class Person(object): def __init__(self, name): self.__name = name def get_name(self): return self.__name p1 = Person('Bob') print p1.get_name() # self不需要顯式傳入 # => Bob print p1.__dict__ # {'_Person__name': 'Bob'} print p1._Person__name # => Bob
在實例方法內部,可以訪問所有實例屬性,這樣,如果外部需要訪問私有屬性,可以通過方法調用獲得,這種數據封裝的形式除了能保護內部數據一致性外,還可以簡化外部調用的難度。
11、方法也分實例方法和類方法。
@classmethod 調用的時候用類名而不是某個對象
在class中定義的全部是實例方法,實例方法第一個參數self是實例本身。
要在class中定義類方法,需要這么寫:
class Person(object): count = 0 @classmethod def how_many(cls): return cls.count def __init__(self, name): self.name = name Person.count = Person.count + 1 print Person.how_many() #0 p1 = Person('Bob') print Person.how_many() #1
通過標記一個@classmethod,該方法將綁定到Person類上,而非類的實例。類方法的第一個參數將傳入類本身,通常將參數名命名為cls,上面的cls.count實際上相當於Person.count。
因為是在類上調用,而非實例上調用,因此類方法無法獲得任何實例變量,只能獲得類的引用
12、@property 像訪問屬性一樣調用方法,即不用括號
class Person(object): count = 0 def __init__(self, name,age,weight): self.name = name self._age = age self.__weight = weight Person.count = Person.count + 1 @classmethod def how_many(cls): return cls.count @property def get_weight(self): return self.__weight print Person.how_many() # 0 p1 = Person('Bob',20,50) print Person.how_many() # 1 print p1.get_weight #50
get/set方法:
class Student(object): def __init__(self, name, score): self.name = name self.__score = score def get_score(self): return self.__score def set_score(self, score): if score < 0 or score > 100: raise ValueError('invalid score') self.__score = score #但是寫 s.get_score() 和 s.set_score() 沒有直接寫 s.score 來得直接。
用裝飾器函數把get/set方法“裝飾”成屬性調用:
把一個getter方法變成屬性,只需要加上 @ property就可以了
setter是關鍵字,這種“@+方法名字+點+setter”是個固定格式與@property搭配使用。
class Student(object): def __init__(self, name, score): self.name = name self.__score = score @property def score(self): return self.__score @score.setter def score(self, score): if score < 0 or score > 100: raise ValueError('invalid score') self.__score = score s = Student("Bob",100) print s.score #100 s.score = 90 #屬性賦值 print s.score #90
13、函數和方法
函數是直接調用函數名,僅僅是一部分代碼
方法必須和對象結合在一起使用,是類的一部分
方法可以看做是類的屬性
class Test(object): def test(self): pass a = Test() print a.test #<bound method Test.test of <__main__.Test object at 0x00000000022B8E10>> print a.test() #None a.test= "123" print a.test #123 print a.test() #報錯
14、定義子類
class Person(object): def __init__(self, name, gender): self.name = name self.gender = gender class Student(Person): def __init__(self, name, gender, score): super(Student, self).__init__(name, gender) #初始化 self.score = score
一定要用super(Student, self).__init__(name, gender)去初始化父類,否則,繼承自Person的Student將沒有name和gender。
函數super(Student, self)將返回當前類繼承的父類,即Person,然后調用__init__()方法,注意self參數已在super()中傳入,在__init__()中將隱式傳遞,不需要寫出(也不能寫)
使用super()的漂亮之處在於,你不需要明確給出任何基類名字,這意味着如果你改變了類繼承關系,你只需要改一行代碼(class語句本身)而不必在大量代碼中去查找所有被修改的那個類的名字。
一個實例可以看成它本身的類型,也可以看成它父類的類型。
15、多重繼承
class A(object): def __init__(self, a): print 'init A...' self.a = a class B(A): def __init__(self, a): super(B, self).__init__(a) print 'init B...' class C(A): def __init__(self, a): super(C, self).__init__(a) print 'init C...' class D(B, C): def __init__(self, a): super(D, self).__init__(a) print 'init D...'
像這樣,D 同時繼承自 B 和 C,也就是 D 擁有了 A、B、C 的全部功能。多重繼承通過super()調用__init__()方法時,A 雖然被繼承了兩次,但__init__()只調用一次:
多重繼承的目的是從兩種繼承樹中分別選擇並繼承出子類,以便組合功能使用。
舉個例子,Python的網絡服務器有TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer,而服務器運行模式有多進程ForkingMixin和多線程ThreadingMixin兩種。
要創建多進程模式的 TCPServer:
class MyTCPServer(TCPServer, ForkingMixin) pass
要創建多線程模式的 UDPServer:
class MyUDPServer(UDPServer, ThreadingMixin): pass
如果沒有多重繼承,要實現上述所有可能的組合需要 4x2=8 個子類。
16、多態
用一個類繼承多個類,調用同一個方法,會有不同的反應,因為被重寫了。
17、鴨子類型
定義:“當看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子。
這種風格適用於動態語言(比如PHP、Python、Ruby、Typescript、Perl、Objective-C、Lua、Julia、JavaScript、Java、Groovy、C#等)和某些靜態語言
在鴨子類型中,關注的不是對象的類型本身,而是它是如何使用的。例如,在不使用鴨子類型的語言中,我們可以編寫一個函數,它接受一個類型為鴨的對象,並調用它的走和叫方法。在使用鴨子類型的語言中,這樣的一個函數可以接受一個任意類型的對象,並調用它的走和叫方法。
鴨子類型通常得益於不測試方法和函數中參數的類型,而是依賴文檔、清晰的代碼和測試來確保正確使用。從靜態類型語言轉向動態類型語言的用戶通常試圖添加一些靜態的(在運行之前的)類型檢查,從而影響了鴨子類型的益處和可伸縮性,並約束了語言的動態特性。
鴨子類型比接口更好用。
class TestClass1: def say(self): print("我是鴨子1") class TestClass2: def say(self): print("我是鴨子2") def duck_say(duck): duck.say() duck_say(TestClass1()) # 我是鴨子1 duck_say(TestClass2()) # 我是鴨子2
18、getattr()、setattr()和delattr():
getattr()和 setattr()函數相應地取得和賦值給對象的屬性,
getattr()會在你試圖讀取一個不存在的屬性時,引發 AttributeError 異常,除非給出那個可選的默認參數。
setattr()將要么加入一個新的屬性,要么取代一個已存在的屬性。
delattr()函數會從一個對象中刪除屬性
class Person(object): def __init__(self, name, gender): self.name = name self.gender = gender class Student(Person): def __init__(self, name, gender, score): super(Student, self).__init__(name, gender) self.score = score def whoAmI(self): return 'I am a Student, my name is %s' % self.name s = Student('Bob', 'Male', 88) print getattr(s, 'name') # 獲取name屬性 #Bob setattr(s, 'name', 'Adam') # 設置新的name屬性 print s.name #Adam getattr(s, 'age') # 獲取age屬性,但是屬性不存在,報錯: getattr(s, 'age', 20) # 獲取age屬性,如果屬性不存在,就返回默認值20:
(9)定制類(魔術方法)
1、__str__():把一個類的實例變成 str
__repr__()轉換為機器看的字符串,可以由eval()執行
class Person(object): def __init__(self, name, gender): self.name = name self.gender = gender def __str__(self): return '(Person: %s, %s)' % (self.name, self.gender) p = Person('Bob', 'male') print p #(Person: Bob, male)
2、比較運算符_cmp__():可以實現對一組 Student 類的實例排序
__eq__()判斷等於,__lt__()判斷小於,__gt__()判斷大於
class Student(object): def __init__(self, name, score): self.name = name self.score = score def __str__(self): return '(%s: %s)' % (self.name, self.score) def __cmp__(self, s): if self.name < s.name: return -1 elif self.name > s.name: return 1 else: return 0 L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 77)] print sorted(L) #[(Alice: 77), (Bob: 88), (Tim: 99)] L = [Student('Tim', 99), Student('Bob', 88), 100, 'Hello'] #list不僅僅包含 Student 類 print sorted(L) #報錯
上述 Student 類實現了__cmp__()方法,__cmp__用實例自身self和傳入的實例 s 進行比較,如果self應該排在前面,就返回 -1,如果s應該排在前面,就返回1,如果兩者相當,返回 0。
3、__len__():返回元素的個數
class Students(object): def __init__(self, *args): self.names = args def __len__(self): return len(self.names) ss = Students('Bob', 'Alice', 'Tim') print len(ss) #3
4、四則運算:__add__(),__sub__(),__mul__(),__div__()
邏輯運算 __or__(),__and__()
def gcd(a, b): if b == 0: return a return gcd(b, a % b) class Rational(object): def __init__(self, p, q): self.p = p self.q = q def __add__(self, r): return Rational(self.p * r.q + self.q * r.p, self.q * r.q) def __sub__(self, r): return Rational(self.p * r.q - self.q * r.p, self.q * r.q) def __mul__(self, r): return Rational(self.p * r.p, self.q * r.q) def __div__(self, r): return Rational(self.p * r.q, self.q * r.p) def __str__(self): g = gcd(self.p, self.q) return '%s/%s' % (self.p / g, self.q / g) __repr__ = __str__ r1 = Rational(1, 2) r2 = Rational(1, 4) print r1 + r2 #3/4 print r1 - r2 #1/4 print r1 * r2 #1/8 print r1 / r2 #2/1
我們也許還有希望覆蓋“原位”操作, 比如, __iadd__()。這是用來支持像 mon += tue 這樣的操作符,並把正確的結果賦給 mon。重載一個__i*__()方法的唯一秘密是它必須返回 self。把下面的片斷加到我們例子中,以修復上面的 repr()問題,並支持增量賦值:
5、類型轉換:__int__(),__float__()
class Rational(object): def __init__(self, p, q): self.p = p self.q = q def __int__(self): return self.p // self.q def __float__(self): return float(self.p) / self.q print int(Rational(7, 2)) #3 print int(Rational(1, 3)) #0 print float(Rational(7, 2)) #3.5 print float(Rational(1, 3)) #0.333333333333
6、__slots__():限制當前類所能擁有的屬性,能節省內存
class Person(object): __slots__ = ('name', 'gender') def __init__(self, name, gender): self.name = name self.gender = gender p = Person('Bob', 'male') print p.name #Bob print p.gender #male print p.score #報錯 class Student(Person): __slots__ = ('score',) def __init__(self, name, gender, score): super(Student, self).__init__(name, gender) self.score = score s = Student('Bob', 'male', 59) print s.score # 59
__slots__定義的屬性僅對當前類起作用,對繼承的子類是不起作用的。除非在子類中也定義__slots__
7、__call__():
在Python中,所有的函數都是可調用對象。
一個類實例也可以變成一個可調用對象,只需要實現一個特殊方法__call__()。
class Person(object): def __init__(self, name, gender): self.name = name self.gender = gender def __call__(self, friend): print 'My name is %s...' % self.name print 'My friend is %s...' % friend p = Person('Bob', 'male') p('Tim') # My name is Bob... # My friend is Tim...
單看p('Tim')你無法確定p是一個函數還是一個類實例,所以,在Python中,函數也是對象,對象和函數的區別並不顯著。
(10)日期和時間
1、time包基於C語言的庫函數(library functions)。Python的解釋器通常是用C編寫的,Python的一些函數也會直接調用C語言的庫函數。
2、時間間隔是以秒為單位的浮點小數。
3、每個時間戳都以自從1970年1月1日午夜(歷元)經過了多長時間來表示。
import time; # 引入time模塊 ticks = time.time() print ticks
4、獲取當前時間
import time localtime = time.localtime(time.time()) print "本地時間為 :", localtime #本地時間為 : time.struct_time(tm_year=2017, tm_mon=2, tm_mday=13, tm_hour=22, tm_min=20, tm_sec=59, tm_wday=0, tm_yday=44, tm_isdst=0) #時間元組struct_time:年,月,日,小時,分鍾,秒,一周第幾天(0是周一),一年第幾天,夏令時
5、獲取格式化時間
最簡單的獲取可讀的時間模式的函數是asctime():
import time localtime = time.asctime( time.localtime(time.time()) ) print "本地時間為 :", localtime #本地時間為 : Mon Feb 13 22:23:57 2017
我們可以使用 time 模塊的 strftime 方法來格式化日期:
import time # 格式化成2016-03-20 11:45:39形式 print time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) #2017-02-13 22:25:08 # 格式化成Sat Mar 28 22:24:24 2016形式 print time.strftime("%a %b %d %H:%M:%S %Y", time.localtime()) #Mon Feb 13 22:25:08 2017 # 將格式字符串轉換為時間戳 a = "Sat Mar 28 22:24:24 2016" print time.mktime(time.strptime(a, "%a %b %d %H:%M:%S %Y")) #1459175064.0
python中時間日期格式化符號:
%y 兩位數的年份表示(00-99)
%Y 四位數的年份表示(000-9999)
%m 月份(01-12)
%d 月內中的一天(0-31)
%H 24小時制小時數(0-23)
%I 12小時制小時數(01-12)
%M 分鍾數(00=59)
%S 秒(00-59)
%a 本地簡化星期名稱
%A 本地完整星期名稱
%b 本地簡化的月份名稱
%B 本地完整的月份名稱
%j 年內的一天(001-366)
%U 一年中的星期數(00-53)星期天為星期的開始
%w 星期(0-6),星期天為星期的開始
%W 一年中的星期數(00-53)星期一為星期的開始
6、獲取某月日歷
Calendar模塊有很廣泛的方法用來處理年歷和月歷,例如打印某月的月歷:
import calendar cal = calendar.month(2017, 2) print "以下輸出2017年2月份的日歷:" print cal 以下輸出2017年2月份的日歷: # February 2017 # Mo Tu We Th Fr Sa Su # 1 2 3 4 5 # 6 7 8 9 10 11 12 # 13 14 15 16 17 18 19 # 20 21 22 23 24 25 26 # 27 28
7、time.sleep(secs):推遲調用線程的運行,secs指秒數。
import time print('start') time.sleep(3) # sleep for 3 seconds print('wake up')
8、datetime包
datetime包是基於time包的一個高級包, 為我們提供了多一層的便利。
datetime可以理解為date和time兩個組成部分。date是指年月日構成的日期(相當於日歷),time是指時分秒微秒構成的一天24小時中的具體時間(相當於手表)。
import datetime t = datetime.datetime(2017,2,13,21,30) print(t)#2017-02-13 21:30:00
datetime包還定義了時間間隔對象(timedelta)。一個時間點(datetime)加上一個時間間隔(timedelta)可以得到一個新的時間點(datetime)。同理,兩個時間點相減會得到一個時間間隔。
import datetime t = datetime.datetime(2017,2,13,21,30) t_next = datetime.datetime(2017,2,13,23,30) delta1 = datetime.timedelta(seconds = 600) delta2 = datetime.timedelta(weeks = 3) print(t + delta1) #2017-02-13 21:40:00 print(t + delta2) #2017-03-06 21:30:00 print(t_next - t) #2:00:00
9、其他模塊:pytz模塊、dateutil模塊
(11)數學與隨機數
1、math包主要處理數學相關的運算。math包定義了兩個常數:
math.e # 自然常數e
math.pi # 圓周率pi
2、math包運算
math.ceil(x) # 對x向上取整,比如x=1.2,返回2
math.floor(x) # 對x向下取整,比如x=1.2,返回1
math.pow(x,y) # 指數運算,得到x的y次方
math.log(x) # 對數,默認基底為e。可以使用base參數,來改變對數的基地。
math.sqrt(x) # 平方根
3、math包三角函數:
math.sin(x), math.cos(x), math.tan(x), math.asin(x), math.acos(x), math.atan(x)
這些函數都接收一個弧度(radian)為單位的x作為參數。
角度和弧度互換: math.degrees(x), math.radians(x)
4、random包
1) 隨機挑選和排序
random.choice(seq) # 從序列的元素中隨機挑選一個元素
比如random.choice(range(10)) #從0到9中隨機挑選一個整數。
random.sample(seq,k) # 從序列中隨機挑選k個元素
random.shuffle(seq) # 將序列的所有元素隨機排序
2)隨機生成實數
random.random() # 隨機生成下一個實數,它在[0,1)范圍內。
random.uniform(a,b) # 隨機生成下一個實數,它在[a,b]范圍內。
5、decimal 十進制浮點運算類 Decimal
(12)正則表達式
1、匹配字符串
str.startswith()和str.endswith()
一個句子結尾是\n來結束的,所以用endswith(‘’)方法匹配時要注意傳入的變量帶有\n
或者用切片操作,str[:-1].endswith()
def find(fname): f = open(fname,"r+") for line in f : if line.startswith("this")\ #一個句子太長時使用 \ 符號來換行 or line[:-1].endswith("apple"): print line find("test1.txt") # this is an apple # this is a pear # this is a banana # that is an apple
2、正則表達式概念
使用單個字符串來描述匹配一系列符合某個句法規則的字符串,是對字符串操作的一種邏輯公式,應用場景在處理文本和數據。
re 模塊使Python語言擁有全部的正則表達式功能。
3、導入re模塊 #import re
利用re.compile(正則表達式)返回pattern
利用pattern.match(待匹配字符串)返回match
match.group()返回子串
match.string()返回主串
match.span()返回子串在主串中的位置
import re str1 = "this is an apple" regex = re.compile(r"this") #r‘ ’內的字符不轉義 print regex #<_sre.SRE_Pattern object at 0x0000000001D2DDD8> ma = regex.match(str1) print ma #<_sre.SRE_Match object at 0x0000000002075510> print ma.group() #this print ma.span() #(0, 4) print ma.string #this is an apple print ma.re #=regex
4、re.match 嘗試從字符串的開始匹配一個模式。
re.match(pattern, string, flags=0)
pattern 匹配的正則表達式
string 要匹配的字符串
flags 標志位,用於控制正則表達式的匹配方式,如:是否區分大小寫, 多行匹配等等
匹配成功re.match方法返回一個匹配的對象,否則返回None。
5、group(num)或groups()匹配對象函數來獲取匹配表達式。
group(num=0)匹配的整個表達式的字符串,group()可以一次輸入多個組號,在這種情況下它將返回一個包含那些組所對應值的元組。
groups()返回一個包含所有小組字符串的元組。
6、基本匹配符
①. 匹配任意字符(除了\n),一個點只是一個字符
②[…] 匹配字符集 如[a-z][A-Z][0-9][a-zA-Z0-9]
③[^…] 匹配不在[]中的任意字符
④\d 匹配數字等價於[0-9]
⑤\D 匹配非數字
⑥\s 匹配空白,包括空格、制表符、換頁符等等。等價於[\f\n\r\t\v]
⑦\S 匹配非空白
⑧\w 匹配單詞字符,匹配包括下划線的任何單詞字符。等價於[A-Za-z0-9_]
⑨\W 匹配非單詞字符
import re ma = re.match(r'[\w]','0') print ma.group()#0 ma = re.match(r'\[[\w]\]','[0]') print ma.group()#[0]
7、特殊匹配符
①* 匹配前一個字符0次或無限次
②+ 匹配前一個字符1次或無限次
③? 匹配前一個字符0次或1次
④{m} 匹配前一個字符m次
⑤{m,n} 匹配前一個字符m到n次
⑥*?或+?或?? 匹配模式變為非貪婪(盡可能減少匹配字符)
import re ma = re.match(r'[a-zA-Z0-9]*','SAFGAFG') print ma.group() #SAFGAFG ma = re.match(r'[a-zA-Z0-9]{3,6}@163.com','SAFGFG@163.com') print ma.group() #SAFGFG@163.com ma = re.match(r'[0-9][a-z]*','1bc') print ma.group() #1bc ma = re.match(r'[0-9][a-z]*?','1bc') print ma.group() #1
8、高級匹配符
①^ 匹配字符串開頭
②$ 匹配字符串結尾
③\A 指定的字符串必須出現在開頭
④\Z 指定的字符串必須出現在結尾
⑤| 或,匹配左右任意一個表達式
⑥(ab) 括號中表達式作為一個分組
⑦\<number> 引用編號為num的分組匹配到的字符串
⑧(?P<name>) 分組起一個別名
⑨(?P=name) 引用別名為name的分組匹配字符串
import re ma = re.match(r'^[\w]{4,10}@163.com$',"abc123@163.com") print ma.group() #abc123@163.com ma = re.match(r'\Aabc[\w]*',"abc123") print ma.group() #abc123 ma = re.match(r'[1-9]?\d$',"99")#0-99 print ma.group() #99 ma = re.match(r'[1-9]?\d$|100',"100")#0-99 print ma.group() #100 ma = re.match(r'[\w]{4,6}@(163|126|qq).com',"abc123@qq.com") print ma.group() #abc123@qq.com ma = re.match(r'<([\w]+>)[\w]+</\1',"<book>python</book>") print ma.group() #<book>python</book> ma = re.match(r'<(?P<mark>[\w]+>)[\w]+</(?P=mark)',"<book>python</book>") print ma.group() #<book>python</book> ma = re.match(r'<(?P<ht>[\w]+>)<(?P<h>[\w]+>)<(?P<s>[\w]+>).+</(?P=s)</(?P=h)</(?P=ht)',"<html><head><script>javascript:alect('hello world')</script></head></html>") print ma.group() #<html><head><script>javascript:alect('hello world')</script></head></html> print ma.groups() #('html>', 'head>', 'script>')
9、其他方式
①search(pattern,string,flags=0)
會在字符串內查找模式匹配,直到找到第一個匹配
匹配成功re.search方法返回一個匹配的對象,否則返回None
②findall(pattern,string,flags=0)
找到匹配,返回所有匹配部分的列表
③sub(pattern,repl,string,count=0,flags=0)
將字符串中匹配正則表達式的部分替換為其他值
④split(pattern,string,maxsplit=0,flags=0)
根據匹配分割字符串,返回分割字符串組成的列表
import re str1 = "abc = 100" info = re.search(r'\d+',str1) print info.group() #100 str2 = "c++=100,java=90,python=80" info = re.search(r'\d+',str2) print info.group() #100 info = re.findall(r'\d+',str2) print info #['100', '90', '80'] print sum([int(x) for x in info]) #270 #列表解析 str3 = "python video = 1000" info = re.sub(r'\d+',"1001",str3) #sub是調用findall而不是search print info #python video = 1001 def add3(match): val = match.group() num = int(val)+3 return str(num) info = re.sub(r'\d+',add3,str3) print info #python video = 1001 str4 = "class:C C++ JAVA Python C#" info = re.split(r':| |,',str4) print info #['class', 'C', 'C++', 'JAVA', 'Python', 'C#']
10、修飾符
正則表達式可以包含一些可選標志修飾符來控制匹配的模式。修飾符被指定為一個可選的標志。多個標志可以通過按位 OR(|) 它們來指定。
①re.I 使匹配對大小寫不敏感
②re.M 多行匹配,影響 ^和$
③re.S 使.匹配包括換行在內的所有字符
11、抓取網頁中的圖片到本地
1:抓取網頁
2:獲取圖片地址
3:抓取圖片內容並保存到本地
import urllib2,re req = urllib2.urlopen("http://www.imooc.com/course/list") buf = req.read() listurl = re.findall(r'http:.+\.jpg',buf) i = 0 for url in listurl: f = open(str(i)+".jpg","wb") req = urllib2.urlopen(url) buf = req.read() f.write(buf) i+=1 print I #看看輸出了多少個,此時也生成i個.jpg