看完了莫煩Python的視頻,對於Python有了一點感覺,接下來打算把小甲魚的視頻啃完,附上學習網址:http://blog.fishc.com/category/python
小甲魚的視頻是從零基礎開始的,所以這篇筆記補充一些注意點和新知識點,有很多內容在前面的筆記中已經提及:《Python初學基礎》,在這里就不再贅述啦。
1 Python & IDLE
Python 是什么類型的語言?
Python是腳本語言
腳本語言(Scripting language)是電腦編程語言,因此也能讓開發者藉以編寫出讓電腦聽命行事的程序。以簡單的方式快速完成某些復雜的事情通常是創造腳本語言的重要原則,基於這項原則,使得腳本語言通常比 C語言、C++語言或 Java 之類的系統編程語言要簡單容易。
也讓腳本語言另有一些屬於腳本語言的特性:
- 語法和結構通常比較簡單
- 學習和使用通常比較簡單
- 通常以容易修改程序的“解釋”作為運行方式,而不需要“編譯”
- 程序的開發產能優於運行性能
一個腳本可以使得本來要用鍵盤進行的相互式操作自動化。一個Shell腳本主要由原本需要在命令行輸入的命令組成,或在一個文本編輯器中,用戶可以使用腳本來把一些常用的操作組合成一組串行。主要用來書寫這種腳本的語言叫做腳本語言。很多腳本語言實際上已經超過簡單的用戶命令串行的指令,還可以編寫更復雜的程序。
IDLE是什么?
IDLE是一個Python Shell,shell的意思就是“外殼”,基本上來說,就是一個通過鍵入文本與程序交互的途徑!像我們Windows那個cmd窗口,像Linux那個黑乎乎的命令窗口,他們都是shell,利用他們,我們就可以給操作系統下達命令。同樣的,我們可以利用IDLE這個shell與Python進行互動。
2 BIF
BIF就是Built-in Functions,內置函數。為了方便程序員快速編寫腳本程序,Python提供了非常豐富的內置函數,我們只需要直接調用即可,不是內置函數則需通過import語句調入擴展包后才能使用。在IDLE下鍵入dir(__builtins__)即可查看所有的內置函數,help(內置函數名)則可產看函數的使用方法。
3 變量和字符串
變量:在Python中變量不需要事先聲明,但是需要先賦值后再使用,變量更像是貼在值上的標簽,這給Python帶來了很大便捷。變量的名字和C及java編程語言一樣,由字母、數字、下划線組成,且首位不允許是數字,而且變量名最好能專業些,讓人一看就知道其含義。
字符串:用單引號或者雙引號均可,如果一個字符串本身又包含字符串,可使用轉義字符'\',對於想要將其作為一個字符處理的單引號或者雙引號而言,前面只要加上轉義字符就可以了。
例如打印Let's go! 下述兩種方法均可
print("Let's go!") print('Let\'s go!')
原始字符串:對於頻繁使用到反斜杠的特殊字符串,一個一個去添加轉義字符顯得繁瑣,可在字符串前面加上一個‘r’,這就是原始字符串的含義。例如字符串C:\Program File\Intel\WiFi\Help,定義只要r'C:\Program File\Intel\WiFi\Help',它的實現方法其實很簡單,就是程序會自動對每一個反斜杠前面再加一個反斜杠。但是r不能對末尾是反斜杠的字符串進行定義,'C:\Temp\'。這個時候如果非要使用r進行原始字符串操作的話可以采用如下形式解決:r'C:\Temp' '\\' 或者r'C:\Temp'+'\\'。
對於多行的較長的字符串,可以通過三引號,單引號或者雙引號均可。
與列表和元組一樣,字符串也可以進行分片:
字符串和元組一樣,里面的元素是不能進行修改和刪除的。
字符串內置函數用法可參考:《字符串的方法及注釋》
字符串的format函數,通過花括號{}來表明字符串中需要格式化的值,{}內的值可以是位置參數(0,1,2,3等)或關鍵字參數
例如:
使用關鍵字參數時必須賦值,關鍵字和位置參數可以混用,但是位置參數應放在左邊。
在字符串中花括號表示的內容是要被替換的,如果想讓花括號和其中的內容作為一個正常字符串處理的話,必須在外面再加上一層花括號,原理和轉義字符前加上轉義字符一樣,例:
位置參數直接跟‘:’表明后面要跟一個字符串格式化操作符,例:
字符串格式化操作符可參考:《字符串格式化符號含義及轉義字符含義》
4 猜數字小游戲
之前第二講所設計的文字小游戲如下所示,此節對該代碼進行改進增加功能:
- 猜錯的時候給提示,例如大了還是小了(條件分支)
- 每運行一次程序只能猜一次,應提供多次機會給用戶猜測(while循環)
- 每次運行程序,答案可以是隨機的(random模塊)
input是內置函數,返回的是字符串類型,用於if判斷時類型強制轉換為int型
print('----------------游戲開始---------------') temp = input('猜猜看我現在心里想的是哪個數字:') guess = int(temp) if guess == 6: print('猜對啦!') print('但是沒有獎勵哈哈哈') else: print('猜錯啦,我心里想的是6') print('游戲結束,不玩啦')
首先完成第一個功能,添加if條件分支:
print('----------------游戲開始---------------') temp = input('猜猜看我現在心里想的是哪個數字:') guess = int(temp) if guess == 6: print('猜對啦!') print('但是沒有獎勵哈哈哈') elif guess > 6: print('比我心里想的數字大!') else: print('比我心里想的數字小!') print('游戲結束,不玩啦')
第二步通過while循環實現未猜中時繼續游戲:
temp = input('猜猜看我現在心里想的是哪個數字:') guess = int(temp) print('----------------游戲開始---------------') while guess != 6: temp = input('猜錯了,請重新輸入吧:') guess = int(temp) if guess == 6: print('猜對啦!') print('但是沒有獎勵哈哈哈') if guess > 6: print('比我心里想的數字大!') else: print('比我心里想的數字小!') print('游戲結束,不玩啦')
此外,還要求游戲機會只有三次,最后一個功能import模塊random,通過其中的randint來產生隨機數,代碼如下所示:
import random secret = random.randint(1,10) print('----------------游戲開始---------------') times = 3 temp = input('猜猜看我現在心里想的是哪個數字,注意只有三次機會哦:') guess = int(temp) times -= 1
if guess == secret: print('猜對啦!') print('但是沒有獎勵哈哈哈') else: while (guess!=secret) and (times > 0): if guess > secret: print('比我心里想的數字大!') else: print('比我心里想的數字小!') temp = input('猜錯了,請重新輸入吧:') guess = int(temp) times -= 1
if guess == secret: print('恭喜,猜對啦!') if times == 0: print('三次機會已用光') print('游戲結束,不玩啦')
或者:
import random secret = random.randint(1,10) times = 3
print('----------------游戲開始---------------') guess = 0 print('猜猜看我現在心里想的是哪個數字,注意只有三次機會哦:',end=' ') while (guess!=secret) and (times > 0): temp = input() guess = int(temp) times -= 1
if guess == secret: print('猜對啦!') print('但是沒有獎勵哈哈哈') elif guess > secret: print('比我心里想的數字大!') else: print('比我心里想的數字小!') if times > 0: print('再試一次吧:',end=' ') else: print('三次機會已用光') print('游戲結束,不玩啦')
這種行為被稱為短路邏輯(short-circuit logic)或者惰性求值(lazy evaluation),這種行為同樣也應用與 or 操作符。
5 數據類型
5.1 基本數據類型
(1)整型,在Python3中長整形和整形歸為一類,所有的整數都屬於整型,例如1,0,1000,1203等等
(2)浮點型,數字中有小數點的數,如12.1 1.85 10.0 等等,另外1e10也表示浮點數
(3)字符串,所有以單引號或雙引號括起來的值都叫做字符串
(4)布爾類型, 布爾類型只有兩個值,True 或False ,記得在Python里面這兩個值首字母均大寫。
5.2 數據類型轉換
整型強制轉換函數 int(),可以將純數字的字符串或浮點型強制轉換為整形,如int('0012')為12; int(12.5)為12
字符串強制類型轉換函數str(),可以將任何類型轉換為字符串類型,例如str(1e28)為‘1e+28' str(1e2)為'100.0'等等
浮點型強制轉換函數float(),可以將包含一個小數點的數字字符串或者整形轉換為浮點型,如float('0012')為12.0,float('.123')為0.123
5.3 數據類型信息獲取
函數type(),直接給出數的數據類型,例type(False)返回bool,函數isinstance()需要給出需要判斷的數和一個數據類型,是則返回True,否則返回False,如isinstace(1,int)返回True,isinstance('I love u',float)返回False,而isinstance('I love u',str)返回True
補充字符串的一些功能:
s為字符串
s.isalnum() 所有字符都是數字或者字母,為真返回 Ture,否則返回 False。
s.isalpha() 所有字符都是字母,為真返回 Ture,否則返回 False。
s.isdigit() 所有字符都是數字,為真返回 Ture,否則返回 False。
s.islower() 所有字符都是小寫,為真返回 Ture,否則返回 False。
s.isupper() 所有字符都是大寫,為真返回 Ture,否則返回 False。
s.istitle() 所有單詞都是首字母大寫,為真返回 Ture,否則返回 False。
s.isspace() 所有字符都是空白字符,為真返回 Ture,否則返回 False。
6 常用操作符
算數運算符: 加(+),減(-),乘(*),除(/),冪運算(**),地板除(//)
/和//的區別
在Python中的除運算符與其它程序語言的不太一樣,/表示真正的除號,例如1/3=0.3333333333333333,而4/2的值為2.0。說明兩個數相除的值是一個浮點數,而其它程序語言/卻表示兩個整數相除,只返回一個整數。Python后來為了兼容這種用法,引入了//來實現。例如3//2的值為1,而3.0//2的值為1.0,且3//2.0的值也為1.0。說明在Python中//符號兩邊同為整數時的值才為整數,否則則為一個浮點數(后面帶'.0')。
邏輯運算符:and or not,優先級為not>and>or
比較運算符:< <= > >= == !=
7 分支和循環
三目運算符:形式為 x if 條件表達式 else y,先判斷條件表達式真假,真則取x的值,否則取y的值。舉個例子 3 if 4>3 else 4 值為3,而3 if 4<3 else 4 的值為4
斷言語句assert:assert后面跟一個條件表達式,如果條件表達式的值為假是程序自動崩潰並報異常AssertionError,如果為真則繼續執行后面的語句,主要用於在程序中植入檢查點,只有assert后面的條件永遠為真時程序才能正常運行,否則就崩潰。例如 assert 3>4,程序就會執行這一條語句后崩潰並報出異常,而assert 4>3則正常執行
8 列表
Python列表的含義和其他程序語言中的數組類似,由一系列元素組成並且可以通過下標進行訪問,不同的是,列表里面的元素可以是任何數據類型,甚至可以是一個列表。
在列表內預先定義好的函數叫方法,下面介紹三種插入新元素的方法:append(),extent(),insert(),需要列表名加成員操作符.以及方法名來使用
8.1 插入新元素
(1)append()函數
一次添加一個元素到列表末尾,調用方式為列表.append(新元素)
(2)extend()函數
一次添加多個元素到列表末尾,括號內的參數是列表,調用方式為列表.extend(要加入的列表)
(3)insert()函數
可將元素加入到列表中的特定位置,注意列表元素的位置是從0開始的,調用方式為列表.insert(位置,元素)
8.2 從列表中刪除元素
(1)remove()
參數是一個元素,列表.remove(元素),如果元素不在列表中,將報錯
(2)del
直接刪除一個元素值,可以直接通過索引值獲得需要刪除的元素,del 列表名[索引值]
如果執行del 列表名,則列表將被刪除
(3)pop()
不給出參數則直接返回列表中最后一個元素並從列表中刪除
a = [1,2,3,4,1,1,-1] b = a.pop()
執行上述語句后,b = -1,a = [1,2,3,4,1,1]
也可以指定索引值,直接返回索引值對應元素並從列表中刪除
8.3 列表分片
當需要從列表一次性取出多個元素時,需要通過列表分片的方式來實現,基本形式是列表名[左索引值:右索引值:步長],(指定右索引值時不包括該元素)左右索引值及步長都可以忽略,左索引值忽略時表明列表元素從0開始,右索引值省略表示包括索引值右邊的所有元素,步長省略默認為1。
a[1:3],輸出[2, 3]
a[:3],輸出[1, 2, 3]
a[1:],輸出[2, 3, 4, 1, 1, -1]
a[:]得到列表的拷貝
8.4 列表拷貝
拷貝方法:
(1)已知列表名直接賦值給一個變量
(2)通過分片操作拷貝
(3)通過列表內置方法copy()賦值
(1)兩個變量指向同一個數據存儲空間,存儲空間內數值變化,則兩者均改變,(2)(3)相當於重新建立了一個存儲空間,里面的數據不受其他空間內數值變化影響。
8.5 列表中的常用操作符
1)比較運算符
列表可以直接比較大小,從第0個元素開始比較
2)邏輯運算符
3)拼接運算符
+不能進行新元素的操作,兩邊的必須都是列表
4)重復
5)成員關系操作符
in和not in
8.6 內置方法
着重介紹reverse()和sort()方法
reverse將所有元素倒置
L.sort(key=None,reverse=False)默認是從小到大排序,若想從大到小,則將reverse的值改為True
8.7 列表推導式/列表解析簡介
列表推導式(list comprehensions)也叫列表解析,靈感取自於函數式變成語言Haskell,可以用來動態創建列表,一般形式如下:
[有關A的表達式 for A in B],例:
9 元組
9.1 創建和訪問元組
元組和列表類似,但是元組一旦創建,元組內的元素不允許修改和刪除,這是元組和列表最大的區別。元組的標示是()
當元組中僅有一個元素時,需要在元素后面加上逗號,例:
只有加上逗號時,類型才是一個tuple,甚至不需要括號,僅僅有逗號就行,即t=1,再如:
元組的訪問和列表一樣,都是元組名[索引值]
9.2 更新和刪除元組
雖然元組本身不允許修改和刪除,但是和字符串一樣,可以對原元組的元素進行操作並生成一個新的元組,例:
通過分片和拼接,其實是生成了一個新的元組,賦值給了t,原來的元組還存在,但是不再叫t了,將會被Python內存垃圾回收。
可以通過del語句直接刪除一個元組,但是不允許利用del刪除元組的一個元素
但同樣可以通過分片和拼接來實現某個元素的刪除:
同樣,原來的元組還存在,但是不再叫t了,將會被Python內存垃圾回收。
10 函數
函數的定義和參數的使用可參考《Python初學基礎》,這里補充一些新的知識點。
10.1 全局變量
在python中可以整個代碼內訪問全局變量,但是不要試圖在函數內部去修改它。如果在函數內修改全局變量的值,Python會使用屏蔽(Shadowing)的方式保護全局變量,將會在函數內部自動創建一個新的局部變量,名字和全局變量相同。二者互不影響,因為存儲空間是不一樣的。這樣在函數內部所修改的其實是局部變量,而不會影響到全局變量。
舉例說明:
def discounts(price,rate): final_price = price * rate old_price = 50
print('修改后old_price的值1:',old_price) return final_price old_price = float(input('請輸入原價:')) rate = float(input('請輸入折扣率:')) new_price = discounts(old_price,rate) print('修改后old_price的值2:',old_price) print('打折后的價格是:',new_price)
運行結果:
在函數內訪問全局變量old_price,並試圖去修改它,可發現輸出的值1已經改變,而在函數外打印的值2依然沒有改變,仍然等於調用函數時所賦的100。在函數體內的操作並沒有改變全局變量的值。
如果一定要在函數內部對全局變量進行修改,可在函數內部先用global關鍵字聲明該變量為全局變量。
10.2 閉包
Python中的閉包從表現形式上定義為:如果在一個內部函數里,對在外部作用域(但不是在全局作用域)的變量進行引用,那么內部函數就被認為是閉包(closure)。
閉包是一種滿足特定要求的內嵌函數,這里講內嵌函數稱為子函數,其外部函數稱為母函數,則當子函數體內有對母函數體內定義的變量的引用時我們稱這個子函數為一個閉包。但當母函數和其閉包都定義了參數時,由於在母函數體外是無法直接對閉包進行函數調用的,為了能夠實現對閉包的調用,需要在母函數內增加一條返回閉包函數名本身的語句,這個時候調用母函數后返回的是一個閉包的函數對象,就可以通過這種方法間接調用閉包函數了。例:
上述函數還可以使用funx(5)(8)進行調用。
無法直接在閉包內部對外部函數的變量進行修改,但是如果非要修改的話,在Python3中是可以的,需要增加一條聲明變量是外部函數內變量的語句nonlocal
如上所示,若在fun2中直接調用fun1的x會報錯,加上nonlocal后可以正常運行。
10.3 lambda表達式
lambda關鍵字用來創建匿名函數(隱函數),定義形式:形參:返回值表達式,相對於一般函數而言具有以下優勢:
(1)免去了函數定義過程,代碼變得更加精簡
(2)省卻定義函數名過程
(3)省去了返回到函數定義出閱讀代碼過程,提高了代碼的可讀性
如下所以,lambda表達式也可對多個形參進行處理
10.4 兩個內置函數filter()和map()
(1)filter()
filter()函數實現過濾功能,有兩個參數,第一個參數為None或一個函數對象,第二個參數為一個可迭代的對象,當第一個參數為None時,實現將可迭代對象的每一個元素值為False的過濾掉,生成一個僅包含元素值為True的新可迭代對象
如下所示,將值為False過濾,留下值為True的值
還可以通過自定義function來實現不同的功能,如下所示為輸出0-9的奇數
也可通過lambda表達式來直接定義
(2)map()
map()函數與filter函數形式一致,實現對可迭代對象的映射,但其第一個參數必須是一個函數對象,而且map()函數是將經過函數對象處理后的返回值作為新的元素組成一個可迭代對象的,舉例說明:
11 遞歸
遞歸就是函數通過return語句實現自己調用自己的過程。
Python3中針對遞歸提供了程序保護機制,默認允許的遞歸深度是100層,而如果我們使用網絡爬蟲等需要遠遠超過百次的遞歸層次時,就需要去修改程序默認的遞歸深度以滿足要求。設置遞歸深度的函數在sys模塊里面的settrecursionlimit()函數進行設置,如將遞歸層次設置為110層:
import sys sys.setrecursionlimit(110)
11.1 階乘
如下為不采用遞歸的方式來實現一個數的階乘
def factorial(n): result = n for i in range(1,n): result *= i return result number = int(input('請輸入一個正整數:')) result = factorial(number) print('%d的階乘是:%d'%(number,result))
采用遞歸方式如下:
def factorial(n): if n == 1: return 1
else: return n * factorial(n-1) number = int(input('請輸入一個正整數:')) result = factorial(number) print('%d的階乘是:%d'%(number,result))
大多數問題使用遞歸實現起來更高效,在某些特殊場合合理的使用會使得你的代碼精簡且高效,但是遞歸是函數調用自身,調用函數需要入棧出棧,對於內存和CPU的消耗還是比較大的,對於上述求階乘的例子,當輸入一個比較大的數的時候采用遞歸消耗比較大,反而迭代會比遞歸來得更有效率。
11.2 斐波那契(Fibonacci)數列
如果說兔子在出生兩個月后就有繁殖能力,在擁有繁殖能力后,這對兔子每個月能生出一對小兔子,假設所有的兔子都不會死去,那么一年之后可以繁殖多少對兔子呢?
小兔子每月的總對數呈Fibonacci數列形式:
月份 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
兔子總對數 | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | 55 | 89 | 144 |
將兔子總對數與月份n的關系歸納為數學公式則為
F(n) = 1 n=1
1 n=2
F(n-1)+F(n-2) n>2
則接着我們采用迭代和遞歸的方式來計算20個月后兔子的總對數
(1)迭代方式:
def fab(n): n1 = 1 n2 = 1 n3 = 0 if n < 1: print('輸入有誤!') return -1
while(n-2) > 0: n3 = n1 + n2 n1 = n2 n2 = n3 n -= 1
return n3 number = int(input('請輸入月份:')) sum = fab(number) if sum != -1: print('%d個月后總共有%d對小兔子'%(number,sum))
(2)遞歸方式:
def fab(n): sum = 0 if n < 1: print('輸入有誤!') return -1
if n == 1 or n == 2: sum = 1
else: sum = fab(n-1)+fab(n-2) return sum number = int(input('請輸入月份:')) sum = fab(number)
if sum != -1:
print('%d個月后總共有%d對小兔子'%(number,sum))
當月份數字比較小的時候,兩者運行的速度都比較快,但是當月份數目比較大的時候,遞歸的效率明顯變慢,運行結果如下:
遞歸是一個雙刃劍,用好了能解決很棘手的問題,但是用不好會使得程序的執行效率降低。
11.3 漢諾塔
漢諾塔:漢諾塔(又稱河內塔)問題是源於印度一個古老傳說的益智玩具。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞着64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。遞歸算法的思想是“分制”方法,即將一個復雜問題分解為幾個較復雜問題,再繼續將較負責問題分解為次較復雜問題,繼續將次較復雜問題往下分解直到分解為一個能直接解決的簡單問題。采用遞歸方法解決漢諾塔游戲的思路如下:將n個盤子從x柱子轉移到z柱子問題分解為三個問題:
(1)將x柱子頂上的n-1個盤子借助z轉移到y柱子上
(2)將x柱子最后一個盤子轉移到z柱子上
(3)將y柱子上的n-1個盤子借助x轉移到z柱子上
依次將問題分解下去,以64為例
(1)可以繼續分解為:
11)將前62個盤子從x移動到z上
12)將最底下的第63個盤子移動到y上
13)將z上的62個盤子移動到y上同理(3)也可以繼續分解,代碼如下所示:
def hanoi(n,x,y,z): #n代表盤子數量,x,y,z代表相應柱子
global count #聲明為全局變量,統計一共要移動盤子的次數
count += 1
if n == 1: print('%c -> %c'%(x,z)) else: hanoi(n-1,x,z,y) #將n-1個盤子從x借助z移動到y
print('%c -> %c'%(x,z)) #將最后一個盤子從x移動到z
hanoi(n-1,y,x,z) #將n-1個盤子從y借助x移動到z
count = 0 number = int(input('請輸入盤子總數:')) hanoi(number,'x','y','z') print('一共需要移動盤子的次數為%d次!'%count)
如下所示為自己繪制的以3為例的程序流程: