《简明Python教程》学习笔记
基本知識
第一個Python程序:
print("hello world")如何退出Python的解釋器提示符:
? 在Linux或OS X的shell程序中,可以通過按下[ctrl+d]或輸入exit()。
? 在windows命令提示符中,可以按下[ctrl+z]組合鍵,然后敲擊[center]鍵來退出。
注意:
Python 是區分大小寫的,如 print 和 Print 是不同的——注意前者的 p 是小寫的,而后者的 P 是大寫的。此外,你需要確保每一行的第一個字符前面都沒有任何空格或制表格。
注釋
? 注釋 是任何存在于 # 號右側的文字,其主要用作寫給程序讀者看的筆記。
? 舉個例子:
print('hello world') #注意到 print 是一個函數? 或者:
# 注意到 print 是一個函數 print('hello world')字面常量
? 可以直接表示其字面意思的量,例如5、1.23這樣的數字或字符串文本。
數字
? 數字主要分為兩種類型——整數(Integers)與浮點數(Floats)。
? 針對有經驗的程序員的提示
? 沒有單獨的 long 類型。 int 類型可以指任何大小的整數。
字符串
? 一串字符串(String)是 字符(Characters) 的 序列(Sequence)。基本上,字符串就是一串詞匯。
? 單引號和雙引號的作用差不多,都是指定字符串:
? '我是一個字符串’和“我是一個字符串”的效果一樣。
? 三引號,主要用于多行字符串,在三引號中可以任意使用單引號和雙引號。例如:
'''這是一段多行字符串。這是它的第一行。 This is the second line. "What's your name?," I asked. He said "Bond, James Bond." '''? 字符串是不可變的。
? format可以使用內容填充字符串中的占位符(格式)。
? print輸出字符串時,會在末尾自動加上換行符“\n”,可以使用end參數替換掉換行符。例如:
print('a',end='') print('b',end='')? 輸出結果如下:
ab? 轉義序列
? 對于有特殊含義的字符出現在字符串中,需要使用轉義序列""來進行轉義,例如’What’s your name’在python中會報錯,你需要對其中的但引號進行轉義,‘What’s your name’。
? 最重要的轉義序列:\n代表換行,\t代表制表符
? 在一個字符串中,一個放置在末尾的反斜杠表示字符串將在下一行繼續,但不會添加新的一行。來看看例子:
"This is the first sentence. \ This is the second sentence."相當于:
"This is the first sentence. This is the second sentence."原始字符串
在字符串前面使用r或R可以表示這個字符串沒有經過任何處理,也就是原樣輸出。
r"Newlines are indicated by \n"在使用正則表達式時,應該全程使用原樣字符串。舉例說明的話,反向引用可以通過 ‘\1’ 或 r’\1’ 來實現。
變量
? 可以存儲任何信息,并且也能夠操作它們。
標識符命名
? 就是給某些東西一個名稱。標識符命名規則和C語言、Java的一樣,標識符中只能有字母、數字、下劃線,并且開頭只能是字母或數字。
數據類型
? 變量可以將各種形式的值保存為不同的數據類型(Data Type)。基本的類型是我們已經討論過的數字與字符串。
對象
? Python將程序中的任何內容統稱為對象(Object)。
? 針對面向對象編程語言用戶的提示:
? Python 是強(Strongly)面向對象的,因為所有的一切都是對象, 包括數字、字符串與
? 函數。
? 針對靜態編程語言程序員的提示:變量只需被賦予某一值。不需要聲明或定義數據類型。
物理行與邏輯行
? 在編輯器中看到的一行代碼就是物理行,Python所讀到的一個語句就是一個邏輯行。
? Python隱藏的期望是在一行中只寫一個語句,也就是盡量不要使用分號。
? 如果一行代碼實在是太長了,可以使用反斜杠""將其拆分成多個物理行,這被稱作顯式行連接。
? 例如:
i=\ 5 #等價于 i=5? 在某些情況下,會存在一個隱含的假設,允許你不使用反斜杠。這一情況即邏輯行以括號開始,它可以是方括號或花括號,但不能是右括號。
縮進
? 每個語句 前面的空白區就是縮進,空白區可以確定邏輯行的縮進級別,而縮進級別又可以用于確定語句的分組。
? 有一件事你需要記住:錯誤的縮進可能會導致錯誤。
? Python官方的建議是使用四個空格來縮進。
運算符與表達式
? 表達式可以拆分成運算符(Operators)與操作數(Operands)。
? 各個操作符的詳細信息參考《簡明Python教程》的p45-46的運算符的速覽。
? 基本的操作符都和Java的一樣,需要注意的是邏輯非用“not”、邏輯與用“and”、邏輯或用“or”。
數值運算與賦值的快捷方式
? 其實就是Python的數值運算支持乘賦“*=”等快捷操作。例如:
a=2 a=a*3 #等價于 a=2 a *= 3求值順序
? Python中對于運算符的優先級也是有規定的,《簡明Python教程》中有說明,在P47頁,也可以參考Python的手冊。最好是使用圓括號對運算符和表達式進行分組。
控制流
? 在Python中有三種控制流語句——if、for和while。
if 語句
示例:
number = 23 guess = int(input('Enter an integer : '))if guess == number : # 我們借助這個冒號向Python指定接下來會有一塊語句在后頭# 新塊從這里開始print('Congratulations, you guessed it.')print('(but you do not win any prizes!)') elif guess < number :# 另一代碼塊print('No, it is a little higher than that ')# 你可以在此做任何你希望在改代碼塊內進行的事情 else :print('No, it is a little lower than that')# 你必須通過猜測一個大于(>)設置數的數字來到達這里。print('Done') # 這最后一句話將在 if 語句執行完畢后執行輸出:
$ python if.py Enter an integer : 50 No, it is a little lower than that Done $ python if.py Enter an integer : 22 No, it is a little higher than that Done $ python if.py Enter an integer : 23 Congratulations, you guessed it. (but you do not win any prizes!) Done針對 C/C++ 程序員的提示
Python 中不存在 switch 語句。你可以通過使用 if…elif…else 語句來實現同樣的事情(在某些情況下,使用一部字典能夠更快速地完成)。
while 語句
? while語句是循環語句的一種。while語句同樣可以擁有else子句作為可選項。
number = 23 running = Truewhile running :guess = int(input('Enter an integer : '))if guess == number :print('Congratulations, you guessed it. ')# 這將導致while循環終止running = Falseelif guess < number :print('No, it is a little higher than that. ')else :print('No, it is a little lower than that. ') else : # 用冒號告訴Python下面有語句塊print('The while loop is over. ')# 在這里你可以做你想做的任何事情print('Done')輸出:
$ python while.py Enter an integer : 50 No, it is a little lower than that. Enter an integer : 22 No, it is a little higher than that. Enter an integer : 23 Congratulations, you guessed it. The while loop is over. Done? 如果 while 循環中存在一個 else 代碼塊,它將總是被執行,除非你通過 break 語句來中斷這一循環。
針對 C/C++ 程序員的提示
你可以在 while 循環中使用 else 從句。
for 循環
? for…in 語句是另一種循環語句,其特點是會在一系列對象上進行迭代(Iterates),意即它會遍歷序列中的每一個項目。
案例:
for i in range(1, 5) :print(i) else:print('The for loop is over')輸出:
$ python for.py 1 2 3 4 The for loop is over? 要記住, else 部分是可選的。當循環中包含他時,它總會在 for 循環結束后開始執行,除非程序遇到了 break 語句。
break 語句
? break語句用以中斷循環語句,即使循環條件沒有變成False,也可以被終止。
? 需要注意的是,如果你中斷了一個For或while循環,任何相應循環中的else塊都將不會被執行。
示例:
while True :s = input('Enter something : ')if s == 'quit' : #zhg: 注意字符串判斷相等的方式breakprint('Length of the string is', len(s)) print('Done')輸出:
$ python break.py Enter something : Programming is fun Length of the string is 18 Enter something : When the work is done Length of the string is 21 Enter something : if you wanna make your work also fun: Length of the string is 37 Enter something : use Python! Length of the string is 11 Enter something : quit Done? 記住, break 語句同樣可以適用于 for 循環。
Continue語句
? continue 語句用以告訴 Python 跳過當前循環塊中的剩余語句,并繼續該循環的下一次迭代。
示例:
while True :s = input('Enter something : ')if s == 'quit' :breakif len(s) < 3 :print('Too small')continueprint('Input is of sufficient length')# 自此處起繼續進行其它任何處理輸出:
$ python continue.py Enter something : a Too small Enter something : 12 Too small Enter something : abc Input is of sufficient length Enter something : quit? 要注意 continue 語句同樣能用于 for 循環。
函數
? 函數(Functions)是指可重復使用的程序片段。
? 函數可以通過關鍵字 def 來定義。這一關鍵字后跟一個函數的標識符名稱,再跟一對圓括號,其中可以包括一些變量的名稱,再以冒號結尾,結束這一行。隨后而來的語句塊是函數的一部分。下面的案例將會展示出這其實非常簡單:
def say_hello() :# 該塊屬于這一函數print('hello world') # 函數結束say_hello() # 調用函數 say_hello() # 再次調用函數輸出:
$ python function1.py hello world hello world函數參數
? 函數的參數就是定義在那個圓括號中變量,通過它們可以向函數傳遞信息。
? 在定義函數時給定的名稱稱作“形參”(Parameters),在調用函數時你所提供給函數的值稱作“實參”(Arguments)。
? 案例:
輸出:
$ python function_param.py 4 is maximum 7 is maximum局部變量
? 在函數定義中聲明的變量就是局部變量,它們不會以任何形式與函數外的同名變量發生聯系。
? 所有變量的作用域是它們被定義的塊,從定義它們的名字的定義點開始。
? 示例:
x = 50def func(x) :print('x is', x)x = 2print('changed local x to', x)func(x) print('x is still', x)輸出:
$ python function_local.py x is 50 Changed local x to 2 x is still 50zhg: python的函數也是遵從值傳遞規則,即傳遞給函數參數的是值,而不是地址。
global 語句
? 使用global語句可以使用并更改函數之外的變量的值。
? 案例:
x = 50def func() :global xprint('x is', x)x = 2print('changed global x to', x)func() print('value of x is', x)輸出:
$ python function_global.py x is 50 Changed global x to 2 Value of x is 2? global 語句用以聲明 x 是一個全局變量——因此,當我們在函數中為 x 進行賦值時,這一改動將影響到我們在主代碼塊中使用的 x 的值。
? 你可以在同一句 global 語句中指定不止一個的全局變量,例如 global x, y, z 。
默認參數值
? 你可以通過在函數定義時附加一個賦值運算符( = )來為參數指定默認參數值。
? 默認參數值應該是常量,即是不可變的。
? 示例:
def say(message, times=1):print(message * times)say('Hello') say('World', 5)輸出:
$ python function_default.py Hello WorldWorldWorldWorldWorld注意
只有那些位于參數列表末尾的參數才能被賦予默認參數值,意即在函數的參數列表中擁
有默認參數值的參數不能位于沒有默認參數值的參數之前。
這是因為值是按參數所處的位置依次分配的。舉例來說, def func(a, b=5) 是有效的,
但 def func(a=5, b) 是無效的。
關鍵字參數
? 可以通過命名的防止給制定參數賦值。
? 示例:
def func(a, b=5, c=10):print('a is', a, 'and b is', b, 'and c is', c)func(3, 7) func(25, c=24) func(c=50, a=100)? 輸出:
$ python function_keyword.py a is 3 and b is 7 and c is 10 a is 25 and b is 5 and c is 24 a is 100 and b is 5 and c is 50可變參數
? 可以通過星號來實現任意數量的變量。
? 示例:
def total(a=5, *numbers, **phonebook):print('a', a)#遍歷元組中的所有項目for single_item in numbers :print('single_item', single_item)#遍歷字典中的所有項目for first_part, second_part in phonebook.items() :print(first_part, second_part)print(total(10, 1, 2, 3, Jack=1123, John=2231, Inge=1560))? 輸出:
$ python function_varargs.py a 10 single_item 1 single_item 2 single_item 3 Inge 1560 John 2231 Jack 1123 None? 當我們聲明一個諸如 *param 的星號參數時,從此處開始直到結束的所有位置參數(Positional Arguments)都將被收集并匯集成一個稱為“param”的元組(Tuple)。
? 類似地,當我們聲明一個諸如 **param 的雙星號參數時,從此處開始直至結束的所有關鍵字參數都將被收集并匯集成一個名為 param 的字典(Dictionary)。
return 語句
? return語句用于在函數中返回,我們可以選擇從函數中返回一個值。
? 示例:
def maximum(x, y):if x > y :return xelif x == y :return 'The numbers are equal'else :return yprint(maximum(2, 3))? 輸出:
$ python function_return.py 3? 如果return語句沒有搭配任何一個值,則代表返回None。None在Python中是一個特殊的類型,代表著虛無。
? 每一個函數都在其末尾隱含了一句 return None ,除非你寫了你自己的 return 語句。你可以運行 print(some_function()) ,其中 some_function 函數不使用 return 語句,就像這樣:
def some_function(): pass? Python 中的 pass 語句用于指示一個沒有內容的語句塊。
提示:有一個名為 max 的內置函數已經實現了“找到最大數”這一功能,所以盡可能地使
用這一內置函數。
DocStrings
? 文檔字符串就是對函數的一個說明,其實和Java的類似,就是位置和規范不同。
? 示例:
def print_max(x, y) :'''打印兩個數值中的最大數這兩個數都應該是整數'''# 如果可能,將其轉換成整數類型x = int(x)y = int(y)if x > y :print(x, 'is maximum')else :print(y, 'is maximum')print_max(3, 5) print(print_max.__doc__)? 輸出:
$ python function_docstring.py 5 is maximum 打印兩個數值中的最大數。這兩個數都應該是整數? 函數的第一行邏輯行中的字符串是該函數的 文檔字符串(DocString)。
? 該文檔字符串所約定的是一串多行字符串,其中第一行以某一大寫字母開始,以句號結束。第二行為空行,后跟的第三行開始是任何詳細的解釋說明。在此強烈建議你在你所有重要功能的所有文檔字符串中都遵循這一約定。
? 我們可以通過使用函數的 doc (注意其中的雙下劃綫)屬性(屬于函數的名稱)來獲取函數 print_max 的文檔字符串屬性。Python將所有東西都視為一個對象,其中包括函數。
? help()函數所做的便是獲取函數的 doc 屬性并以一種整潔的方式將其呈現給你。
? Python發行版中附帶的pydoc 命令與 help() 使用文檔字符串的方式類似。
模塊
? 模塊就是在不同程序間復用代碼的手段。
? 編寫模塊有很多種方法,其中最簡單的一種便是創建一個包含函數與變量、以 .py 為后綴的文件。
? 另一種方法是使用撰寫 Python 解釋器本身的本地語言來編寫模塊。
? 一個模塊可以被其它程序導入并運用其功能。
? 案例:
import sysprint('The command line arguments are:') for i in sys.argv:print(i)print('\n\nThe PYTHONPATH is', sys.path, '\n')? 輸出:
$ python module_using_sys.py we are arguments The command line arguments are: module_using_sys.py we are argumentsThe PYTHONPATH is ['/tmp/py', # many entries here, not shown here '/Library/Python/2.7/site-packages', '/usr/local/lib/python2.7/site-packages']需要注意的是,運行的腳本名稱在 sys.argv 的列表中總會位列第一。
按字節碼編譯的 .pyc 文件
? 可以將Python文件按字節編譯成中間文件,這樣有利于模塊的加載速度。
from…import語句
? from…import語句可以避免每次使用argv時都要輸入"sys.",即使用from sys import argv 語句來實現這一點。
? 示例:
from math import sqrt print("Square root of 16 is", sqrt(16))模塊的__name__
? 每個模塊都有一個名稱,可以通過模塊的名稱來確定它是獨立運行的還是被導入進來的,這可以通過使用模塊的__name__ 屬性來實現。。
? 案例:
if __name__ == '__main__' :print('This program is being run by itself') else:print('I am being imported from another module')? 輸出:
$ python module_using_name.py This program is being run by itself $ python >>> import module_using_name I am being imported from another module >>>? 每一個 Python 模塊都定義了它的 name 屬性。如果它與 main 屬性相同則代表這一模塊是由用戶獨立運行的。
編寫你自己的模塊
? 每一個 Python 程序同時也是一個模塊。你只需要保證它以 .py 為擴展名即可。
? 案例:
def say_hi():print('Hi, this is mymodule speaking.')__version__ = '0.1'? 注意:自己定義的模塊要放在要導入該模塊的程序的所在目錄,或者在sys.path所列出的某個目錄下。
? 另一個模塊:
import mymodulemymodule.say_hi() print('Version', mymodule.__version__)? 輸出:
$ python mymodule_demo.py Hi, this is mymodule speaking. Version 0.1? 下面是一個使用 from…import 語法的范本:
from mymodule import say_hi, __version__say_hi() print('Version', __version__)? 你還可以使用:
from mymodule import *? 這將導入諸如 say_hi 等所有公共名稱,但不會導入 __version__ 名稱,因為后者以雙下劃線開頭。
? 警告:盡量不要使用from…import形式,容易引起變量名沖突。
dir 函數
? 內置的 dir() 函數能夠返回由對象所定義的名稱列表。
? 該函數接受參數。 如果參數是模塊名稱,函數將返回這一指定模塊的名稱列表。 如果沒有提供參數,函數將返回當前模塊的名稱列表。
案例:
$ python >>> import sys # 給出 sys 模塊中的屬性名稱 >>> dir(sys) ['__displayhook__', '__doc__', 'argv', 'builtin_module_names', 'version', 'version_info'] # 此處只展示部分條目 # 給出當前模塊的屬性名稱 >>> dir() ['__builtins__', '__doc__', '__name__', '__package__','sys'] # 創建一個新的變量 'a' >>> a = 5 >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'a'] # 刪除或移除一個名稱 >>> del a >>> dir() ['__builtins__', '__doc__', '__name__','__package__']? 關于 del 的一個小小提示——這一語句用于刪除一個變量或名稱,當這一語句運行后,在本例中即 del a ,你便不再能訪問變量 a ——它將如同從未存在過一般。
包
? 包是指一個包含模塊與一個特殊的 __init__.py 文件的文件夾,后者向 Python 表明這一文件夾是特別的,因為其包含了 Python 模塊。
? 讓我們這樣設想:你想創建一個名為“world”的包,其中還包含著 “asia”、“africa”等其它子包,同時這些子包都包含了諸如“india”、 “madagascar”等模塊。
? 下面是你會構建出的文件夾的結構:
- <some folder present in the sys.path>/- world/- __init__.py- asia/- __init__.py- india/- __init__.py- foo.py- africa/- __init__.py- madagascar/- __init__.py- bar.py? 包是一種能夠方便地分層組織模塊的方式。你將在 標準庫 中看到許多有關于此的實例。
總結
? 如同函數是程序中的可重用部分那般,模塊是一種可重用的程序。包是用以組織模塊的另一種層次結構。Python 所附帶的標準庫就是這樣一組有關包與模塊的例子。
數據結構
? 數據結構是用來存儲一系列相關數據的集合。
? Python 中有四種內置的數據結構——列表(List)、元組(Tuple)、字典(Dictionary)和集合(Set)。
列表
? 列表 是一種用于保存一系列有序項目的集合。項目的列表應該用方括號括起來,這樣 Python 才能理解到你正在指定一張列表。
? 列表對應的類型是list
? 示例:
# This is my shopping list shoplist = ['apple', 'mango', 'carrot', 'banana']print('I have', len(shoplist), 'item to purchase.')print('These items are:', end=' ') for item in shoplist :print(item, end=' ')print('\nI also have to buy rice.') shoplist.append('rice') print('My shopping list is now', shoplist)print('I will sort my list now') shoplist.sort() print('Sorted shopping list is', shoplist)print('The first item I will buy is', shoplist[0]) olditem = shoplist[0] del shoplist[0] print('I bought the', olditem) print('My shopping list is now', shoplist)? 輸出:
$ python ds_using_list.py I have 4 items to purchase. These items are: apple mango carrot banana I also have to buy rice. My shopping list is now ['apple', 'mango', 'carrot', 'banana', 'rice'] I will sort my list now Sorted shopping list is ['apple', 'banana', 'carrot', 'mango', 'rice'] The first item I will buy is apple I bought the apple My shopping list is now ['banana', 'carrot', 'mango', 'rice']元組
? 元組(Tuple)用于將多個對象保存到一起。元組的一大特征類似于字符串,它們是不可變的,也就是說,你不能編輯或更改元組。
? 元組對應的類型是tuple
? 案例:
# 我會推薦你總是使用括號 # 來指明元組的開始與結束 # 盡管括號是一個可選選項。 # 明了勝過晦澀,顯式優于隱式。 zoo = ('python', 'elephant', 'penguin') print('Number of animals in the zoo is', len(zoo))new_zoo = 'monkey', 'camel', zoo print('Number of cages in the new zoo is', len(new_zoo)) print('All animals in new zoo are', new_zoo) print('Animals brought from old zoo are', new_zoo[2]) print('Last animal brought from old zoo is', new_zoo[2][2]) print('Number of animals in the new zoo is',len(new_zoo)-1+len(new_zoo[2]))? 輸出:
$ python ds_using_tuple.py Number of animals in the zoo is 3 Number of cages in the new zoo is 3 All animals in new zoo are ('monkey', 'camel', ('python', 'elephant', 'penguin')) Animals brought from old zoo are ('python', 'elephant', 'penguin') Last animal brought from old zoo is penguin Number of animals in the new zoo is 5包含 0 或 1 個項目的元組
一個空的元組由一對圓括號構成,就像 myempty = () 這樣。然而,一個只擁有一個項目的元組并不像這樣簡單。你必須在第一個(也是唯一一個)項目的后面加上一個逗號來指定它,如此一來 Python 才可以識別出在這個表達式想表達的究竟是一個元組還是只是一個被括號所環繞的對象,也就是說,如果你想指定一個包含項目 2 的元組,你必須指定 singleton = (2, ) 。
字典
? 字典其實就是一個映射。在字典中鍵不能重復,并且鍵必須是不可變的對象,而對應的值可以是可變的或不可變的。
? 另外需要記住,字典中的成對的鍵—值配對不會以任何方式進行排序。
? 下面使用的字典是屬于dict類下的實例或對象。
# “ab”是地址(Address)簿(Book)的縮寫ab = {'Swaroop':'swaroop@swaroopch.com','Larry' : 'larry@qq.com','Matsumoto':'matz@163.com','Spammer':'spammer@hotmail.com' }print("Swaroop's address is", ab['Swaroop'])# 刪除一對鍵值—值配對 del ab['Spammer']print('\nThere are {} contacts in the address-book\n'.format(len(ab)))for name, address in ab.items() :print('Contact {} at {} '.format(name, address))# 添加一堆鍵——值配對 ab['Guido']='guido@python.org'if 'Guido' in ab :print("\nGuido's address is", ab['Guido'])? 輸出:
$ python ds_using_dict.py Swaroop's address is swaroop@swaroopch.comThere are 3 contacts in the address-bookContact Swaroop at swaroop@swaroopch.com Contact Matsumoto at matz@ruby-lang.org Contact Larry at larry@wall.orgGuido's address is guido@python.org序列
? 序列的主要功能就是判斷是否存在和索引操作。
? 列表、元組和字符串都是序列,它們都擁有切片操作。切片操作可以獲取序列中的一部分。
? 案例:
shoplist = ['apple', 'mango', 'carrot', 'banana'] name = 'swaroop'# Indexing or 'Subscription' operation # # 索引或“下標(Subscription)”操作符 # print('Item 0 is', shoplist[0]) print('Item 1 is', shoplist[1]) print('Item 2 is', shoplist[2]) print('Item 3 is', shoplist[3]) print('Item -1 is', shoplist[-1]) print('Item -2 is', shoplist[-2]) print('Character 0 is', name[0])# Slicing on a list # print('Item 1 to 3 is', shoplist[1:3]) print('Item 2 to end is', shoplist[2:]) print('Item 1 to -1 is', shoplist[1:-1]) print('Item start to end is', shoplist[:])# 從某一字符串中切片 # print('characters 1 to 3 is', name[1:3]) print('characters 2 to end is', name[2:]) print('characters 1 to -1 is', name[1:-1]) print('characters start to end is', name[:])? 輸出:
$ python ds_seq.py Item 0 is apple Item 1 is mango Item 2 is carrot Item 3 is banana Item -1 is banana Item -2 is carrot Character 0 is s Item 1 to 3 is ['mango', 'carrot'] Item 2 to end is ['carrot', 'banana'] Item 1 to -1 is ['mango', 'carrot'] Item start to end is ['apple', 'mango', 'carrot', 'banana'] characters 1 to 3 is wa characters 2 to end is aroop characters 1 to -1 is waroo characters start to end is swaroop? 切片時可以提供第三個數字,這數字就是步長,如果不提供的話就默認為1。
? 案例:
>>> shoplist = ['apple', 'mango', 'carrot', 'banana'] >>> shoplist[::1] ['apple', 'mango', 'carrot', 'banana'] >>> shoplist[::2] ['apple', 'carrot'] >>> shoplist[::3] ['apple', 'banana'] >>> shoplist[::-1] ['banana', 'carrot', 'mango', 'apple']? 你會注意到當步長為 2 時,我們得到的是第 0、2、4…… 位項目。當步長為 3 時,我們得到的是第 0、3……位項目。
集合
? 集合(Set)是簡單對象的無序集合(Collection)。集合中元素無序且唯一,集合主要是用來測試項目是否存在,子集判斷和交集運算等。
? 案例:
>>> bri = set(['brazil', 'russia', 'india']) >>> 'india' in bri True >>> 'usa' in bri False >>> bric = bri.copy() >>> bric.add('china') >>> bric.issuperset(bri) True >>> bri.remove('russia') >>> bri & bric # OR bri.intersection(bric) {'brazil', 'india'}引用
? 就是將一個變量綁定到一個對象。這個變量不是代表那個對象本身,只是指向了內存中存儲相應對象的那一部分。
? 案例:
print('Simple Assignment') shoplist = ['apple', 'mango', 'carrot', 'banana'] # mylist 只是指向同一對象的另一種名稱 mylist = shoplist# 我購買了第一項項目,所以我將其從列表中刪除 del shoplist[0]print('shoplist is', shoplist) print('mylist is', mylist) # 注意到 shoplist 和 mylist 二者都 # 打印出了其中都沒有 apple 的同樣的列表,以此我們確認 # 它們指向的是同一個對象print('Copy by making a full slice') # 通過生成一份完整的切片制作一份列表的副本 mylist = shoplist[:] # 刪除第一個項目 del mylist[0]print('shoplist is', shoplist) print('mylist is', mylist)? 輸出:
$ python ds_reference.py Simple Assignment shoplist is ['mango', 'carrot', 'banana'] mylist is ['mango', 'carrot', 'banana'] Copy by making a full slice shoplist is ['mango', 'carrot', 'banana'] mylist is ['carrot', 'banana']有關字符串的更多內容
? 你在程序中使用的所有字符串都是 str 類下的對象。可以查閱help(str)獲得更多關于字符串的信息。
? 案例:
# 這是一個字符串對象 name = 'Swaroop'if name.startswith('Swa') :print('Yes, the string starts with "Swa"')if 'a' in name :print('Yes, it contains the string "a"')if name.find('war') != -1 :print('Yes, it contains the string "war"')delimiter = '_*_' mylist = ['Brazil', 'Russia', 'India', 'China'] print(delimiter.join(mylist))? 輸出:
$ python ds_str_methods.py Yes, the string starts with "Swa" Yes, it contains the string "a" Yes, it contains the string "war" Brazil_*_Russia_*_India_*_China? startswith 方法用于查找字符串是否以給定的字符串內容開頭。 in 運算符用以檢查給定的字符串是否是查詢的字符串中的一部分。
? find 方法用于定位字符串中給定的子字符串的位置。如果找不到相應的子字符串, find會返回 -1。 str 類同樣還擁有一個簡潔的方法用以 聯結(Join) 序列中的項目,其中字符串將會作為每一項目之間的分隔符,并以此生成并返回一串更大的字符串。
解決問題
待解決的問題:
我想要一款程序來備份我所有的重要文件。
《簡明Python教程》解決問題的方式:
先分析問題,提出一些需要解決的問題。
設計穩定的方案,確定步驟
進行編碼實現
進行測試
解決方案:
import os import time# 1. 需要備份的文件與目錄將被 # 指定在一個列表中。 # 例如在 Windows 下: # source = ['"C:\\My Documents"', 'C:\\Code'] # 又例如在 Mac OS X 與 Linux 下: source = ['/Users/swa/notes'] # 在這里要注意到我們必須在字符串中使用雙引號 # 用以括起其中包含空格的名稱。#2. 備份文件必須存儲在一個 #主備份目錄中 #例如在 Windows 下: # target_dir = 'E:\\Backup' # 又例如在 Mac OS X 和 Linux 下: target_dir = '/Users/swa/backup' # 要記得將這里的目錄地址修改至你將使用的路徑# 3. 備份文件將打包壓縮成 zip 文件。 # 4. zip 壓縮文件的文件名由當前日期與時間構成。 target = target_dir + os.sep + \ time.strftime('%Y%m%d%H%M%S') + '.zip'# 如果目標目錄還不存在,則進行創建 if not os.path.exists(target_dir): os.mkdir(target_dir) # 創建目錄# 5. 我們使用 zip 命令將文件打包成 zip 格式 zip_command = 'zip -r {0} {1}'.format(target, ' '.join(source))# 運行備份 print('Zip command is:') print(zip_command) print('Running:') if os.system(zip_command) == 0: print('Successful backup to', target) else: print('Backup FAILED')? 輸出:
$ python backup_ver1.py Zip command is: zip -r /Users/swa/backup/20140328084844.zip /Users/swa/notes Running: adding: Users/swa/notes/ (stored 0%) adding: Users/swa/notes/blah1.txt (stored 0%) adding: Users/swa/notes/blah2.txt (stored 0%) adding: Users/swa/notes/blah3.txt (stored 0%) Successful backup to /Users/swa/backup/20140328084844.zip針對 Windows 用戶的提示
除了使用雙反斜杠轉義序列,你還可以使用原始字符串。例如使用 ‘C:\Documents’ 或
r’C:\Documents’ 。然而,不要使用 ‘C:\Documents’ ,因為它將被識別為你使用了一個
未知的轉義序列 \D 來結束路徑的輸入。
可以對上述的程序持續的改進,參照《簡明Python教程》的P94.
軟件開發流程
? 我們已經經歷了開發一款軟件的流程中的各個 階段(Phases) 。現在可以將這些階段總結如下:
? 編寫程序時推薦的一種方式是遵循我們在編寫備份腳本時所經歷的步驟:進行分析與設計;開始實現一個簡單版本;測試并修復錯誤;開始使用以確保工作狀況皆如期望那般。現在,你可以添加任何你所希望擁有的功能,并繼續去重復這一“開始做—測試—使用”循環,需要做多少次就去做多少次。
要記住:
程序是成長起來的,不是搭建出來的。 (Software is grown, not built.) ——Bill dehóra
zhg: 這一章節非常的重要,強烈建議看一下《簡明Python教程》的“解決問題”章節。
面向對象編程
? 類與對象是面向對象編程的兩個主要方面。一個類(Class)能夠創建一種新的類型(Type),其中對象(Object)就是類的實例(Instance)。可以這樣來類比:你可以擁有類型 int 的變量,也就是說存儲整數的變量是 int 類的實例(對象)。
? 需要注意的是:在Python中int也是一個類型,整數也會被視為對象。
? 在對象中存儲數據的普通變量稱作“字段”。對象可以調用類的函數來實現功能,這些函數叫做“方法”。
? 總之,字段與方法通稱類的屬性(Attribute)。
? 字段有兩種類型——它們屬于某一類的各個實例或對象,或是從屬于某一類本身。它們被分別稱作實例變量(Instance Variables)與類變量(Class Variables)。
? 通過 class 關鍵字可以創建一個類。這個類的字段與方法可以在縮進代碼塊中予以列出。
self
? 類方法與普通函數只有一種特定的區別——前者必須多加一個參數在參數列表開頭,這個名字必須添加到參數列表的開頭,但是你不用在你調用這個功能時為這個參數賦值,Python 會為它提供。這種特定的變量引用的是對象本身,按照慣例,它被賦予 self 這一名稱。
? 上述參數也可以指定為其他的名稱,但是強烈建議使用self,方便其他人識別,也有利于IDE提供編程提示。
? 你一定會在想 Python 是如何給 self 賦值的,以及為什么你不必給它一個值。一個例子或許會讓這些疑問得到解答。假設你有一個 MyClass 的類,這個類下有一個實例 myobject 。當你調用一個這個對象的方法,如 myobject.method(arg1, arg2) 時,Python 將會自動將其轉換成 MyClass.method(myobject, arg1, arg2) ——這就是 self 的全部特殊之處所在。
? 這同時意味著,如果你有一個沒有參數的方法,你依舊必須擁有一個參數—— self 。
類
? 最簡單的類如下:
class Person:pass # 一個空的代碼塊p = Person() print(p)? 輸出:
$ python oop_simplestclass.py <__main__.Person instance at 0x10171f518>方法
? 類和對象都可以帶有方法,但是需要多一個額外的參數。
? 案例:
class Person:def say_hi(self):print('Hello, how are you?')p = Person() p.say_hi() # 前面兩行同樣可以寫作 # Person().say_hi()? 輸出:
$ python oop_method.py Hello, how are you?? 要注意到 say_hi 這一方法不需要參數,但是依舊在函數定義中擁有 self 變量。
__init__方法
? 這個方法會在對象被示例化的時候被調用,其實就相當于是構造函數。
? 案例:
class Person:def __init__(self, name):self.name = namedef say_hi(self):print('Hello, my name is', self.name)p = Person('Swaroop') p.say_hi() # 前面兩行同時也能寫作 # Person('Swaroop').say_hi()? 輸出:
$ python oop_init.py Hello, my name is Swaroop? self.name 中的點號意味著這個叫作“name”的東西是某個叫作“self”的對象的一部分,而另一個 name 則是一個局部變量。
? zhg: 注意上面這個案例中創建對象字段的方式,根本就沒先定義,而是直接就開始賦值了。
類變量與對象變量
? 數據部分——也就是字段——只不過是綁定(Bound)到類與對象的命名空間(Namespace)的普通變量。這些名稱僅在這些類與對象所在的上下文中有效,這就是它們被稱作“命名空間”的原因。
? 字段有兩種類型——類變量和對象變量。
? 類變量是屬于類的,所有類的實例可以共享這個變量,類變量只有一個副本,當一個類的實例對類變量進行改變時,其他的實例都可以看到這種改變。
? 對象變量是屬于實例或對象的,每個實例都會有對象變量的一個單獨副本,在一個實例中對對象變量進行改變時,其他實例中的同名對象變量不會有任何的變化。
? 案例:
# coding=UTF-8 class Robot:"""表示有一個帶名字的機器人"""# 一個類變量, 用來計數機器人的數量population = 0def __init__(self, name):"""初始化數據"""self.name = nameprint("(Initializing {})".format(self.name))# 當有人被創建時,機器人# 將會增加人口數量Robot.population += 1def die(self):"""我掛了"""print("{} is being destroyed!".format(self.name))Robot.population -= 1if Robot.population == 0:print("{} was the last one.".format(self.name))else:print("There are still {:d} robots working.".format(Robot.population))def say_hi(self):"""來自機器人的誠摯問候沒問題,你做得到。"""print("Greetings, my masters call me {}.".format(self.name))@classmethoddef how_many(cls):"""打印出當前的人口數量"""print("We have {:d} robots.".format(cls.population))droid1 = Robot("R2-D2") droid1.say_hi() Robot.how_many()droid2 = Robot("C-3P0") droid2.say_hi() Robot.how_many()print("\nRobots can do some work here.\n")print("Robots have finished their work. So let's destroy them.") droid1.die() droid2.die()Robot.how_many()? 輸出:
$ python oop_objvar.py (Initializing R2-D2) Greetings, my masters call me R2-D2. We have 1 robots. (Initializing C-3PO) Greetings, my masters call me C-3PO. We have 2 robots.Robots can do some work here.Robots have finished their work. So let's destroy them. R2-D2 is being destroyed! There are still 1 robots working. C-3PO is being destroyed! C-3PO was the last one. We have 0 robots.? 除了 Robot.popluation ,我們還可以使用 self.__class__.population ,因為每個對象都通過
self.__class__ 屬性來引用它的類。
? 我們使用裝飾器(Decorator)將 how_many 方法標記為類方法。
? 你可以將裝飾器想象為調用一個包裝器(Wrapper)函數的快捷方式,因此啟用@classmethod 裝飾器等價于調用:
how_many = classmethod(how_many)? 你需要記住你只能使用 self 來引用同一對象的變量與方法。這被稱作屬性引用(Attribute Reference)。
? 所有的類成員都是公開的,但是如果使用雙下劃線作為名稱前綴,例如__privatevar,那么Python 會使用名稱調整(Namemangling)來使其有效地成為一個私有變量。
針對 C++/Java/C# 程序員的提示
所有類成員(包括數據成員)都是公開的,并且 Python 中所有的方法都是虛擬的(Virtual)。
繼承
? 面向對象編程的一大優點是對代碼的重用(Reuse),重用的一種實現方法就是通過繼承(Inheritance)機制。繼承最好是想象成在類之間實現類型與子類型(Type and Subtype)關系的工具。
? 對于基類的改動會自動反映到子類型中。在某些情況下可以將子類型對象看作是基類對象,這叫做多態性。
? 案例:
# coding=UTF-8class SchoolMember:'''代表任何學校里的成員'''def __init__(self, name, age):self.name = nameself.age = ageprint('(Initialized SchoolMember: {})'.format(self.name))def tell(self):'''告訴我有關我的細節'''print('Name:"{}" Age:"{}"'.format(self.name, self.age), end=" ")class Teacher(SchoolMember) :'''代表一位老師。'''def __init__(self, name, age, salary):SchoolMember.__init__(self, name, age)self.salary = salaryprint('(Initialized Teacher: {})'.format(self.name))def tell(self):SchoolMember.tell(self)print('Salary: "{:d}"'.format(self.salary))class Student(SchoolMember):'''代表一位學生'''def __init__(self, name, age, marks):SchoolMember.__init__(self, name, age)self.marks = marksprint('(Initialized Student: {})'.format(self.name))def tell(self):SchoolMember.tell(self)print('Marks: "{:d}"'.format(self.marks))t = Teacher('Mrs.Shrividya', 40, 30000) s =Student('Swaroop', 25, 75)#打印一行空白行 print()members = [t, s] for member in members :#對全體師生工作member.tell()? 輸出:
$ python oop_subclass.py (Initialized SchoolMember: Mrs. Shrividya) (Initialized Teacher: Mrs. Shrividya) (Initialized SchoolMember: Swaroop) (Initialized Student: Swaroop)Name:"Mrs. Shrividya" Age:"40" Salary: "30000" Name:"Swaroop" Age:"25" Marks: "75"? 下面這一點很重要,需要牢記——因為我們在 Teacher 和 Student 子類中定義了__init__ 方法,Python 不會自動調用基類 SchoolMember 的構造函數,你必須自己顯式地調用它。
? 相反,如果我們沒有在一個子類中定義一個 init 方法,Python 將會自動調用基類的構造函數。
? 定義子類時,子類名稱后的括號是一個元組,稱作繼承元組。
? 這里有一條有關術語的注釋——如果繼承元組(Inheritance Tuple)中有超過一個類,這種情況就會被稱作多重繼承(Multiple Inheritance)。
總結:
? Python是高度面向對象,所有的東西都可以看作是對象,包括函數。所以有必要清楚的知道類與對象的知識。
輸入與輸出
? 可以用input()獲取用戶輸入,print()打印輸出。
? 對于輸入,我們還可以使用 str (String,字符串)類的各種方法。
? 下面主要是探討文件的輸入與輸出。
用戶輸入內容
? 案例:
def reverse(text):return text[::-1]def is_palindrome(text): # 判斷是否是回文return text == reverse(text)something = input('Enter text: ') if is_palindrome(something) :print('Yes, it is a palindrome') else:print('No, it is not a palindrome')? 輸出:
$ python3 io_input.py Enter text: sir No, it is not a palindrome$ python3 io_input.py Enter text: madam Yes, it is a palindrome$ python3 io_input.py Enter text: racecar Yes, it is a palindrome? input() 函數可以接受一個字符串作為參數,并將其展示給用戶。爾后它將等待用戶輸入內容或敲擊返回鍵。一旦用戶輸入了某些內容并敲下返回鍵, input() 函數將返回用戶輸入的文本。
文件
? 你可以通過創建一個屬于 file 類的對象并適當使用它的 read 、 readline 、 write 方法來打開或使用文件,并對它們進行讀取或寫入。讀取或寫入文件的能力取決于你指定以何種方式打開文件。
? 當完成文件操作后,要調用close()方法關閉文件。
? 案例:
poem = '''\ Programming is fun When the work is done if you wanna make your work also fun:use Python! '''# 打開文件以編輯('w'riting) f = open('poem.txt', 'w') # 向文件中編寫文本 f.write(poem) # 關閉文件 f.close()# 如果沒有特別指定, # 將假定啟用默認的閱讀('r'ead)模式 f = open('poem.txt') while True :line = f.readline()# 零長度指示 EOFif len(line) == 0:break# 每行(`line`)的末尾# 都已經有了換行符#因為它是從一個文件中進行讀取的print(line, end='')#關閉文件 f.close()? 輸出:
$ python3 io_using_file.py Programming is fun When the work is done if you wanna make your work also fun:use Python!? open()函數會以指定的模式打開文件,有關更多詳細的模式,可以用help(open)進行查看。
Pickle
? 可以將Python對象輸出到文件中,并且稍后可以取回。這叫作持久地存儲對象。
? 案例:
import pickle# 我們存儲相關對象的文件的名稱 shoplistfile = 'shoplist.data' # 需要購買的物品清單 shoplist = ['apple', 'mango', 'carrot']# 準備寫入文件 f = open(shoplistfile, 'wb') # 轉儲對象至文件 pickle.dump(shoplist, f) f.close()# 清除 shoplist 變量 del shoplist# 重新打開存儲文件 f = open(shoplistfile, 'rb') # 從文件中載入對象 storedlist = pickle.load(f) print(storedlist)? 輸出:
$ python io_pickle.py ['apple', 'mango', 'carrot']? 要想將一個對象存儲到一個文件中,我們首先需要通過 open 以寫入(write)二進制(binary)模式打開文件,然后調用 pickle 模塊的 dump 函數。這一過程被稱作封裝(Pickling)。
? 接著,我們通過 pickle 模塊的 load 函數接收返回的對象。這個過程被稱作拆封(Unpickling)。
Unicode
? 前面使用和操作字符串的時候都沒關關注編碼問題。
注意:如果你正在使用 Python 2,我們又希望能夠讀寫其它非英語語言,我們需要使用
unicode 類型,它全都以字母 u 開頭,例如 u"hello world"
? zhg: 上述案例中,type()可以查看對象的類型。
? 若需要制定打開文件的編碼是UTF-8,則在open()函數中添加一個關鍵字參數就可以完成。
? 案例:
# encoding=utf-8 import io f = io.open("abc.txt", "wt", encoding="utf-8") f.write(u"Imagine non-English language here") f.close() text = io.open("abc.txt", encoding="utf-8").read() print(text)? 每當我們諸如上面那番使用 Unicode 字面量編寫一款程序時,我們必須確保 Python 程序已經被告知我們使用的是 UTF-8,因此我們必須將 # encoding=utf-8 這一注釋放置在我們程序的頂端。
異常
? 當程序發生錯誤的時候,Python就會通過異常來告訴你哪兒出現了錯誤。
錯誤
>>> Print("Hello World") Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'Print' is not defined >>> print("Hello World") Hello World? 你會注意到一個 NameError 錯誤被拋出,同時 Python 還會打印出檢測到的錯誤發生的位置。這就是一個錯誤錯誤處理器(Error Handler) 為這個錯誤所做的事情。
異常
? 我們將嘗試(Try)去讀取用戶的輸入內容。按下 [ctrl-d] 來看看會發生什么事情。
>>> s = input('Enter something --> ') Enter something --> Traceback (most recent call last): File "<stdin>", line 1, in <module> EOFError處理異常
? 可以通過使用 try…except 來處理異常狀況。一般來說我們會把通常的語句放在 try 代碼塊中,將我們的錯誤處理器代碼放置在 except 代碼塊中。
? 類似于Java的try{}catch(){}機制。
? 案例:
try:text = input('Enter something --> ') except EOFError:print('why did you do an EOF on me?') except KeyboardInterrupt:print('You cancelled the operation. ') else: # 沒有異常發生print('You entered {}'.format(text))? 輸出:
# Press ctrl + d windows下是ctrl + z代表輸入結束 $ python exceptions_handle.py Enter something --> Why did you do an EOF on me? # Press ctrl + c $ python exceptions_handle.py Enter something --> ^CYou cancelled the operation. $ python exceptions_handle.py Enter something --> No exceptions You entered No exceptions? 將所有可能引發異常或錯誤的語句放在 try 代碼塊中,并將相應的錯誤或異常的處理器(Handler)放在 except 子句或代碼塊中。 except 子句可以處理某種特定的錯誤或異常,或者是一個在括號中列出的錯誤或異常。如果沒有提供錯誤或異常的名稱,它將處理所有錯誤與異常。
? 必須至少有一句 except 字句與每一句 try 字句相關聯。不然,有一個 try 代碼塊又有什么意義?
拋出異常
? 可以通過 raise 語句來引發一次異常。
? 你能夠引發的錯誤或異常必須是直接或間接從屬于 Exception (異常) 類的派生類。
? 案例:
# encoding=utf-8class ShortInputException(Exception):'''一個由用戶定義的異常類'''def __init__(self, length, atleast):Exception.__init__(self)self.length = lengthself.atleast = atleasttry:text = input('Enter something -->')if len(text) < 3:raise ShortInputException(len(text), 3)# 其他工作能在此處繼續正常運行 except EOFError:print('Why did you do an EOF on me?') except ShortInputException as ex: # 注意這兒的asprint(('ShortInputException: The input was '+'{0} long, expected ad least {1}').format(ex.length, ex.atleast)) else:print('No exception was raised.')? 輸出:
$ python exceptions_raise.py Enter something --> a ShortInputException: The input was 1 long, expected at least 3$ python exceptions_raise.py Enter something --> abc No exception was raised.Try … Finally
? Try…Finally塊可以確保打開的文件一定被正常的關閉,也就是說無論是否發生異常,finally子句中的代碼塊都會被執行。
? 案例:
import sys import timef = None try:f = open("poem.txt")# 我們常用的文件閱讀風格while True:line = f.readline()if len(line) == 0:breakprint(line, end='')sys.stdout.flush()print("Press ctrl+c now")# 為了確保它能運行一段時間time.sleep(3) except IOError:print("Could not find file poem.txt") except KeyboardInterrupt:print("!! You cancelled the reading from the file.") finally:if f:f.close()print("(Cleaning up: closed the file)")? 輸出:
$ python exceptions_finally.py Programming is fun Press ctrl+c now ^C!! You cancelled the reading from the file. (Cleaning up: Closed the file)with語句
? 在 try 塊中獲取資源,然后在 finally 塊中釋放資源是一種常見的模式。因此,還有一個with 語句使得這一過程可以以一種干凈的姿態得以完成。
? 案例:
with open("poem.txt") as f :for line in f:print(line, end='')? 我們使用的是 open 函數與 with 語句——我們將關閉文件的操作交由 with open 來自動完成。
? 其實是with使用了一個協議,總會在代碼塊開始之前調用 thefile.__enter__ 函數,并且總會在代碼塊執行完畢之后調用 thefile.__exit__ 。
標準庫
? Python 標準庫(Python Standrad Library)中包含了大量有用的模塊,同時也是每個標準的Python 安裝包中的一部分。
Sys模塊
? sys 模塊包括了一些針對特定系統的功能。我們已經了解過 sys.argv 列表中包括了命令行參數。
日志模塊
? logging 模塊可以記錄日志。
? 案例:
import os import platform import loggingif platform.platform().startswith('Windows'):logging_file = os.path.join(os.getenv('HOMEDRIVE'),os.getenv('HOMEPATH'),'test.log') else:logging_file = os.path.join(os.getenv('HOME'),'test.log')print("logging to", logging_file)logging.basicConfig(level=logging.DEBUG,format='%(asctime)s : %(levelname)s : %(message)s',filename=logging_file,filemode='w', )logging.debug("Start of the program") logging.info("Doing something") logging.warning("Dying now")? 輸出:
$ python stdlib_logging.py Logging to /Users/swa/test.log$ cat /Users/swa/test.log 2014-03-29 09:27:36,660 : DEBUG : Start of the program 2014-03-29 09:27:36,660 : INFO : Doing something 2014-03-29 09:27:36,660 : WARNING : Dying now? 我們使用了三款標準庫中的模塊—— os 模塊用以和操作系統交互, platform 模塊用以獲取平臺——操作系統——的信息, logging 模塊用來記錄(Log)信息。
每周模塊系列:標準庫中還有非常多的有用模塊,可以閱讀Doug Hellmann撰寫的優秀的Python Module of the Week或者閱讀Python的官方文檔。
更多
? 前面的部分涵蓋了將使用到的Python的大部分方面,下面介紹一些其他部分,使得對Python的認識更加全面。
傳遞元組
? 你可曾希望從一個函數中返回兩個不同的值?你能做到的。只需要使用一個元組。
>>> def get_error_details(): ... return (2, 'details') ... >>> errnum, errstr = get_error_details() >>> errnum 2 >>> errstr 'details'? 要注意到 a, b = <some expression> 的用法會將表達式的結果解釋為具有兩個值的一個元組。
? 這也意味著在 Python 中交換兩個變量的最快方法是:
>>> a = 5; b = 8 >>> a, b (5, 8) >>> a, b = b, a >>> a, b (8, 5)特殊方法
? 諸如 __init__ 和 __del__ 等一些方法對于類來說有特殊意義。
? 特殊方法用來模擬內置類型的某些行為。如果你希望為你的類使用 x[key] 索引操作(就像你在列表與元組中使用的那樣),那么你所需要做的只不過是實現__getitem__() 方法,然后你的工作就完成了。如果你試圖理解它,就想想 Python 就是對list 類這樣做的!
? 如果想了解更多的特殊方法,可以參閱Python手冊
單語句塊
? 每個語句塊都是由其自身的縮進級別與其他部分進行區分。如果你的語句塊只有單獨的一句,那么你可以在同一行指定它,例如條件語句與循環語句。
? 案例:
>>> flag = True >>> if flag: print('Yes') ... Yes? 注意,單個語句是在原地立即使用的,它不會被看作一個單獨的塊。雖然這個方式可以使得程序更加小巧,但是除非是為了檢查錯誤,建議避免使用這種快捷方式。
Lambda表格
? lambda 語句可以創建一個新的函數對象。從本質上說, lambda 需要一個參數,后跟一個表達式作為函數體,這一表達式執行的值將作為這個新函數的返回值。
? 案例:
points = [{'x': 2, 'y': 3},{'x': 4, 'y': 1}] points.sort(key=lambda i : i['y']) print(points)? 輸出:
$ python more_lambda.py [{'y': 1, 'x': 4}, {'y': 3, 'x': 2}]? 要注意到一個 list 的 sort 方法可以獲得一個 key 參數,用以決定列表的排序方式(通常我們只知道升序與降序)。在我們的案例中,我們希望進行一次自定義排序,為此我們需要編寫一個函數,但是又不是為函數編寫一個獨立的 def 塊,只在這一個地方使用,因此我們使用 Lambda 表達式來創建一個新函數。
列表推導
? 列表推導(List Comprehension)用于從一份現有的列表中得到一份新列表。想象一下,現在你已經有了一份數字列表,你想得到一個相應的列表,其中的數字在大于 2 的情況下將乘以2。列表推導就是這類情況的理想選擇。
? 案例:
listone = [2, 3, 4] listtwo = [2*i for i in listone if i > 2] print(listtwo)? 輸出:
$ python more_list_comprehension.py [6, 8]? 在本案例中,當滿足了某些條件時( if i > 2 ),我們進行指定的操作( 2*i ),以此來獲得一份新的列表。要注意到原始列表依舊保持不變。
? 使用列表推導的優點在于可以減少代碼。
在函數中接收元組與字典
? 有一種特殊方法,即分別使用 * 或 ** 作為元組或字典的前綴,來使它們作為一個參數為函數所接收。當函數需要一個可變數量的實參時,這將頗為有用。
>>> def powersum(power, *args): ... '''Return the sum of each argument raised to the specified power.''' ... total = 0 ... for i in args: ... total += pow(i, power) ... return total ... >>> powersum(2, 3, 4) 25 >>> powersum(2, 10) 100? 我們在 args 變量前添加了一個 * 前綴,函數的所有其它的額外參數都將傳遞到args 中,并作為一個元組予以儲存。如果采用的是 ** 前綴,則額外的參數將被視為字典的鍵值—值配對。
assert語句
? assert 語句用以斷言(Assert)某事是真的。
? 當assert判斷事實為真時,則會繼續執行后續代碼,如果判斷其不是真的,那么就會拋出一個錯誤。當語句斷言失敗時,將會拋出 AssertionError 。
? 案例:
>>> mylist = ['item'] >>> assert len(mylist) >= 1 >>> mylist.pop() 'item' >>> assert len(mylist) >= 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError? zhg: 自己的理解,這個assert就是用來驗證自己的猜測和假設的,如果猜測和假設是真的,那么后續的基于假設和猜測的邏輯就不會有有問題,那么assert會通過,并繼續執行后續代碼;如果猜測和假設是錯誤的,那么后續的基于假設和猜測的邏輯就是有問題的,那么assert就不會通過,則會拋出錯誤,進而阻止后續代碼的運行。
裝飾器
? 裝飾器(Decorators)是應用包裝函數的快捷方式。這有助于將某一功能與一些代碼一遍又一遍地“包裝”。
? 案例:
from time import sleep from functools import wraps import logginglogging.basicConfig() log = logging.getLogger("retry")def retry(f):@wraps(f)def wrapped_f(*args, **kwargs):MAX_ATTEMPS = 5for attempt in range(1, MAX_ATTEMPS + 1):try:return f(*args, **kwargs)except:log.exception("Attemp %s/%s failed :%s",attempt,MAX_ATTEMPS,(args, kwargs))sleep(10 * attempt)log.critical("All %s attemps failed : %s",MAX_ATTEMPS,(args, kwargs))return wrapped_fcounter = 0@retry def save_to_database(arg):print("Write to a database or make a network call or etc.")print("This will be automatically retried if exception is thrown.")global countercounter += 1# 這將在第一次調用時拋出異常# 在第二次運行時將正常工作(也就是重試)if counter < 2:raise ValueError(arg)if __name__ == '__main__':save_to_database("Some bad value")? 輸出:
$ python more_decorator.py Write to a database or make a network call or etc. This will be automatically retried if exception is thrown. ERROR:retry:Attempt 1/5 failed : (('Some bad value',), {}) Traceback (most recent call last):File "more_decorator.py", line 14, in wrapped_freturn f(*args, **kwargs)File "more_decorator.py", line 39, in save_to_databaseraise ValueError(arg) ValueError: Some bad value Write to a database or make a network call or etc. This will be automatically retried if exception is thrown.總結
以上是生活随笔為你收集整理的《简明Python教程》学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数组中的forEach和map的区别
- 下一篇: UVA 213 Message Deco