該系列文章繫個人讀書筆記及總結性內容,任何組織和個人不得轉載進行商業活動!
2018-05-07——2018-05-28
參考內容《Python tutorial 2.7.13》
實踐源碼 Python2.7.13 初識
#coding=utf-8
# Release 2.7.13
# Date 2017-02-13
# Python簡單易學 且功能強大
# 1.擁有高效的高級數據結構,能以簡單且高效的方式進行面向對象編程;
# 2.語法優雅、動態類型,結合它的解釋性,是大多數平臺或領域中編寫腳本和開發應用程序的理想語言;
# Python官方點 http:www.python.org,以源碼或二進制的形式提供Python解釋器及其標準擴展庫,並可以自由分發;
# 該站點也提供了大量的第三方Python模塊、程序、工具及其附加文檔;
# 可以使用C C++(其他可以通過C調用的語言)爲Python解釋器擴展函數和數據類型;
# Python還可以被用作定製應用程序的一門擴展語言
# Python 參考文檔:https://docs.python.org/2.7/library/index.html
# Python 參考文檔:https://docs.python.org/2.7/index.html
# Python 解釋器擴展和集成章節:https://docs.python.org/2.7/extending/index.html#extending-index
# C API參考手冊 https://docs.python.org/2.7/c-api/index.html#c-api-index
# 或其他進階書籍
# 本學習過程學完之後,能閱讀並編寫Pyhton程序,併爲以後使用《Python參考手冊》繼續學習Python模塊庫做準備;
#coding=utf-8
# Python可以快速的開發應用程序,實現擴展,在主流的操作系統上都可以幫助更快的完成任務;
# Python雖然簡單,卻是一門完整的語言:
# 1.爲編寫大型程序踢提供了更多的結構和支持;
# 2.作爲一門高級語言,內置支持高級的數據結構和類型;
# 3.Python允許將程序分割成不同的模塊,以便在其他Python程序中重用;
# 同時內置了大量的標準模塊;如 文件I/O、系統調用、Socket支持,甚至GUI用戶圖形界面工具包;
# Python是一門解釋型語言,因此無需編譯和鏈接;
# 使用Python編程更加緊湊和可讀:
# 1.高級數據結構使你可以在一條語句中表達複雜的操作;
# 2.語句組使用縮進代替開始和結束大括號組織;
# 3.變量和參數無需聲明;
# Python是可擴展的:
# 使用C語言可以輕而易舉地爲解釋器添加內置函數或模塊,或者對性能瓶頸進行優化;或者將Python程序與只有二進制形式的庫鏈接起來;
# -*- coding: utf-8 -*-
# Python一般安裝在目標機器的/usr/local/bin/python目錄下;以確保可以輸入python命令;
# windows下可以配置環境變量
# Python解釋器可以交互式的解釋並執行終端輸入的命令;當使用文件名參數或以文件作爲標準輸入調用時,它讀取文件並將文件作爲腳本執行;
# python -c commond [arg] ... :這種方式可以在命令行執行Python語句;爲了避免特殊字符衝突,一般將命令行用單引號包裹起來;
# python -m module [arg] ... :將一些Python模塊當做腳本使用;使用腳本文件時,經常會運行腳本然後進入交互模式,這也可以通過在腳本之
# 前加上-參數來實現;
# 調用解釋器時,腳本名和附加參數傳入一個名爲sys.argv的字符串列表;可以通過import sys來獲取此列表,列表長度>=1,無給定腳本和參數
# 時,argv[0]爲空字符串;
# 腳本名指定爲'-'(表示標準輸入)時,sys.argv[0]被指定爲'-';使用-c指令時,sys.argv[0]被指定爲'-c';
# 使用-m模塊參數時,sys.argv[0]被設定爲指定模塊的全名;-c指令或-m模塊之後的參數不會被Python解釋器的選項處理機制所截獲,而是留
# 在sys.argv中,供腳本命令操作;
# 從tty(命令行)讀取命令時,我們稱解釋器工作於 交互模式;
# 這種模式下它根據主提示符來執行,主提示符通常標識爲3個大於號>>>,繼續的部分成爲從屬提示符,由三個點標識(...);
# 輸入多行(縮進)時就需要從屬提示符了;
# 在Python源代碼使用非ASCII編碼,最好的方式是在#!行後邊增加一行特殊的註釋來定義源文件中的編碼(即便只是註釋也不可以,編碼方式必須支持
# 文件中所有字符的字體);
# 如果編輯器支持保存爲帶有UTF-8字節順序標記(也叫做BOM)的UTF-8格式的文件,則可以使用這種功能爲不必編碼聲明;
# IDLE如果設置了Default Source Encoding/UTF-8也可以;
# -*- coding: utf-8 -*-
# 關於註釋:以#開頭直至行尾,出現在代碼中的#僅作爲正常的#號;
SPAN = 1
STRING = "# This is not a comment."
# 由於之前已經練習過一部分Python,就不再在命令行中做相應的實踐,而是使用PCharm IDE;
print 2 + 2
print 50 - 5 * 6
print (50 - 5.0 * 6)/4
# 整數類型是int
# 帶有小數的是float,後續我們會學習到更多的數字類型;
# /法的返回類型取決於它的操作數,兩個int,採用floor division(可向下取整的除法)返回的也是一個int;float參與的返回的就是一個float;
# //運算符也是除法,用於floor division而無論操作數是什麼類型;
# 餘數使用%操作符計算;
print 17/3
print 17.0//3
print 17%3
# 可以使用**運算符計算冪乘方
print 5**2
# 變量在使用前必須"定義"(賦值),否則會出錯
n = 3
print n
# 浮點數與整數混合計算時,自動轉換爲浮點數
# 交互模式中,最近一個表達式的值賦給變量'_';這樣可以把它當做一個桌面計算器,用於連續計算;此變量對於用戶時只讀的;如果對他進行賦值的
# 話,只會創建一個獨立的同名局部變量;
# 除了int和float,Python還支持其他數字類型,如Decimal何Fraction;
# Python還內建支持複數,使用後綴j或J表示虛數部分
f = 5 + 3j
print f.real
print abs(f) #實部的平方 + 虛部的平方開根號
# Python支持幾種不同的方式表示字符串;
# 可以使用單引號('...')或雙引號("...");\可以用來轉義引號;
print "doesn't"
print 'doesn\'t'
# 如果你前面帶有的\字符被當做特殊字符,你可以使用原始字符,方法是在第一個引號前面加上一個r;
print 'C:\some\name'
print r'C:\some\name'
# 字符串文本能夠分成多行,一種方法就是使用3引號:"""..."""或者'''...''';
# 行尾換行符會被自動包含到字符串中,也可以在行尾加上\來避免這個行爲;
# 這裏\反斜槓表示下一行在邏輯上是本行的後續內容;
print """abc
def\
ghi
jkl"""
# 字符串可以由+操作符連接,用*表示重複
print 3*'UN' + 'China' 'is great country!'
# 相鄰的兩個字符串會自動連接在一起
# 如果是一段很長的字符串文本你可以這樣寫
print ('1awefjaiwjefiaw-'
'2awefahwuefhawuefhaw-'
'3awefhahwefhawuefhawu-')
# 值得注意的是 上邊說的這種相鄰的兩個字符串會自動連接的方式只能用於兩個字符串文本,不能用於字符串表達式,或者是字符串文本和變量之間;
# 如我們將3*'UN'加上一個括號就變成了一個表達式,此時上邊的這個例子就不可行了;
# 如果想連接多個變量或者是字符串文本,可以使用+
# 字符串也可以被截取(檢索);第一個字符索引爲0;
# Python沒有單獨的字符類型,一個字符就是一個簡單的長度爲1的字符串;
# 索引也可以是負數,這也將導致從右邊開始計算;注意-0就是0
word = "Python"
print word[0]
print word[-1]
# 除了索引,還支持切片;索引用於獲取單個字符,切片讓你獲得一個子字符串;
print word[0:2]
print word[:2]# 省略第一個索引值默認0
print word[2:]# 省略第二個索引值默認爲切片的字符串大小
print word[:2] + word[2:]
# 切片的範圍包含起始字符,不包含末尾字符;
# 理解索引和切片的工作方式:
# P y t h o n
# 0 1 2 3 4 5 6
# -6 -5 -4 -3 -2 -1
print word[-6:5]
print word[0:5]
# 試圖使用太大的索引會導致錯誤
# Python能夠優雅地處理那些沒有意義的切片索引:
# 切片中過大的索引值會被替換爲字符串的實際長度;
# 切片中上邊界>下邊界時返回空字符串;
# Python字符串是不可以被修改的,試圖賦值給字符串索引的位置會導致錯誤;
# 如果你需要一個不同的字符串,你應該創建一個新的;
# 內置函數len()返回字符串的長度
print len(word)
# 舊的字符串格式化操作,是在%操作符的左操作數時調用;
# 從Python2.0開始,可以使用Unicode對象存儲文本數據類型;用來存儲和維護Unicode數據;
# 與現有的字符串對象有良好的集成,必要時提供自動轉換
# Unicode的先進之處在於爲每一種現代或古代使用的文字系統中出現的每一個字符都提供了統一的序列號;
print "花 強"
print u"花 強"
# 引號前的'u'表示這會創建一個Unicode字符串;
# 和普通字符一樣,Unicode字符串也有原始字符;可以在引號前加'ur'
# 如果你需要大量輸入反斜槓,原始模式很有用,這在正則表達式中幾乎是必須的;
print str(u"abc")#字符串轉換
# print str(u"啊喔阿") # 會報錯 這幾個Unicode字符不在ASCII的編碼範圍
print u"啊喔阿"
print u"啊喔阿".encode('utf-8')
# 爲了將一個Unicode字符串轉換爲一個使用特定編碼的8位字符串,Unicode對象提供一個encode()方法,它接收編碼名作爲參數;編碼名應該小寫;
# 如果有一個其他編碼的數據,希望可以從中生成一個Unicode字符串,你可以使用unicode函數,它接收編碼名作爲第二個參數;
# Python中最爲通用的複合數據類型就是list(列表)
# 他可以寫作中括號之間的一列逗號分隔的值;值得注意的是 列表的元素不必是同一類型
squares = [1,2,3,4,5,6]
print squares
print ['a',"b",1,2]
# 就像字符串一樣,列表也可以被索引和切片:
print squares[0]
print squares[1:3]
print squares[-3:]
# 所有的切片操作都會返回一個包含請求的元素的新列表
# 列表也支持+連接這樣的操作:
print squares + ['h','e','l','l','o']
# 與字符串的不可變不一樣的是,列表允許修改元素
cubes = [2**0,2**1,2**2,2**3,2**4]
cubes[1] = 4
print cubes
# 可以使用append()方法,在列表的末尾添加新的元素
cubes.append(32)
print cubes
# 內置函數len()同樣適用於列表
print len(cubes)
# 列表允許嵌套
a_list = ['a','b','c']
b_list = [1,2,3]
n_list = [a_list,b_list]
print n_list[1][1]
# 生成一個菲波那切數列
a_ep,b_ep = 0,1
while b_ep < 10:
print b_ep
a_ep,b_ep = b_ep,a_ep + b_ep
# 和C語言中類似,0表示false(列表爲空也是false),非0爲true
# 縮進是Python的組織語法,Python不提供集成的行編輯功能;
# 字符串打印的時候不用引號包圍,每兩個子項之間插入空格;
print "hahaha",1
a_ep,b_ep = 0,1
while b_ep < 10:
print b_ep,
a_ep,b_ep = b_ep,a_ep + b_ep
# 用一個逗號結尾就可以禁止輸出換行
# 注意:
# **的優先級高於加減乘除等基本運算,所以請使用括號,避免歧義;
# \n在''和""中意義是一樣的,''和""的唯一區別在於''中你不需要轉義",但必須轉義\'
# -*- coding:utf-8 -*-
# 除了前面用過的while,Python還從其他語言借鑑了一些流程控制功能,但有所改變
# x = int(raw_input("Please enter an integer:"))
x = 5
if x > 0:
print x
elif x <= 0:
print 0
else:
print "default"
# 可能會有0-多個elif部分,else是可選的;
# 關鍵字elif是else if的縮寫,這個可以有效的避免過深的縮進
# if...elif...elif...序列用於代替其他語言中的switch或case語句
# Python中的for循環和其他語言略有不同;
# Python中的for語句依據任意序列(鏈表或字符串)中的子項,按它們在序列中的順序來進行迭代;
words =['cat','dog','elephont']
for w in words:
print w,len(w)
# 在迭代過程中修改序列是不安全的(只有在鏈表這樣的可變序列的遍歷過程中才會出現這樣的情況);
# 如果你要修改你迭代的序列,可以迭代它的副本;使用切割標識可以很方便的做到這一點;
for w in words[:]:
if len(w) > 4:
words.insert(0,w)
print words
# 這樣 我們通過對副本的判斷 實現了對原列表的修改
# 如果你需要一個數值序列,內置函數range()會很方便,他會生成一個等差級數列表:
print range(10)
# range(10)是一個包含10個值的鏈表,他用鏈表的索引值填充了這個長度爲10的列表;
# 所生成的鏈表中不包括範圍中的結束值
# 也可以讓range操作從另一個數值開始,到指定的數值;
# 或者可以指定一個不同的步進值;(步進值可以是負數)
print range(10,100,10)
# 我們可以這樣迭代列表的索引值
for i in range(len(words)):
print i,words[i]
# 不過這種場合更多見的是使用enumerate();
# break語句和C中類似,用於跳出最近的一級for或while循環;
# 循環也可以有一個else子句:它會在循環迭代完 整個列表(對於for)後執行;這種情況的else子句是屬於for循環的;
# 如果for中使用了break,則else不會執行;
# continue語句是從C語言中借鑑過來的,他表示循環繼續執行下一次迭代
for num in range(2,10):
if num % 2 == 0:
continue
print num,"even number!"
# pass語句什麼也不做,他用在那些語法上必須要有什麼的、但程序什麼也不做的場合;
# while True:
# pass
# 這通常用於創建最小結構的類
# class MyEmptyClass:
# pass
# 另一方面,pass可以在創建新代碼時用來做函數或控制體的佔位符;可以讓你在更抽象的級別上思考;pass可以默默地被忽視;
# def initlog(* args):
# pass
# 我們定義一個函數:用來生成任意上界的斐波那契數列
def fib(n):
"""Do Fibonacci series
para number of upfloor:n
"""
print "Print a Fibonacci series up to",n
a,b = 0,1
while a < n:
print a,#,號可以去掉換行
a,b = b,a+b
fib(10)
# 關鍵字def引入了一個函數定義:在其後必須跟着 函數名 和 包括形式參數的圓括號;
# 函數體從下一行開始,必須是縮進的;
# 函數體的第一行語句可以是可選的字符串文本——即函數的文檔字符串(docstring);
# 有些在線工具通過docstring可以自動生成在線的可打印的文檔;
# 函數調用會爲函數局部變量生成一個新的符號表;
# 確切的來說,所有函數中的變量賦值都是將值存儲在局部符號表,變量引用首先在局部符號表中查找,然後是包含函數的局部符號表;
# 然後是全局符號表,最後是內置名字表;
# 因此,全局變量不能在函數中直接賦值(除非用global語句命名),儘管他們可以被這樣用;
# 函數引用的實際參數在函數調用時引入局部符號表,因此,實參總是傳值調用(這裏的值總是一個對象的引用,而非對象的值);
# 一個函數被另一個函數調用時,一個新的餓局部符號表在調用過程中被創建
# 一個函數定義會在當前符號表內引用函數名;函數名指代的值(即函數體)存在一個被Python解釋器認定爲用戶自定義函數的類型;
# 這個值可以賦予其他的名字(即變量名),然後它也可以被當做函數使用;
f = fib
print f(10)
# 我們看到fib()函數並沒有return語句;
# 事實上,沒有return語句的函數確實會返回一個值,雖然是一個相當令人厭惡的值——None(內建的一個名稱);
# 我們使用print就可以看到這個值的輸出;
print f(0)
# 我們修改下之前的代碼,以返回一個包含斐波那契數列的值的鏈表,而不是打印他
def fibs(n):
"""Return a list containing the Fibonacci series up to N."""
result = []
a,b = 0,1
while a < n:
result.append(a)
a,b = b,a+b
return result
f100 = fibs(100)
print f100
# return語句從函數中返回一個值,不帶return則返回None,過程結束後也會返回None;
# append是鏈表對象result的一個方法;作用類似result = result + 【b】,不過效率更高;
# 在Python中,你可以定義包含若干參數的函數;
# 這裏有三種可用的形式,也可以混合使用
# 最常用的一種形式是爲一個或多個參數指定默認值;
# 這會創建一個可以使用比定義時允許的參數更少的參數調用的函數;
def ask_ok(prompt,retries = 4,complaint = 'Yes or No,please!'):
while True:
ok = raw_input(prompt)
if ok in ('y','ye','yes'):
return True
if ok in ('n','no','nop','nope'):
return False
retries = retries - 1
if retries < 0:
raise IOError('refuse input user')
print complaint
# ask_ok("Do you want to quit? ")
# ask_ok("Do you want to quit? ",2)
# ask_ok("Do you want to quit? ",2,"Come on,only yes or no!")
# 這裏介紹下in關鍵字,他測定序列中是否包含某個確定的值;
# 值得注意的是:默認值在函數定義作用域被解析(下面示例將打印5)
i= 5
def f_i(arg = i):
print arg
i = 6
f_i()
# 重要警告:
# 默認值只被賦值一次;這使得當默認值是可變對象時會有所不同;
# 如列表、字典、大多數類的實例等;
# 下面的這個函數在後續調用中會累計(前面)傳給他的參數;
def f_list(a,L = []):
L.append(a)
return L
f_list(1)
f_list(2)
f_list(3)
print f_list(4)
# 如果你不想在隨後的調用中共享默認值,你可以這樣修改
def f_list_n(a,L = None):
if L is None:
L = []
L.append(a)
return L
# 函數可以通過關鍵字參數的形式來調用,形如keyword = value;
def parrot(voltage,state = 'a stiff',action = 'voom',type = 'Norwegian blue!'):
print '1',voltage,'2',state,'3',action,'4',type
parrot(1000)
parrot(voltage=1000)
parrot('a','b','c')
parrot('a',state=1000)
# 因爲非默認值參數的存在,不可以不傳參數
# 調用時,在一個關鍵字參數後邊的入參也必須是關鍵參數的方式
# 在函數調用中,關鍵字的參數必須跟隨在位置參數的後面;
# 傳遞的所有關鍵字參數必須與參數接收的某個參數相匹配;他們的順序並不重要;
# 任何參數都不可以多次賦值;
# 引入一個形如 **name 的參數:
# 它接收一個字典,該字典包含了所有未出現在形式參數列表中的關鍵字參數;
# 這裏還會組合使用一個形如 *name的形式參數;
# 包含了所有沒有出現在形式參數列表中的參數值;
# *name必須在**name之前出現;
def cheeseshop(kind , *arguments,**keywordDic):
for arg in arguments:
print arg
print "-"*40
keys = sorted(keywordDic.keys())
for key in keys:
print key,':',keywordDic[key]
return
cheeseshop("Flower","a","b","c",k_a = "k_a_value",k_b = "k_b_value",k_c = "k_c_value")
# 這裏我們對字典keys進行了排序,使輸出按照key有序;
# 最後一個最不常用的選擇是讓函數調用可變個數的參數;
# 這些參數被包裝進一個元組(和Swift的語法如此相似!)
# 在這些可變參數之前,可能有0-多個普通的參數;
# 就是前面我們引入的形如*name形式的參數,對應的就是一個元組;(這裏不再舉例)
# 另一種相反的情況:當你要傳遞的參數已經是一個列表,但要調用的函數卻接受分開一個個的參數值;
# 這時候需要把已有的列表拆開來;
# 例如內建函數range()需要獨立的start,stop參數;你可以在調用函數時加一個*操作符來自動把參數列表拆開;
print list(range(3,6))
args = [3,6]
print range(*args)
# 以同樣的方式,可以使用**操作符分拆關鍵字參數爲字典;
def parrots(voltage,state = 'abc',action = 'def'):
print voltage,state,action
d = {"voltage":"v_voltage","state":"v_state","action":"v_action"}
parrots(**d)
# 函數式編程:通過lambda關鍵字,可以創建短小的匿名函數;
lambda a,b:a+b
# lambda形式可以用於任何需要的函數對象;
# 處於語法限制,他們只能有一個單獨的表達式;
# 語義上,他們只是普通函數定義中的一個語法技巧;
# 類似嵌套函數定義,lambda形式可以從外部作用域引用變量;
def make_incrementor(n):
return lambda x:x+n
f = make_incrementor(40)
print f(0)
print f(1)
print f(2)
# 使用lambda表達式除了能返回一個函數,還可以將一個函數最爲參數
pairs = [(1,"one"),(2,"two"),(3,"three"),]
pairs.sort(key=(lambda pair:pair[1]))#傳入的key是一個函數
# 這個函數的入參是排序過程中遍歷的每一個元組 返回的是這個元組的索引1位置的值
print pairs
# 這裏介紹下文檔字符串的概念和格式
def my_func():
"""第一行是關於對象用途的簡介,簡短期間,首字母大寫,./。結尾
第二行空出來
後續是一或多段描述對象的調用約定、邊界效應等"""
# Python解釋器不會從多行的文檔字符串中去除縮進,所以必要時需要自己清除;
# 第一行之後的第一個非空行決定了整個文檔的縮進格式;
# 1.使用4個空格縮進,而非Tab;
# 2.折行以確保不會超過79個字符;
# 3.使用空行分隔函數和類,以及函數中的大塊代碼;
# 4.使用文檔字符串
# 5.把空格放到操作符兩邊,以及逗號後面,但是括號裏側不加空格;a = f(1, 2) + g(3, 4)
# 6.統一函數和類的命名;
# 推薦類名使用駝峯命名;
# 函數和方法名用小寫_和_下劃線;
# 總是使用self作爲方法的一個參數;
# 7.不要使用花哨的編碼,默認情況utf-8,甚至普通的ASCII總是工作的最好;
# 同樣也不要使用非ASCII字符的標識符;
# -*- coding:utf-8 -*-
# 列表對象方法
list_a = ['a','b','c']
list_a.append(1)#添加一個元素至列表末尾
list_a.extend(['m','n'])#將一個列表中的數據添加到另一個列表中
list_a.insert(2,"flower")#在指定位置插入元素
list_a.remove("flower")#刪除鏈表中值爲"flower"的第一個元素,如果沒有這樣的元素,就會返回一個錯誤;
list_a.pop()#從鏈表的指定位置list.pop([i])刪除元素,並將其返回;如果沒有指定索引,默認返回最後一個元素;
# 元素隨機從鏈表中被刪除;
# 方法中i兩邊的[]表示這個參數是可選的,不是要你真的輸入一對方括號;
list_a.index('m')#返回鏈表中第一個值爲'm'元素的索引,如果沒有則會返回一個錯誤;
list_a.count('n')#返回'n'在鏈表中出現的次數;
list_a.sort()#對鏈表中的元素就地進行排序,參數可以指定自定義的排序方法
list_a.reverse()#就地倒排鏈表中元素
print list_a
# 像insert,remove或者sort這樣的修改列表的方法沒有打印返回值——他們的返回值是None;
# 在Python中對所有可變的數據類型這是統一的設計原則;
stack = [3,4,5]
stack.append(6)#入棧
stack.pop()#出棧
# 隊列有先進先出的特點,列表這樣用效率並不高;
# 相對來說從列表末尾添加和彈出很快;在頭部插入和彈出很慢,因爲爲了一個元素,要移動整個列表中的所有元素;
# 要實現隊列,使用collections.deque,他爲在首尾兩端快速插入和刪除而設計;
from collections import deque
queue = deque(['a','b','c'])
queue.append('Fl')
print queue
queue.popleft()
print queue
# 對於鏈表來講,有三個非常有用的內置函數:filter(),map()以及redece();(關聯Swift中的相關語法)
# filter(func,sequence):過濾
# 返回一個序列sequence,包含給定序列中所有調用傳入函數後返回值爲true的元素;
def f_test(x):
return x % 3 == 0 and x % 5 == 0
print filter(f_test,range(5,25))
# 上述示例會返回既能被3、也能被5整除的原序列中的元素所組成的序列;
# map(func,sequence):擴展
# 爲每一個元素依次調用func並將返回值組成一個鏈表返回;
def f_test_cube(x):
return x**2 * x
print map(f_test_cube,range(1,10))
def f_test_add(x, y):
return x + y
print map(f_test_add,range(8),range(8,16))
# 也可以傳入多個序列,函數也必須要有對應數量的參數;
# reduce(func,sequence):展平
# reduce返回一個單值,它是這樣的結構:首先以序列的前兩個元素調用函數func,再以返回值和第三個參數調用,依次執行下去;
# 計算1-10的整數之和
print reduce(f_test_add,range(1,11))
# 由於合計數值是一個通用的需求,早已有內置的sum(sequence)函數,很好用;
# 列表推導式爲從序列中創建列表提供了一個簡單的方法
# 舉個簡單的例子
squares = [x**2 for x in range(10)]
print 'squares',squares
# 這樣我們就很簡潔的創建了一個列表,否則的話 我們可能需要for循環,append元素;
# 我們通過lambda表達式也可以實現,但上邊的方式更簡潔
squares_1 = map(lambda x:x**2,range(10))
print 'squares_1',squares_1
# 再舉一個 使用列表推導式 生成一個元組列表的例子
print [(x,y) for x in range(5) for y in range(4) if x != y]
# 值得注意的是上面兩個方法中的for和if語句的順序;因爲要得到元素所以需要對x,y加上括號;
# 列表推導式可使用複雜的表達式和嵌套函數
from math import pi
print [str(round(pi,i)) for i in range(1,6)]
# 列表推導式可以嵌套
matris = [
[1,2,3,4],
[5,6,7,8],
[9,10,11,12]
]
print matris
# 現在想交換行和列,就可以通過嵌套的列表推導式
print [[row[i] for row in matris] for i in range(4)]
# 轉換成我們傳統的多層for循環的話 最內層的for循環 對應 列表推導式最近的for循環;
# 實際中,你應該更喜歡使用內置函數組成複雜流程語句;對此情況zip()函數將會做的更好;
# 內置的zip()函數實現了我們之前說的交換行列的需求;
print list(zip(*matris))
# del語句:可以從列表中按照指定的索引而不是值來刪除一個子項;
# 語句del可以從列表中刪除切片或清空整個列表
list_a = [1,2,3,4,5,6,7,8]
del list_a[0]
print list_a
del list_a[1:4]
print list_a
del list_a[:]
print list_a
# del也可以刪除整個變量
del list_a
# 知道再次賦值,否則引用list_a將會報錯,提示not defined;後續我們還會看見del的其他用法;
# 我們知道鏈表和字符串有很多通用的屬性,例如索引和切割操作——它們都是序列類型
# Sequence Type-str,unicode,list,tuple,bytearray,buffer,xrange
# 這裏先介紹下其中的一個標準數據類型:元組;
# 元組輸出的時候總是有括號,以表達正確的嵌套結構;輸入時經常括號也是必須的;
# 元組和鏈表不同的是,元組是不可變的;
# 元組通常包含不同類型的元素,列表的元素通常是相同類型的;
# 一個空的括號可以創建一個空元組;
# 要創建一個單元素 元組,可以在值後面跟一個逗號(在括號中的單獨一個值不夠明確,不可以這樣做);
empty = ()
tuple_1 = ('tuple')
tuple_2 = 'tuple',
tuple_3 = 'tuple1','tuple2','tuple3'
print len(empty)
print len(tuple_1)#這就不是一個元組!!!
print len(tuple_2)
print len(tuple_3)
# 可以通過tuple_3賦值的逆操作將元組的值取出
x_t_1, x_t_2, x_t_3 = tuple_3
print x_t_1,x_t_2,x_t_3
# 實際上,這個調用的等號右邊可以是任何線性序列——稱之爲 序列拆封 最爲恰當;
# 序列拆封 要求左側的變量數目與序列的元素個數相同;
# 值得注意的是,可變參數其實就是元組封裝和序列拆封的一個結合;
# Python還包含了一個數據類型——集合(set)
# 集合是一個無需不重複元素的集;基本功能包括關係測試和消除重複元素;
# 集合對象還支持union(聯合),intersection(交),different(差)和sysmmetric difference(對稱差集)等數學運算;
# 大括號或set()函數可以用來創建集合;
# 值得注意的是要想創建空的集合必須使用set();{}是用來創建空字典的;
basket = ['apple','orange','apple','pear']
fruit = set(basket)
print fruit
print 'apple' in fruit
set_a = set('abcde')
set_b = set('cdefg')
print set_a - set_b
print set_a | set_b
print set_a & set_b
print set_a ^ set_b
# 類似列表推導式,這裏有一種集合推導式語法;
print {x for x in 'abcde' if x not in 'abc'}
# 字典:也是一個Python非常有用的內建數據類型;
# 字典在某些語言中可能稱爲聯合內存(associative memories)或聯合數組(associative arrays);
# 序列是以連續的整數作爲索引,與此不同的是,字典以關鍵字爲索引,關鍵字可以是任何不可變類型,通常用字符串或數值;
# 如果元組中只包含字符串和數字,他也可以作爲關鍵字,如果它直接或間接的包含了可變對象,就不能當做關鍵字;
# 不能有鏈表做關鍵字,因爲鏈表可以改變;
# 理解字典的最佳方式是把它看做無序的鍵:值對集合(key:value);
# key不相同,使用{}創建空字典;
# 初始化鏈表時,在大括號內放置一組逗號分割的 鍵:值 對,這也是字典的輸出的方法;
# 字典的主要操作是依據鍵來存儲和析取值;也可以用del來刪除鍵:值對;
# 對一個字典執行keys()將返回一個字典中所有關鍵字組成的無序列表,可以再使用sorted()排序;
# 使用in關鍵字可以檢查字典中是否存在某個關鍵字;
dic_tel= {'a':11,'b':12}
dic_tel['c'] = 13
print dic_tel
del dic_tel['a']
print dic_tel
print 'b' in dic_tel
# dict()構造函數可以直接從key-value對(元組s)中創建字典:
print dict([('A',65),('B',66)])
# 字典推導式可以從任意的鍵值表達式中創建字典
print {x:x**2 for x in (2,4,6)}
# 如果關鍵字都是簡單的字符串,有時通過 關鍵字參數 指定key-value更方便
print dict(sap = 1, sbp = 2, scp = 3)
# 在序列中循環時,索引位置和對應值可以使用enumerate()函數同時得到
for i, v in enumerate(['a','b','c']):
print i,v
# 同時循環兩個或更多序列時,可以使用zip()整體打包
questions = ['name','quest','answer']
answers = ['labd','scape','blue']
for q, a in zip(questions, answers):
print q, a
print 'question is {0},answer is {1}'.format(q, a)
# 需要逆向循環序列的話,先正向定位序列,然後調用reversed()函數;
for i in reversed(range(1,10,2)):
print (i)
# 要按排序後的順序循環序列的話,使用sorted()函數,它不改動原序列,而是生成一個新的已排序的序列;
for f in sorted(set(basket)):
print f
# 變量字典時,使用iteritems()方法同時得到鍵和對應的值;
knights = {'a':'white','b':'blue'}
for k, v in knights.iteritems():
print k, v
# 如果需要在循環內部修改正在遍歷的序列,可以使用切片製作副本
words = ['cat','dog']
for w in words[:]:
words.insert(0,w)
print words
# while和if語句中使用的條件不僅可以使用比較,而且可以包含任意的操作;
# 比較操作符in和not in用來判斷值是否在一個區間之內;
# 操作符is和is not 比較兩個對象是否相同;這隻和諸如鏈表這樣的可變對象有關;
# 所有的比較操作符具有相同的優先級,低於所有的數值操作;
# 比較操作可以傳遞,例如a < b == c 判斷的是a<b並且b==c
# 比較操作可以通過邏輯操作符and和or組合;比較的結果可以用not取反義;not優先級高於or;
# 邏輯運算符and和or也被稱爲短路運算符:從左向右解析,一旦確定結果將停止;
# 可以把比較或其他邏輯表達式的返回值賦給一個變量;
string1, string2, string3 = '','T','H'
non_null = string1 or string2 or string3
print non_null# T
# 值得注意的是 Python和C不同,在表達式內部不能賦值;這也避免了使用==誤用=的問題;
# 序列對象可以與相同類型的其他對象進行比較;
# 比較是按照字典序進行;如果兩個元素本身就是同樣類型的序列,就遞歸字典序比較;
# 如果兩個序列的所有子項都相等,就認爲序列相等;
print (1,2) < (1,2,3)
print 'ABC' < 'C' < 'P'
print (1,2) < (1,3)
print (1) < (1,-1)
print (1,2,('a'),4) < (1,2,('b'))
# 比較不同類型的對象雖然合法 但是不合理
# -*- coding:utf-8 -*-
# 在我們編寫大程序時,爲準備解釋器輸入使用一個文本編輯器會更好;
# 並以那個文件代替輸入執行——這就是傳說中的腳本;
# 我們可已將程序分割,更加利於維護;
# 爲此,Python提供了一個方法可以從文件中獲取定義,在腳本或解釋器的一個交互式實例中使用;
# 這樣的文件被稱爲——模塊;
# 模塊中的定義可以導入到另一個模塊或主模塊中;
# 模塊是包括Python定義和聲明的文件;文件名就是模塊名加上.py後綴;
# 模塊的模塊名可以由全局變量 __name__ 得到;
import Py2_6_fibo
# 我們創建了一個模塊,使用import命令導入到當前模塊;
# 這樣做不會直接把fibo中的函數導入當前的語義表;他只是引入了模塊名fibo;
# 你可以通過模塊名按如下方式訪問這個函數;
print Py2_6_fibo.fib2(1000)
print Py2_6_fibo.__name__
# 如果打算頻繁的使用一個函數,可以將它賦值給一個本地變量
fibo = Py2_6_fibo.fib2
print fibo(100)
# 除了包含函數定義外,模塊也可以包含可執行語句;
# 這些語句一般用來初始化模塊;他們僅在第一次被導入的地方執行一次;
# 每個模塊都有自己的私有的符號表,被模塊內所有的函數定義作爲全局符號表使用;
# 因此,模塊的作者可以在模塊內部使用全局變量,而無需擔心他與某個用戶的全局變量意外衝突;
# 模塊可以導入其他模塊,一個好的習慣是將所有的import語句放在模塊的開始(或者是腳本);這並非強制;
# 被導入的模塊名會放入當前模塊的全局符號表中;
# import語句的一個變體直接從被導入的模塊中導入命名到本模塊的語義表中;
#Py2_6_fibo.py(如下)
# -*- coding:utf-8 -*-
def fib(n):
a, b = 0, 1
while b < n:
print b
a, b = b, a + b
def fib2(n):
result = []
a, b = 0, 1
while b < n:
result.append(b)
a, b = b, a + b
return result
# if __name__ == "__main__":
# import sys
# fib(int(sys.argv[1]))
from Py2_6_fibo import fib, fib2
fib(10)
# 甚至可以使用 import * 導入from模塊中的所有定義(這樣可以導入所有除了以下劃線_開頭的命名);
# 這樣做的好處是 不會從局部語義表中導入模塊名;
# 並不推薦使用import *的方式,因爲這樣會使代碼比較難讀;
# 出於性能考慮,每個模塊在每個解釋器會話中只導入一遍;因此,如果你修改了你的模塊,需要重啓解釋器或者
# 如果想交互式的測試一個模塊,可以用reload()重新加載;
reload(Py2_6_fibo)
# 我們在模塊後添加代碼如下
# if __name__ == "__main__":
# import sys
# fib(int(sys.argv[1]))
# 並以如下方式運行該Python模塊,模塊中的代碼就會運行
# python Py2_6_fibo.py 50
# 此時,模塊會被作爲'main'文件執行,__name__也被這是成"__main__";
# 如果模塊使用import被導入,這段代碼就不會執行了;
# 這通常爲模塊提供一個便於測試的用戶接口(將模塊作爲腳本執行測試需求);
# 導入一個叫spam的模塊時,解釋器先在當前目錄中搜索名爲spam.py的文件;
# 如果沒有,會接着到sys.path變量中給出的目錄列表中查找;
# sys.path初始值來自如下:
# 1.輸出腳本的目錄(當前目錄);
# 2.環境變量PYTHONPATH表示的目錄列表中搜索;(PATH就是一系列目錄名的列表)
# 3.Python默認的安裝路徑中搜索;
# 需要注意的是,由於這些目錄中包含有搜索路徑中運行的腳本,所以這些腳本不應該和標準模塊重名;
# 否則在導入模塊時Python會嘗試把這些腳本當做模塊來加載,這通常會引發錯誤;
# 對於大量引入標準模塊的短程序,有一個提高啓動速度的重要方法:
# 在spam.py所在的目錄下存在一個名爲spam.pyc的文件,他會被視爲spam模塊的預編譯(二進制編譯)版本;
# 用於創建spam.pyc的這一版spam.py的修改時間記錄在spam.pyc文件中,如果兩者不匹配,.pyc文件就會被忽略;
# C中的靜態庫就是這個套路,很多編程語言都有類似的策略;
# 通常不需要爲創建.pyc文件做什麼;一旦spam.py文件編譯成功,就會嘗試生成對應版本的.pyc文件;
# spam.pyc文件的內容是平臺獨立的,所以Python模塊目錄可以在不同架構的機器之間共享;
# 部分高級技巧:
# 1.以-O參數調用Python解釋器時,會編譯生成優化代碼並保存在.pyo文件中;(刪除斷言assert)
# 2.向Python解釋器傳遞兩個-O(即-OO)會執行完全優化的二進制優化編譯,這偶爾也會生成錯誤的程序;(刪除__doc__造成的)
# 生成的是更加緊湊的.pyo文件;
# 3.來自.pyo或.pyc文件中的程序不會比來自.py文件的運行更快;只是加載的時候更快一點;
# 4.通過腳本名在命令行中運行腳本時,不會將爲該腳本創建二進制代碼寫入.pyc或.pyo文件;
# 當然,把腳本的主要代碼移進一個模塊裏,然後用一個小的啓動腳本導入這個模塊,就可以提高腳本的啓動速度;
# 也可以直接在命令行中指定一個.pyc或.pyo文件;
# 5.對於同一個模塊,可以只有.pyc/.pyo文件,而沒有.py文件,這樣可以打包發佈比較難於逆向工程的Python代碼;
# 6.complieall模塊可以爲指定目錄中的所有模塊創建.pyc文件(或者使用-O參數創建.pyo文件);
# Python帶有一個標準模塊庫,併發布有獨立文檔——Python庫參考手冊;
# 有一些模塊內置於解釋器之中。這些操作的訪問接口不是語言內核的一部分,但是已經內置於解釋器了;
# 這既提高了效率,也是給系統調用等操作系統原生訪問提供接口;
# 這類模塊集合是一個依賴底層平臺的配置選型;
# 如:winreg模塊只在windows系統上纔有;sys模塊內置於所有的Pyhton解釋器;
# 變量sys.ps1 和sys.ps2定義了主提示符和輔助提示符字符串
import sys
# print sys.ps1
# print sys.ps2
# 這兩個變量只有在解釋器的交互模式下才有意義;
# 變量sys.path是解釋器模塊搜索路徑的字符串列表;它由環境變量PYTHONPATH初始化,可以用標準的列表操作來修改:
# print sys.path.append('/test')
print sys.path
# 內置函數dir()用於按模塊名搜索模塊定義,他返回一個排好序的字符串類型的存儲列表
print dir(Py2_6_fibo)
print dir()
# 無參數調用時,dir()函數返回當前定義的命名列表;
# 注意該列表列出了所有類型的名稱:變量,模塊,函數,等等;
# dir()不會列出內置函數和變量名;如果需要則需使用標準模塊__builtin__;
import __builtin__
print dir(__builtin__)
# 包 通常是使用"圓點模塊名"的結構化模塊命名空間;
# 例如A.B模塊表示 包A中的子模塊B;
# 使用不同的類庫架構(包)可以避免模塊之間的命名衝突;
# 可能的包結構舉例
print '''
sound/
__init__.py
formats/
__init__.py
wavred.py
wavwrite.py
effects/
__init__.py
echo.py
surround.py
filters/
__init__.py
vocoder.py
equalizer.py
'''
# 當導入這個包時,Python會通過sys.path搜索路徑查找包含這個包的子目錄;
# 爲了讓Python將目錄當做內容包,目錄中必須包含__init__.py文件;
# 這是爲了避免一個含有爛俗名字的目錄無意中隱藏了稍後在模塊搜索路徑中出現的有效模塊;
# 最簡單的,只需要一個空的__init__文件即可;當然他也可以執行包的初始化代碼,或者定義稍後介紹的__all__變量;
# 用戶可以每次只導入包裏的特定模塊;
# import sound.formats.wavred
# 這樣導入的是 sound.formats.wavred子模塊,調用也需要使用sound.formats.wavred;
# 另一種可選的方式:
# from sound.formats import wavred
# 這樣加載的是 wavred 子模塊,並且可以在沒有包前綴的情況下也可以使用;
# 值得注意的是:使用 from package import item 導入時;子項 item可以是一個子模塊、包;
# 也可以是包中定義的其它命名,像函數、類或變量;
# import語句首先覈對是否包中有這樣一個子項,如果沒有,就假定它是一個模塊,並嘗試加載它;
# 如果沒有找到它,會引發一個ImportError異常;
# 而使用import sound.formats.wavred這樣的語法,前面的子項必須是包,最後的子項可以是包或模塊,但不能是前面子項中定義的類、函數或變量;
# from package import *
# 這種希望在文件系統中找出包中所有的子模塊,然後倒入它們;
# 這可能會花掉很長時間,並且出現期待之外的邊界效應;
# 執行from package import *時,如果包中的__init__.py代碼定義了名爲__all__的列表;
# 就會按照列表中給出的模塊名進行導入;
# __all__ = ["wavred","wavwrite"]
# 並不建議使用import *的方式;
# from Package import specific_submodules,除非導入的模塊需要使用其他包中的同名子模塊,否則這是推薦的寫法;
# 如果包中使用了子包結構,可以按照絕對位置從相鄰的包中引入子模塊;
# 如sound.filter.vocoder 中需要使用sound.effects包中的echo模塊,則可以:
# from sound.effects import echo
# 還可以用.號標明關聯上級包;
# from . import echo #當前包 導入其他模塊
# from .. import formats #上級包 導入其他包
# from ..filters import equalizer #上級包 子包 導入 子包中的模塊
# 需要注意的是顯示或隱式相對位置導入都是基於當前模塊的命名;
# 因爲主模塊的名字總是"__main__",Python應用程序的主模塊應該總是用絕對導入;
# 包支持一個特殊的特性:__path__;
# 在包中的__init__.py文件代碼執行之前,該變量初始化一個目錄名列表;
# 該變量可以修改,作用於包中的子包和模塊的搜索功能;
# 這個功能可以用於擴展包中的模塊集,不過它不常用;
# -*- coding:utf-8 -*-
# 一個程序的輸出方式可能是人類可讀的方式打印,或者寫入一個文件供以後使用,我們來討論幾種可能性
# 先說兩種大相徑庭的輸出值方法:表達式語句和print語句;
# 第三種方法是使用文件對象的write()方法,標準文件輸出可以參考sys.stdout
# 格式化輸出方式:
# 1.自己處理整個字符串,通過使用字符串切割和連接操作可以創建任何你想要的輸出形式;
# string類型包含一些將字符串填充到指定列寬度的有用操作,後續討論;
# 2.使用str.format()方法;
# Python有辦法將任意值轉爲字符串:將它傳入repr()或str()函數;
# 函數str()用於將值轉化爲適合人閱讀的形式;
# repr()轉化爲供閱讀器讀取的形式(如果沒有等價語法,則會發生SyntaxError異常);
# 某對象沒有適於人閱讀的解釋形式的話,兩者會返回等同的值;
# 很多類型,諸如 數值 鏈表 字典 這樣的結構,針對各函數都有統一的解讀方式;
# 字符串和浮點數,有着獨特的解讀方式;
s = 'Hello world!\n'
print str(s)
print repr(s)
print str(1.0/7)
print repr(1.0/7)
print repr((1,3,('a','b')))#repr的參數可以是任何Python對象
# 寫平方和立方表
for x in range(1,11):
print repr(x).rjust(2),repr(x*x).rjust(3),
print repr(x*x*x).rjust(4)#行佔4位 ,號間隔的代碼輸出是間隔空格
for x in range(1,11):
print '{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x)#行佔2、3、4位 間隔空格輸出
# str.rjust()方法可以把字符串輸出到1列,並通過向左側填充空格來使其右對齊;
# 類似的方法還有str.ljust()和str.center();
# 這些函數並不改變字符串 而是返回新的 且全顯示(格式可能會有錯亂)
# 如果想截斷顯示可以使用【:】切片;
# 還有一個方法,str.zfill()它用於向數值的字符串表達式左側填充0;
# 該函數可以正確理解正負號;
print '12'.zfill(5)
print '-3.14'.zfill(6)
# 方法str.format()的基本用法如下:
print 'We war falling in {}!'.format('love')
# 大括號和其中的字符會被替換成傳入str.format()的參數;
# 大括號中的數值可以指明使用傳入參數對象中的哪一個;
print '{1} and {0}'.format('dog','cat')
print '{0} and {1}'.format('dog','cat')
# 如果在str.format()調用時使用關鍵字參數,可以通過參數名來引用值;
print 'This {food} is {adjective}'.format(food = 'span',adjective = 'delicius')
# 且,定位和關鍵字參數可以組合使用
# '!s'表示應用str()
# '!r'表示應用repr()
# 他們可以在格式化之前轉換值
import math
print 'The value of PI is approxumately {}.'.format(math.pi)
print 'The value of PI is approxumately {!r}.'.format(math.pi)
# 字段名後允許可選的':'和格式指令;
# 這允許對值的格式化加以更深入的控制;
print 'The value of PI is approxumately {:.3f}.'.format(math.pi)
print 'The value of PI is approxumately {0:.2f}.'.format(math.pi)
# 我們還可以格式化字段,通過key操作value
table = {'a':123,'b':456,'c':789}
print 'table is a = {0[a]:d}, b = {0[b]:f}, c = {0[c]}'.format(table)
# 也可以用**標誌將這個字典以關鍵字參數的方式傳入
print 'table1 is a = {a:d}, b = {b:.2f}, c = {c}'.format(**table)
# 這種方式與新的內置函數vars()組合使用非常有效;
# 該函數返回包含所有局部變量的字典
# 操作符%也可以用於字符串格式化;
print '%5.3f %5.2f'%(math.pi, math.pi)
# 函數open()返回文件對象,通常的用法需要兩個參數:
# open(filename,mode)
f = open('workfile','r+')
print f
# 第一個參數是一個標識文件名的字符串;
# 第二個參數是由有限個字母組成的字符串,描述了文件將會被如何使用;
# 可選的模式有
# 'r'——使文件只讀(缺省,默認爲此模式)
# 'w'——使文件只寫,對於同名文件,該操作使原文件被覆蓋
# 'a'——以追加方式打開文件
# 'r+'——以讀寫方式打開文件
# 在windows平臺上,'b'模式以二進制方式打開文件,類似的可能會有'rb','wb','r+b'等組合模式;
# windows平臺上文本文件與二進制文件是由區別的,讀寫文本文件時,行尾會自動添加行結束符;
# 這種後臺操作方式對於ASCII文本文件時沒有問題的,但對於JPEG或EXE這樣的二進制文件就會產生破壞;
# 這些操作時一定要記得以二進制模式打開;(在linux上亦然)
# 讀取內容:f.read(size)
# 該方法讀取若干數量的數據並以字符串形式返回其內容;size是可選數值,指定字符串長度;
# 如果沒有指定size或設置爲負數,就會讀取並返回整個文件;(當超出機器內存的兩倍時,就會出問題)
# 到了文件末尾會返回一個空字符串("");
print f.read(2)
# 讀取單獨一行:f.readline()
# 從文件中讀取單獨一行,字符串結尾會自動加上一個換行符(\n)
# 只有當文件最後一行沒有以換行符結尾時,這一操作纔會被忽略;這樣返回值就不會混淆
# 到達文件末尾,readline()會返回空串;
# 返回空行的話,就是'\n',一個只包含換行符的字符串;
print f.readline()
print f.readline()
# f.readlines(sizehint)返回一個列表,其中包含了文件中所有的數據行;
print f.readlines()
# 這個功能通常用於高效讀取大型行文件;避免了將整個文件讀入內存;(這種操作只返回完整的行)
# 一種替代的方式是 通過遍歷文件對象來讀取文件行;
# 這是一種內存高效、快捷的方式;
f.close()
f1 = open('workfile1','r+')
for line in f1:
print (line)
# 雖然這種代替方式更簡單,但不具備細節控制能力;
# 兩種方式的緩存方式不同,別搞混;
# 寫入:f.write(string)
# 將字符串寫入文件
# print f1.write('flower\n')
# 要想寫入非字符串內容,首先需要將它轉換爲字符串
value = ('the answer',42)
s = str(value)
# f1.write(s)#('the answer', 42)
# f.tell()返回一個整數,代表文件對象在文件中的指針位置;
# 該值計量了自文件開頭到指針處的比特數;
# 改變文件對象指針:f.seek(offset,from_what)
# 指針在該操作中從指定的引用位置移動offset比特;
# from_what = 0表示自文件起始處開始;
# 1表示自當前文件指針位置開始;
# 2表示自文件末尾開始;
# from_what可忽略,默認從文件頭開始,值爲0
f1.close()
f2 = open('workfile2','r+')
# f2.write('0123456789abcdef')
f2.seek(5)
print f2.read(1)
f2.close()
# 在文本文件中(那些沒有使用b模式選項打開的文件);
# 只允許使用從文件頭開始計算相對位置,使用seek(-3,2)會報異常;
# 當使用完一個文件時,使用f.close()方法可以關閉文件,並釋放其佔有的全部資源;
# 在調用f.close()之後,試圖再次使用文件會自動失敗;
# 使用關鍵字with處理文件對象是個好習慣;
# 它的先進之處在於文件用完之後會自動關閉;發生異常也沒關係 相當於try-finally塊的簡寫
# with open('/tmp/workfile','r') as f3:
# read_data = f3.read()
# f3.close()
# Python允許使用常用的數據交換格式JSON(JavaScript Object Notation);
# 標準模塊json可以接受Python數據結構,並轉換爲字符串表示形式——此過程成爲 序列化;
# 從字符串表示形式重新構建數據結構——反序列化;
# 序列化和反序列化的過程:
# 表示該對象的字符串可以存儲在文件或數據中,也可以通過網絡連接傳送給遠程的機器;
# 對於一個對象x,你可以這樣查看他的JSON字符串形式
import json
x = {'name':'foot'}
x_s = json.dumps(x)
print x_s
# dumps()函數的另一個變體是dump():直接將一個對象序列化到一個文件
json.dump(x,open('workfile4','w'))#文件中有了對應的json字符串
# 重新解碼對象
x_o = json.loads(x_s)#從字符串中讀取
print x_o
# 如果f是爲讀取而打開的文件對象
x_o = json.load(open('workfile4','r'))#從json文件中讀取
print x_o,type(x_o)
# 這種簡單的序列化技術可以處理列表和字典
# 但序列化任意類實例爲json需要一點額外的努力;
# json模塊的手冊對此有詳細的描述
# pickle - pickle模塊
# 與JSON不同,pickle是一個協議,它允許任意複雜Python對象的序列化;
# 因此,他只能用於Pyhton而不能用來與其他語言編寫的應用程序進行通信;
# 默認情況下它是不安全的,如果數據由熟練的攻擊者精心設計,反序列化來自一個不受信任源的pickle數據可以執行任意代碼;
# -*- coding:utf-8 -*-
# Python中有兩種錯誤:語法錯誤和異常(syntax errors 和 exceptions)
# 也叫做解析錯誤;
# 語法分析器指出錯誤行,並且在檢測到錯誤的位置前面顯示一個小箭頭;錯誤一般就在箭頭標識的前面;
# 錯誤會返回文件名和行號;
# 運行期檢測到的錯誤稱爲 異常;並且程序不會無條件的崩潰;
# 大多數異常都不會被程序處理,我們需要捕捉和處理他們;
# 異常信息通暢作爲錯誤信息的一部分顯示出來;
# 如 除錯誤ZeroDivisionError、命名錯誤NameError、異常錯誤TypeError;
# 打印錯誤信息時,異常的類型作爲異常的內置名顯示;
# 內置異常均是如此,用戶自定義的異常則不一定;
# 標準異常名是內置的標識(沒有保留關鍵字);
# 異常的描述依賴於異常類型
# 通過編程處理選擇的異常是可行的;
# 舉一個例子:我們循環讓用戶輸入一個數值,直到輸入內容合法,或中斷程序;
# 注意:用戶產生的中斷會引發一個KeyboardInterrupt異常;
while True:
try:
# x = int(raw_input('Please input a number :'))
break
except ValueError:
print "Try again..."
# try語句的工作方式
# 1.執行try子句;
# 2.若無異常,except子句在try子句執行完之後被忽略;
# 3.如果try子句發生異常,那麼該子句的其餘部分就會被忽略;
# 4.如果匹配於except關鍵字後面指定的異常類型,則執行except子句;
# 然後繼續執行try-except語句之後的代碼;
# 5.如果發生了一個無匹配的異常,則會上傳至上一級try語句中;
# 如果最終找不到,會成爲一個未處理異常,終止程序,顯示提示信息;
# 一個try語句可能包含多個except子句;
# 至多隻有一個分支被執行
# 異常處理程序只會處理對應的try子句中發生的異常;
# 一個except子句可以在括號中列出多個異常的名字
import sys
try:
print 'normal'
except (ValueError,KeyboardInterrupt):
print 'error'
except:
print "Unexpected error:",sys.exc_info()[0]#打印異常信息
raise #產生異常 後邊可跟異常名
else:
print 'else normal'
# 注意此元組的括號是必須的;
# 最後一個except子句可以省略異常名稱,以作爲通配符使用;
# 慎用此方法,因爲他會輕易隱藏一個實際的錯誤;
# 可以使用它打印一個錯誤信息,然後重新拋出異常
# try...except語句可以帶一個else子句,該子句只能出現在except子句之後;
# 當try沒有拋出異常時,需要執行一些代碼 可以使用這個子句
for arg in sys.argv[1:]:
try:
f = open(arg,'r')
except IOError:
print "can not open",arg
else:
print arg,'has',len(f.readlines()),'lines'
f.close()
# 使用else子句比在try子句中附加代碼要好;
# 因爲這樣可以避免try...except意外的截獲本來不屬於他們保護的那些代碼拋出的異常(放在else子句裏)
# 發生異常時,可能會有一個附屬值,作爲異常的參數存在;
# 這個參數是否存在、什麼類型,依賴於異常的類型
# 在異常名(列表)之後,可以爲except子句指定一個變量;這個變量綁定於一個異常實例;
# 存儲在instance.args的參數中;(爲方便 異常實例定義了__str__(),這樣可以直接訪問打印參數而不必引用.args);
# instance.args更推薦;
# 給異常傳遞一個參數(多個異常傳遞的是一個元組)
try:
raise Exception('spam','eggs')
except Exception as inst:
print type(inst)
x, y = inst.args
print 'x = ', x, 'y = ', y
# 異常處理器不僅僅處理那些在try子句中立刻發生的異常,也會處理那些try子句中調用的函數內部發生的異常
def this_fails():
x = 1/0
try:
this_fails()
except ZeroDivisionError as detail:
print 'Handing run-time error',detail
# raise語句允許程序員強制拋出一個指定的異常;
# 要拋出的異常又raise的唯一參數標識;它必須是一個異常實例或異常類(繼承自Exception的類);
# 如果你需要明確一個異常是否拋出,但不想處理它,raise語句可以讓你很簡單的重新拋出該異常
try:
raise NameError('Flower')
except NameError:
print "An exception flew by!"
# raise
# 在程序中可以通過創建新的異常類型來命名自己的異常;
# 異常類通常應該直接或間接的從Exception類派生;
class MyError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
try:
raise MyError(2*2)
except MyError as e:
print 'My exception occurred, value:', e.value
# 在這個例子中,Exception默認的__init__()被覆蓋;
# 新的方式簡單的創建value屬性;這就替換了原來創建args屬性的方式;
# 異常類中可以定義任何其他類中可以定義的東西,但通常爲了保持簡單,只加入幾個屬性信息,供異常處理語句句柄提取;
# 如果一個新創建的模塊中需要拋出幾種不同的錯誤時,一個通常的做法是爲該模塊定義一個異常基類;
# 然後針對不同的錯誤類型派生出對應的異常子類;
# 與標準異常類似:大多數異常的命名 都以'Error'結尾;
# 很多標準模塊中都定義了自己的異常,用以報告在他們所定義的函數中可能發生的錯誤
class FlowerError(Exception):
"""Base class for exceptions in this module."""
pass
class FlowerInputError(FlowerError):
"""Exception raised for errors in the input.
Attributes:
expr -- input expression in which the error occurred
msg -- explanation of the error
"""
def __init__(self, expr, msg):
self.expr = expr
self.msg = msg
class FlowerTransitionError(FlowerError):
"""Raised when an operation attempts a state transition that is not allowed.
Attributes:
prev -- state at beginning of transition
next -- attempted new state
msg -- explanation of why the specific transition is not allowed
"""
def __init__(self, prev, next, msg):
self.prev = prev
self.next = next
self.msg = msg
# try語句還有另外一個可選的子句——finally,目的在於定義在任何情況下都一定要執行的功能;
def divide(x, y):
try:
result = x / y
except ZeroDivisionError:
print "division error"
else:
print 'result is ',result
finally:
print 'finally play'
divide(2, 1)
divide(2, 0)
# 不管有沒有發生異常,finally子句在程序離開try後都一定會被執行;
# 當try中發生了未被捕獲的異常,在finally執行之後,它會重新被拋出;
# try語句經由break、continue、return語句退出也一樣會執行finally子句;
# 真實的場景中,finally子句會被用於釋放外部資源(文件或網絡連接之類的),無論他們的使用過程中是否出錯;
# 有些對象 定義了標準的清理行爲,無論對象操作是否成功,不再需要該對象的時候就會起作用;
for line in open('myfile.txt'):
print line
# 這段代碼的問題在於代碼執行完之後沒有立即關閉打開的文件;
# 相比之下,with...as語句使得文件之類的對象總能及時準確地被清理;
with open('myfile.txt') as f:
for line in f:
print line
# 類似上邊示例,語句執行後,文件f總會被關閉,即使是在處理文件中的數據時出錯也一樣;
# 其他對象是否提供了預定義的清理行爲則需要查閱文檔;
# -*- coding:utf-8 -*-
# Python的類並沒有在用戶和定義之間設立絕對的屏障,而是依賴於用戶不去"強行闖入定義"的優雅;
# 且類中的大多數特性都被保留:
# 1.類繼承機制允許多重繼承,派生類可以覆蓋基類中的任何方法或類;
# 2.可以使用相同的方法名稱調用基類的方法;對象可以包含任意數量的私有數據;
# 用C++術語:類中成員都是公共的,成員函數都是虛(virtual)的;
# 方法函數在定義時需要以引用的對象作爲第一個參數,調用時則會隱式引用對象;
# 大多數帶有特殊語法的內置操作(運算符、下標等)都可以針對類的需要重新定義;
# 當一個對象有多個引用的時候,並且引用有不同的名稱,我們稱這個對象有別名(aliase);
# 命名空間 是從命名到對象的映射;
# 一些命名空間的例子:
# 1.內置命名集(向abs()這樣的函數、內置異常名);
# 2.模塊中的全局命名;
# 3.函數調用的局部命名;
# 莫種意義上 對象的屬性集也是一個命名空間;
# 值得注意的是,不同命名空間中的命名沒有任何關係;爲了不混淆,用戶必須以模塊名爲前綴來引用它們;
# 我們稱Python中任何一個.之後的命名爲屬性;
# 模塊對象.屬性;
# 模塊屬性和模塊中的全局命名共享同一個命名空間;
# 屬性可以是隻讀或寫的;
# 賦值:modname.answer = 42
# 可寫屬性可以使用del語句刪除,del modname.answer;
# 不同的命名空間在不同的時刻創建,有不同的生存期:
# 1.包含內置命名的命名空間 在Pyhton解釋器啓動時創建,會一直保留;
# 內置命名被包含在一個模塊中,它被稱作__builtin__
# 2.模塊的全局命名在模塊定義被讀入時創建,通常,模塊命名空間也會一直保存到解釋器退出;
# 3.由解釋器在最高層調用執行的語句,不管它是從腳本文件中讀入還是來自交互式輸入,都是__main__模塊的一部分;
# 他們也有自己的命名空間;
# 當調用函數時,會爲他創建一個局部命名空間;
# 並且在函數返回或拋出一個沒有在函數內部處理的異常時刪除;
# 每個遞歸調用都有自己的命名空間;
# 作用域就是一個Python程序可以直接訪問命名空間的正文區域;
# 每次執行時,至少有三個命名空間可以直接訪問的作用域嵌套在一起;
# 1.包含局部命名的使用域在最裏面;首先被搜索;其次搜索的是中層的作用域,這裏包含了同級的函數;
# 最後搜索最外邊的作用域,它包含內置命名;
# 2.最內層作用域:包含局部命名,任意函數包含的都是這種;
# 3.接下來的作用域包含當前模塊的全局命名;
# 4.最外層的作用域(最後搜索)是包含內置命名的命名空間;
# 如果一個命名聲明爲全局的,那麼所有的賦值和引用都直接針對包含模塊全局命名的中級作用域;
# 另外從外部訪問到的所有內層作用域的變量都是隻讀的;
# 類定義也是局部作用域中的另一個命名空間;
# 作用域決定源程序的意義:
# 一個定義於某模塊中的函數的全局作用域是該模塊的命名空間;而不是該函數的別名被定義或調用的位置;
# 事實上,所有引入新命名的操作都作用於局部作用域;
# 特別是import語句和函數名將模塊名或函數綁定於局部作用域(可以使用global語句將變量引入到全局作用域);
class ClassName:
pass
# 類的定義類似函數定義(def語句),要執行才能生效;
# 你可以把類的定義放到if語句的一個分支,也可以放到一個函數的內部;
# 類中的函數定義通常包括了一個特殊形式的參數列表,用於方法調用約定;
# 進入類定義部分後,會創建一個命名空間 作爲局部作用域;
# 所有的賦值成爲這個新命名空間的局部變量;特別是函數定義在此綁定了新的命名;
# 類定義完成時(正常退出),就創建了一個類對象;在這裏類對象綁定到類定義頭部的類名;
# 類對象支持兩種操作:屬性引用和實例化;
# 類對象創建後,類命名空間中所有的命名都是有效屬性名;
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
print MyClass.i
print MyClass.f,'方法參數self爲空 所以是未綁定的方法'
i = MyClass.i
f = MyClass().f
print i
print f()
MyClass.i = 123
print MyClass.i
print i
# MyClass.i MyClass().f都是有效的屬性引用;我們可以通過賦值修改MyClass.i;
# MyClass.__doc__也是一個有效的屬性,返回類的文檔字符串;
print MyClass.__doc__
# 類的實例化
x = MyClass()
print x.i
x.i = 1
print x.i
print MyClass.i
y = MyClass()
print y.i
y.i = 2
print y.i
print MyClass.i
# 很多類都傾向於將對象創建爲有初始狀態的;
# 因此類可能會定義一個名爲__init__()的特殊方法;
class MyClassTest:
def __init__(self):
self.data = []
# 定義了__init__()方法的話,類的實例化操作會自動爲新創建的類實例調用__init__()方法;
x_test= MyClassTest()
print x_test.data
# __init__()方法也可以待參數
class MyClassTest1:
def __init__(self, realpart, imagpart):
self.r = realpart
self.i = imagpart
x_test_1 = MyClassTest1("a","b")
print x_test_1.r, x_test_1.i
# 實例對象唯一可用的操作就是屬性引用;
# 方法通過右綁定方式調用;x.f()
# x.f是一個方法對象,可以存起來再調用;xf = x.f
# 我們來看前面的MyClass例子:
# 我們在f()的函數定義中指明瞭一個參數,這個參數我們並沒有傳,而實際上,Python對於函數調用缺少參數會拋出異常;
# 方法的特別之處在於:實例對象作爲函數的第一個參數傳給了函數;x.f() <=> MyClass.f(x)
# 以n個參數的列表去調用一個方法,就相當於將方法的對象插入到參數列表的最前面;
# 原理:
# 引用非數據屬性的實例屬性時,會搜索它的類;
# 如果這個命名確認爲一個有效的函數對象類屬性,就會將實例對象和函數對象封裝進一個抽象對象:這就是方法對象;
# 以一個參數列表調用方法對象時,它被重新拆封,用實例對象和原始的參數列表構造一個新的參數列表;
# 然後函數對象調用這個新的參數列表;
# 類變量用於類的所有實例共享屬性和方法;
# 前面MyClass x, y實例的例子,相應的x.i 和 y.i 共享的是MyClass.i;
# 即實例屬性共享了類屬性的值;
# 同時我們也看到,之後的實例屬性可以修改i的值,但並不影響MyClass.i的值;
# 我們重新舉一個例子說明:
class Cat:
kind = 'canine'
def __init__(self, name):
self.name = name
c1 = Cat('Fibo')
c2 = Cat('Buddy')
print c1.kind, c1.name
print c2.kind, c2.name
# 但對於一些可變的共享數據可能會有意外的效果;
class Elephont:
tricks = []
def __init__(self, name):
self.name = name
def add_tricks(self, trick):
self.tricks.append(trick)
e1 = Elephont('Fibo')
e2 = Elephont('Buddy')
e1.add_tricks('roll over')
e2.add_tricks('play dead')
print e1.tricks
# 這個類的正確設計應該是
class Dog:
def __init__(self, name):
self.name = name
self.tricks = []
def add_tricks(self, trick):
self.tricks.append(trick)
d1 = Dog('Fibo')
d2 = Dog('Buddy')
d1.add_tricks('roll over')
d2.add_tricks('play dead')
print d1.tricks
print d2.tricks
# 數據屬性會覆蓋同名的方法屬性,使用一些約定來減少衝突是明智的;
# 1.大寫方法名稱的首字母;
# 2.使用唯一的小字符串(也許只是一個下劃線)作爲數據屬性名稱的前綴;
# 3.或者方法使用動詞,數據屬性使用名詞;
# Python中不能強制隱藏數據(使用C編寫的Python實現可以完全隱藏實現細節,並控制對象的訪問,通過C語言擴展Python);
# 客戶應該謹慎的使用數據屬性,通過踐踏數據屬性會使那些由方法維護的常量變得混亂;
# 一般,方法的第一個參數被命名爲self;這只是一個約定,名稱self並沒有特殊性(可以有效改善代碼可讀性)
# 方法可以像引用普通的函數那樣引用全局命名;
# 函數定義代碼不一定非得定義在類中,也可以將一個函數對象賦值給類中的一個局部變量;
# Function defined outside the class
def func1(self, x, y):
return min(x, x + y)
class ClassF:
f = func1
def func2(self):
return 'Hello world!'
h = func2
# f和h都是類ClassF的屬性,引用的都是函數對象,因此它們都會ClassF的實例的方法;
# 通過self參數的方法屬性,方法可以調用其他的方法
class Bag:
def __init__(self):
self.data = []
def add(self, x):
self.data.append(x)
def addtwice(self, x):
self.add(x)
self.add(x)
b = Bag()
print b.__class__
# 每個值都有一個類(class),也稱爲它的類型(type),存儲爲object.__class__
# 如果一個語言不支持繼承,那麼類也就沒了意義;
# 派生類定義如下:
# class DerivedClassName(BaseClassName):
# 派生類定義的執行過程和基類是一樣的;
# 構造派生類,就記住了基類;這在解析屬性引用的時候尤其有用;
# 如果類中找不到請求調用的屬性,就會搜索基類;如果基類是由別的類派生而來,這個規則會遞歸的應用上去;
# 派生類可能會覆蓋其基類的方法;
# 虛方法:對非虛函數的調用在編譯的時候進行了綁定;調用虛方法時,則會在運行時解析該調用;
# 對應運行時決定調用的類型——即動態類型;
# Python中的所有方法本質都是虛方法;
# 調用基類方法:
# BaseClassName.methodname(self, arguments)
# 要注意,只有BaseClassName在同一全局作用域定義或導入時才能這樣用;
# Python有兩個用於運行時判斷的繼承關係的函數:
# 1.函數isinstance()用於檢查實例類型:如isinstance(obj,int) 判斷obj是否是int或其他從int繼承來的類型的實例;
# 2.函數issubclass()用於檢查類繼承:如issubclass(bool, int),爲True,因爲bool是int的子類;
# 但是issubclass(unicode, str)是False,因爲unicode不是str的子類,他們只是共享一個通用的祖先類basestring;
# Pyhton同樣有限的支持多繼承形式;
# 多繼承的類定義形如下例:
# class DerivedClassName(Base1, Base2, Base3):
# 對於舊風格的類,規則是 深度優先+從左到右;
# 即先Base1及其基類,在Base2及其基類 的順序進行屬性名稱搜索;
# 對於新風格的類,方法的解析順序動態變化地支持合作對super()的調用;
# 對於新風格的類,動態調整順序是必要的,因爲菱形關係,至少會有一個父類可以通過多條路徑訪問到;;
# 例如,新的風格的類,都繼承自object;爲了防止基類被重複訪問,使用動態算法線性化搜索順序
# 使每個類都按從左到右的順序被指定,保證每個父類只調用一次;
# 所有這些特性使得設計可靠並且可擴展的多繼承類成爲可能;
# Pyhton中不存在 只能從對象內部訪問的私有實例變量;
# 然而,有一種變通的方式應用於大多數Python代碼:
# 以一個下劃線開頭的命名(如_spam)會被處理爲API的非公開部分,無論它是一個函數、方法或數據成員;
# 它會被視爲一個實現細節,無需公開;
# 一個正當的私有成員用途是:避免子類裏定義的命名與之衝突;
# 因此,Python提供了對這種結構的有限支持,稱爲——name mangling(命名編碼);
# 任何形如__spam的標識(前面兩條下劃線,後面最多一個),被替換爲_classname_spam;
# 此語法不關注標識的位置,只要求在類定義內;
# 我們通過一個例子理解下:
class Mapping:
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)
def update(self, iterable):
for item in iterable:
self.items_list.append(item)
__update = update#private copy of original update() method
class MappingSubclass(Mapping):
def update(self, keys, values):
# private new sinature for update()
# but does not break __init__()
for item in zip(keys, values):
self.items_list.append(item)
m = Mapping([2,3])
print m.items_list
m1 = MappingSubclass([2,3])
print m1.items_list
m1.update(['key1','key2'], ['value1', 'value2'])
print m1.items_list
# 需要注意的是 編碼規則設計爲儘可能的避免衝突,被認作爲私有的變量仍然有可能被訪問或修改;
# 在一些特定場合就很有用,比如調試的時候;
# 要注意的是 代碼傳入exex、eval()或execfile()時不考慮所調用的類的類名,視其爲當前類;
# 這類似於global語句的效應,已經按字節編譯的部分也有同樣的限制;這也同樣作用於getattr(),setattr()和delattr();
# 就像直接引用__dict__一樣;
# 模塊對象有一個隱祕的只讀對象,名爲 __dict__ ,它返回用於實現模塊命名空間的字典;
# 命名 __dict__ 是一個屬性而非全局命名。
# 顯然,使用它違反了命名空間實現的抽象原則,應該被嚴格限制於調試中。
# 有時,類似C中的結構struct,將一組已命名的數據項綁定在一起;
# 一個空的類定義可以很好的實現;
class Employee:
pass
john = Employee()
john.name = "Flower"
john.salary = 1000
print john.name, john.salary
# 用戶自定義異常也可以是類;利用這個機制可以創建可擴展的異常體系;
# 以下是良好總新的、有效的(語義上的)拋出異常形式
# 1. raise Class, instance
# 2. raise instance
# 第一種形式,instance必須是Class或其派生類的一個實例;
# 第二種則是 raise instance.__class__, instance 的簡寫;
# 拋出一個異常,與except子句列出的類相符合需要 滿足 拋出的異常時類或其派生類的子類;
class B:
pass
class C(B):
pass
class D(C):
pass
for c in [B, C, D]:
try:
raise c()
except D:
print 'D'
except C:
print 'C'
except B:
print 'B'
# 簡單來說就是 D是B,但B不是D;上述示例如果將except的B/C/D顛倒,打印的就是B B B了;
# 打印一個異常類的錯誤信息時,先打印類名,然後是一個空格、一個冒號,然後是用內置函數str()將類轉換得到的完整字符串;
# 我們已經注意到 大多數容器類對象 都可以用for遍歷;(序列 元組 字典 字符串 文件。。。)
for key in {'one':1,'tow':2}:
print key
# for語句在容器對象中調用 iter();該函數返回一個定義了next()方法的迭代器對象,他可以在容器中逐一訪問元素;
# 沒有後續元素時,next()拋出一個StopIteration異常通知for語句循環結束;(和Swift同理)
# 給自己的類添加迭代器行爲:
# 定義一個__iter__()方法,使其返回一個帶有next()方�nt-family:FangSong;"> except D:
print 'D'
except C:
print 'C'
except B:
print 'B'
# 簡單來說就是 D是B,但B不是D;上述示例如果將except的B/C/D顛倒,打印的就是B B B了;
# 打印一個異常類的錯誤信息時,先打印類名,然後是一個空格、一個冒號,然後是用內置函數str()將類轉換得到的完整字符串;
# 我們已經注意到 大多數容器類對象 都可以用for遍歷;(序列 元組 字典 字符串 文件。。。)
for key in {'one':1,'tow':2}:
print key
# for語句在容器對象中調用 iter();該函數返回一個定義了next()方法的迭代器對象,他可以在容器中逐一訪問元素;
# 沒有後續元素時,next()拋出一個StopIteration異常通知for語句循環結束;(和Swift同理)
# 給自己的類添加迭代器行爲:
# 定義一個__iter__()方法,使其返回一個帶有next()方法的對象;如果這個類定義了next(),那麼__iter__()只需要返回self;
class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def next(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]