a=['1','2','3'] b=[int(i) for i in a] print(b)
輸出爲:[1, 2, 3]
位和字節的關係?php
b、B、KB、MB、GB 的關係?html
1024B=1K(千)B
1024KB=1M(兆)B
1024MB=1G(吉)B
1024GB=1T(太)B node
什麼是PEP8?python
PEP8是一個編程規範,內容是一些關於如何讓你的程序更具可讀性的建議。
其主要內容包括代碼編排、文檔編排、空格的使用、註釋、文檔描述、命名規範、編碼建議等。linux
Pickle模塊讀入任何Python對象,將它們轉換成字符串,而後使用dump函數將其轉儲到一個文件中——這個過程叫作pickling。
反之從存儲的字符串文件中提取原始Python對象的過程,叫作unpickling。nginx
(1)二進制轉換成十進制:v = 「0b1111011」 git
#先將其轉換爲字符串,再使用int函數,指定進制轉換爲十進制。 print(int("0b1111011",2)) 值爲123
(2)十進制轉換成二進制:v = 18 程序員
print("轉換爲二進制爲:", bin(18)) #轉換爲二進制爲: 0b10010
(3)八進制轉換成十進制:v = 「011」 github
print(int("011",8)) #9
(4)十進制轉換成八進制:v = 30web
print("轉換爲八進制爲:", oct(30)) #轉換爲八進制爲: 0o36
(5)十六進制轉換成十進制:v = 「0x12」
print(int("0x12",16)) #18
(6)十進制轉換成十六進制:v = 87
print("轉換爲十六進制爲:", hex(87)) 轉換爲十六進制爲: 0x57
a=['1','2','3'] b=[int(i) for i in a] print(b)
輸出爲:[1, 2, 3]
def fab(n): if n == 1: return 1 else: return fab(n-1)+ n print (fab(998)) #獲得的最大數爲998,之後就是報錯了,998這個數值莫名想起廣告詞····
import sys sys.setrecursionlimit(100000) def foo(n): print(n) n += 1 foo(n) if __name__ == '__main__': foo(1) #獲得的最大數字在3922-3929之間浮動,這個是和計算機有關係的,將數字調到足夠大了,已經大於系統堆棧,python已經沒法支撐到太大的遞歸崩了。
一、求結果
v=dict.fromkeys(['k1','k2'],[])
v['k1'].append(666)
print(v)
v['k1'] = 777
print(v)
v=dict.fromkeys(['k1','k2'],[]) v['k1'].append(666) print(v) #{'k2': [666], 'k1': [666]} v['k1'] = 777 print(v) #{'k2': [666], 'k1': 777}
二、求結果
def num():
return [lambda x:i*x for i in range(4)]
print(m(2) for m in num())
def num(): return [lambda x:i*x for i in range(4)] print(m(2) for m in num())# <generator object <genexpr> at 0x0000000000B2FA40> 爲元祖 print(list(m(2) for m in num())) # [6, 6, 6, 6]
三、求結果
a、[i%2 for i in range(10)]
b、( i % 2 for i in range(10) )
a=[i%2 for i in range(10) ] print(a) # 由於 [] 爲列表 因此會有結果爲[0, 1, 0, 1, 0, 1, 0, 1, 0, 1] b=(i%2 for i in range(10)) print(b) # 由於()爲元祖 因此會有結果爲 <generator object <genexpr> at 0x0000000000645D00> c=list(b) # 將元祖轉換格式爲列表 print(c) # 打印c,結果爲 [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
四、求結果:
a. 1 or 2
b. 1 and 2
c. 1 < (2==2)
d. 1 < 2 == 2
print(1 or 2) # 1 print(1 and 2) # 2 print(1<(2==2)) # False 由於2==2爲True,而True表示爲1,False表示爲0,因此1<1,結果爲False print(1<2==2) # True python是容許連續比較1<2==2意思是1<2且2==2
v2 = 1 and 3 #3 v3 = 0 and 2 and 1 #0 v4 = 0 and 2 or 1 #1 v5 = 0 and 2 or 1 or 4 #1 v6 = 0 or False and 1 #False
ASCII碼使用一個字節編碼,因此它的範圍基本是隻有英文字母、數字和一些特殊符號 ,只有256個字符。
在表示一個Unicode的字符時,一般會用「U+」而後緊接着一組十六進制的數字來表示這一個字符。在基本多文種平面(英文爲 Basic Multilingual Plane,簡寫 BMP。它又簡稱爲「零號平面」, plane 0)裏的全部字符,要用四位十六進制數(例如U+4AE0,共支持六萬多個字符);在零號平面之外的字符則須要使用五位或六位十六進制數了。舊版的Unicode標準使用相近的標記方法,但卻有些微的差別:在Unicode 3.0裏使用「U-」而後緊接着八位數,而「U+」則必須隨後緊接着四位數。
Unicode可以表示全世界全部的字節
GBK是隻用來編碼漢字的,GBK全稱《漢字內碼擴展規範》,使用雙字節編碼。
UTF-8(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字符編碼,又稱萬國碼。由Ken Thompson於1992年建立。如今已經標準化爲RFC 3629。UTF-8用1到6個字節編碼UNICODE字符。用在網頁上能夠同一頁面顯示中文簡體繁體及其它語言(如英文,日文,韓文)。
字節碼
字節碼是一種中間碼
字節碼一般指的是已經通過編譯,但與特定機器碼無關,須要直譯器轉譯後才能成爲機器碼的中間代碼。字節碼一般不像源碼同樣可讓人閱讀,而是編碼後的數值常量、引用、指令等構成的序列。
字節碼主要爲了實現特定軟件運行和軟件環境、硬件環境無關。字節碼的實現方式是經過編譯器和虛擬機器。編譯器將源碼編譯成字節碼,特定平臺上的虛擬機器將字節碼轉譯爲能夠直接執行的指令。字節碼的典型應用爲Java語言。
總結:字節碼是一種中間狀態(中間碼)的二進制代碼(文件)。須要直譯器轉譯後才能成爲機器碼。
機器碼
機器碼就是計算機能夠直接執行,而且執行速度最快的代碼。
用機器語言編寫程序,編程人員要首先熟記所用計算機的所有指令代碼和代碼的涵義。手編程序時,程序員得本身處理每條指令和每一數據的存儲分配和輸入輸出,還得記住編程過程當中每步所使用的工做單元處在何種狀態。這是一件十分繁瑣的工做,編寫程序花費的時間每每是實際運行時間的幾十倍或幾百倍。並且,編出的程序全是些0和1的指令代碼。
機器語言是微處理器理解和使用的,用於控制它的操做二進制代碼。
8086到Pentium的機器語言指令長度能夠從1字節到13字節。
儘管機器語言好像是很複雜的,然而它是有規律的。
存在着多至100000種機器語言的指令。這意味着不能把這些種類所有列出來。
總結:機器碼是電腦CPU直接讀取運行的機器指令,運行速度最快
三元運算符的功能與「if...else」流程語句一致,它在一行中書寫,代碼很是精練、執行效率更高。在PHP程序中恰當地使用三元運算符可以令腳本更爲簡潔、高效。代碼格式以下:
(expr1) ? (expr2) : (expr3);
解釋:若是條件「expr1」成立,則執行語句「expr2」,不然執行「expr3」。
實現一樣的功能,若使用條件流程語句,就須要書寫多行代碼:
if(expr1) { expr2; } else { expr3; }
可見,前面所說的三元運算符之好並不是誇張。但是,多數狀況下咱們只在代碼較爲簡單的時候使用三元運算符,即執行語句只爲單句的時候。如:
$a>$b ? print "a大於b" : print "a小於b";
事實上,三元運算符能夠擴展使用,當設置的條件成立或不成立,執行語句均可以不止一句,試看如下格式:
(expr1) ? (expr2).(expr3) : (expr4).(expr5);
咱們很是明顯地看到,多個執行語句能夠使用用字符串運算符號(「.」)鏈接起來,各執行語句用小角括號包圍起來以代表它是一個獨立而完整的執行語句。這樣擴展後它的功能更爲逼近「if...else」流程語句。
同時三元運算符還能夠嵌套使用。例如,a大於b成立時:若是a小於c,那麼x=c-a不然x=a-c;不然a小於b成立時:若是b小於c,那麼x=c-b不然x=b-c:
$a>$b ? $x=($a<$c ? $c-$a : $a-$c) : $x=($b<$c ? $c-$b : $b-$c);
嵌套使用的三元運算符可讀性不太好,往後對代碼的維護很可能存在問題,但比起「if...else」之類的流程語句,在上述情形之下,它的確太簡練了,這是它的誘人之處。
對於喜歡偷懶和追求代碼簡潔的人來講,用三元運算符取代if流程語句應該是絕佳的選擇。即便不用考慮「三元」中條件句之外的任意某一「元」,使用三元運算符仍然比if語句簡練。如下語句的語法是正確的,它們以小解引號的方式忽略了第二或第三「元」:
$a>$b ? print "Yes" : ""; $a>$b ? '': print 'No';
應該注意的是:在使用三元運算符時,建議使用print語句替代echo語句。
這個也是python彪悍的特性.
自省就是面向對象的語言所寫的程序在運行時,所能知道對象的類型.簡單一句就是運行時可以得到對象的類型.好比type(),dir(),getattr(),hasattr(),isinstance().
(1)Print
在 Python 2 中, print 被視爲一個語句而不是一個函數,這是一個典型的容易弄混的地方,由於在 Python 中的許多操做都須要括號內的參數來執行。若是在 Python 2 中你想要你的控制檯輸出 」Sammy the Shark is my favorite sea creature」,你應該寫下這樣的 print 語句:
print "Sammy the Shark is my favorite sea creature"
在使用 Python 3 時,print()會被顯式地視爲一個函數,所以要輸出上面相同的字符串,你能夠使用這種很是簡單且易於使用的函數語法:
print("Sammy the Shark is my favorite sea creature")
這種改變使得 Python 的語法更加一致,而且在不一樣的 print 函數之間進行切換更加容易。就方便性而言,print()語法也與 Python 2.7 向後兼容,所以您的 Python 3 print()函數能夠在任一版本中運行。
(2)整數的除法
在 Python 2 中,您鍵入的任何不帶小數的數字,將被視爲整數的編程類型。雖然乍看起來這彷佛是一個簡單的處理編程類型的方法,但有時候當你試圖除以整數以指望得到一個帶小數位的答案(稱爲浮點數),如:
5 / 2 = 2.5
然而,在 Python 2 中,整數是強類型的,而且不會變成帶小數位的浮點數,即便這樣作具備直觀上的意義。
當除法/符號的任一側的兩個數字是整數時,Python 2進行底除法,使得對於商x,返回的數字是小於或等於x的最大整數。這意味着當你寫下 5 / 2 來對這兩個數字相除時,Python 2.7 將返回最大的小於或等於 2.5 的整數,在這種情形下:
a = 5 / 2 print a #a=2
爲解決這個問題,你能夠在 5.0 / 2.0 中添加小數位,以獲得預期的答案 2.5。
在 Python 3 中,整數除法變得更直觀,如
a = 5 / 2 print(a) #a=2.5
你也能夠使用 5.0 / 2.0 返回 2.5,可是若是你想作底層劃分,你應該使用 「//」 這樣的 Python 3 語法,像這樣:
b = 5 // 2 print(b) #b=2
在 Python 3 中的這種修改使得整數除法更爲直觀,而且它的特色是不能向後兼容 Python 2.7。
(3)支持 Unicode
當編程語言處理字符串類型時,也就是一個字符序列,它們能夠用幾種不一樣的方式來作,以便計算機將數字轉換爲字母和其餘符號。
Python 2 默認使用 ASCII 字母表,所以當您輸入「Hello,Sammy!」時, Python 2 將以 ASCII 格式處理字符串。被限定爲在多種擴展形式上的數百個字符,用ASCII 進行字符編碼並非一種很是靈活的方法,特別是使用非英語字符時。
要使用更通用和更強大的Unicode字符編碼,這種編碼支持超過128,000個跨越現今和歷史的腳本和符號集的字符,你必須輸入
u「Hello,Sammy!」
, 前綴 u 表明 Unicode。
Python 3 默認使用 Unicode,這節省了程序員多餘的開發時間,而且您能夠輕鬆地在程序中直接鍵入和顯示更多的字符。由於 Unicode 支持更強大的語言字符多樣性以及 emoji 的顯示,因此將它做爲默認字符編碼來使用,能確保全球的移動設備在您的開發項目中都能獲得支持。
若是你但願你的 Python 3 代碼向後兼容 Python 2,你能夠經過在你的字符串的前面保留 「u」 來實現。
(4)後續發展
Python 3 和 Python 2 之間的最大區別不是語法上的,而是事實上 Python 2.7 將在 2020 年失去後續的支持,Python 3 將繼續開發更多的功能和修復更多的錯誤。
最近的發展包括格式化的字符串,類建立的簡單定製,和用一種更乾淨的句法方式來處理矩陣乘法。
Python 3 的後續開發意味着,開發人員能夠對問題被及時解決抱有信心,而且隨着時間的推移更多的功能將被添加進來,程序也會變得更加有效。
a,b=b,a print(a,b) #a=2,b=1
long整數類型被Python3廢棄,統一使用int
一、Python3 使用 print 必需要以小括號包裹打印內容,好比 print('hi')
Python2 既能夠使用帶小括號的方式,也能夠使用一個空格來分隔打印內容,好比 print 'hi'
二、python2 range(1,10)返回列表,python3中返回迭代器,節約內存
三、python2中使用ascii編碼,python中使用utf-8編碼
四、python2中unicode表示字符串序列,str表示字節序列
python3中str表示字符串序列,byte表示字節序列
五、python2中爲正常顯示中文,引入coding聲明,python3中不須要
六、python2中是raw_input()函數,python3中是input()函數
range 函數說明:range([start,] stop[, step]),根據start與stop指定的範圍以及step設定的步長,生成一個序列。
range示例:
>>> range(5) [0, 1, 2, 3, 4] >>> range(1,5) [1, 2, 3, 4] >>> range(0,6,2) [0, 2, 4]
xrange 函數說明:用法與range徹底相同,所不一樣的是生成的不是一個數組,而是一個生成器。
xrange示例:
>>> xrange(5) xrange(5) >>> list(xrange(5)) [0, 1, 2, 3, 4] >>> xrange(1,5) xrange(1, 5) >>> list(xrange(1,5)) [1, 2, 3, 4] >>> xrange(0,6,2) xrange(0, 6, 2) >>> list(xrange(0,6,2)) [0, 2, 4]
由上面的示例能夠知道:要生成很大的數字序列的時候,用xrange會比range性能優不少,由於不須要一上來就開闢一塊很大的內存空間,這兩個基本上都是在循環的時候用:
for i in range(0, 100): print i for i in xrange(0, 100): print i
這兩個輸出的結果都是同樣的,實際上有不少不一樣,range會直接生成一個list對象:
a = range(0,100) print type(a) print a print a[0], a[1]
輸出結果:
<type 'list'> [0, 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, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] 0 1
而xrange則不會直接生成一個list,而是每次調用返回其中的一個值:
a = xrange(0,100) print type(a) print a print a[0], a[1]
結果以下:
<type 'xrange'> xrange(100) 0
總結:
1.range和xrange都是在循環中使用,輸出結果同樣。
2.range返回的是一個list對象,而xrange返回的是一個生成器對象(xrange object)。
3.xrange則不會直接生成一個list,而是每次調用返回其中的一個值,內存空間使用極少,於是性能很是好。
注意:Python 3.x已經去掉xrange,所有用range代替。
兩者使用時相同,但返回類型不一樣,xreadlines返回的是一個生成器,readlines返回的是list
字符串
words = 'today is a wonderfulday' print(words.strip('today'))#若是strip方法指定一個值的話,那麼會去掉這兩個值 print(words.count('a'))#統計字符串出現的次數 print(words.index('is'))#找下標 print(words.index('z'))#找下標若是元素不找不到的話,會報錯 print(words.find('z'))#找下標,若是元素找不到的話,返回-1 print(words.replace('day','DAY'))#字符串替換 print(words.isdigit())#判斷字符串是否爲純數字 print(words.islower())#判斷字符串是否爲小寫字母 print(words.isupper())#判斷字符串是否爲大寫字母 print(words.startswith('http'))#判斷是否以某個字符串開頭 print(words.endswith('.jpg'))#判斷是否以某個字符串結尾 print(words.upper())#將字符串變成大寫 print(words.lower())#將字符串變成小寫
列表
sample_list = ['a',1,('a','b')] #建立列表 sample_list = ['a','b',0,1,3] # Python 列表操做 value_start = sample_list[0] #獲得列表中的某一個值 end_value = sample_list[-1] #獲得列表中的某一個值 del sample_list[0] #刪除列表的第一個值 sample_list[0:0] = ['sample value'] #在列表中插入一個值 list_length = len(sample_list) #獲得列表的長度 for element in sample_list: #列表遍歷 print(element)
基本操做:
元祖
#元組也是一個list,他和list的區別是元組的元素沒法修改 tuple1 = (2,3,4,5,6,4,7) print(type(tuple1)) print(tuple1[:7]) print(tuple1[:5:-1]) for i in range(6): print(tuple1[i]) for i in tuple1: print(i)
字典
dict = {'ob1':'computer', 'ob2':'mouse', 'ob3':'printer'} 每個元素是pair,包含key、value兩部分。key是Integer或string類型,value 是任意類型。 鍵是惟一的,字典只認最後一個賦的鍵值。 dictionary的方法 D.get(key, 0) #同dict[key],多了個沒有則返回缺省值,0。[]沒有則拋異常 D.has_key(key) #有該鍵返回TRUE,不然FALSE D.keys() #返回字典鍵的列表 D.values() D.items() D.update(dict2) #增長合併字典 D.popitem() #獲得一個pair,並從字典中刪除它。已空則拋異常 D.clear() #清空字典,同del dict D.copy() #拷貝字典 D.cmp(dict1,dict2) #比較字典,(優先級爲元素個數、鍵大小、鍵值大小) #第一個大返回1,小返回-1,同樣返回0 dictionary的複製 dict1 = dict #別名 dict2=dict.copy() #克隆,即另外一個拷貝。
經常使用操做:
匿名函數:爲了解決那些功能很簡單的需求而設計的一句話函數
#這段代碼 def calc(n): return n**n print(calc(10)) #換成匿名函數 calc = lambda n:n**n print(calc(10))
上面是咱們對calc這個匿名函數的分析,下面給出了一個關於匿名函數格式的說明
函數名 = lambda 參數 :返回值 #參數能夠有多個,用逗號隔開 #匿名函數無論邏輯多複雜,只能寫一行,且邏輯執行結束後的內容就是返回值 #返回值和正常的函數同樣能夠是任意數據類型
咱們能夠看出,匿名函數並非真的不能有名字。
匿名函數的調用和正常的調用也沒有什麼分別。 就是 函數名(參數) 就能夠了~~~
lambda表達式,一般是在須要一個函數,可是又不想費神去命名一個函數的場合下使用,也就是指匿名函數。
lambda函數的語法只包含一個語句,以下:
lambda [arg1 [,arg2,.....argn]]:expression
以下實例:
sum = lambda arg1, arg2: arg1 + arg2 #調用sum函數 print "Value of total : ", sum( 10, 20 ) print "Value of total : ", sum( 20, 20 )
以上實例輸出結果:
Value of total : 30 Value of total : 40
Lambda函數能接收任何數量的參數但只能返回一個表達式的值
匿名函數不能直接調用print,由於lambda須要一個表達式
一、空語句 do nothing
if true: pass #do nothing else: pass #do something
二、保證格式完整
def iplaypython(): pass
三、保證語義完整
while True: pass
要想理解*arg和**kwarg的做用,先彆着急,經過下面的示例,慢慢思考體會下他們的做用是什麼?
*arg
好比如今我有一個最簡單的加法(Jiafa)函數:
def Jiafa(x, y): z = x + y return z print(Jiafa(1,2))
這個很簡單,一看就知道輸出等於3。
那下一個問題是,若是我要算不固定個數的數字相加,那怎麼來計算呢?
這時,就使用args和*kwarg,就能夠幫助咱們解決這種問題。
*args:能夠理解爲只有一列的表格,長度不固定。
**kwargs:能夠理解爲字典,長度也不固定。
先說args的做用,仍是開篇的案例,咱們要算不定長的加法,就能夠用args來定義了,固然也能夠叫x,y。
def Jiafa(*args): sum = 0 for i in args: sum = sum + i print(sum) Jiafa(1, 3, 5) Jiafa(2, 4, 6, 8, )
輸出結果,9和20。這個案例很簡單,用*args定義就能夠引入,至關於定義了不定長度的函數,而後在程序中就能夠屢次使用。
**kwargs
**kwargs的字典呢?先看一下普通的字典,用一對大括號{}就能夠建立字典,好比下面3行程序,就能夠編一個字典的程序:
dict = {"system": "系統", "China": "中國", "link": "聯接"} x = input("請輸入一個英文單詞:") print(dict.get(x, "本字典裏沒找到!"))
若是輸入正確,會獲得答案,不然會顯示沒找到。
在這個程序裏,dict = {"system": "系統", "China": "中國", "link": "聯接"}建立了三對「鍵和值」(key和value),好比「system」是key,「系統」是key對應的值,也叫鍵值。
還能夠寫一個測試單詞的小軟件。
dict = {"system": "系統", "China": "中國", "link": "聯接"}
y = input("請輸入China的中文意思:") if dict['China'] == y: print("恭喜你,答對了!")
經過value找Key的語句:
z = input("請輸入「系統」的英文單詞:") if list(dict.keys())[list(dict.values()).index("系統")] == z: print("恭喜你,答對了!")
如今問題來了,若是開始不知道字典裏都有哪些內容,須要在程序運程中才知道怎麼辦?這個時候就能夠用kwargs來定義了。咱們先用kwargs來定義一個函數,函數內容先都不用寫,再看看下面的小程序:
def dict(**kwargs): return kwargs mydict = dict(system="系統", China="中國", link="聯接") x = input("請輸入單詞:") if x in mydict.keys(): print("中文意思:", mydict[x]) else: print("抱歉,沒找到。")
用字典也能夠達成這樣的功能,使用in……keys(),就是判斷這個key是否是存在,若是存在就返回它的值。 一樣,用**kwargs傳遞數據的功能,還能夠設計一個用戶登陸的程序:
def dict(**kwargs): return kwargs userdict = dict(user1="1234", user2="5678") x = input("請輸入用戶名:") if x in userdict.keys(): y = input("請輸入密碼:") if userdict[x] == y: print("徹底正確") else: print("密碼錯誤!") else: print("用戶不存在!")
因此從以上的示例能夠看到*arg和**kwarg的做用爲:
一、函數調用裏的*arg和**kwarg:
(1)*arg:元組或列表「出現」
**kwarg:字典「出沒」
(2)分割參數
二、函數定義時傳的*arg /**kwarg:
(1)接收參數
>>> a = 'cheesezh' >>> b = 'cheesezh' >>> a == b True
is也被叫作同一性運算符,這個運算符比較判斷的是對象間的惟一身份標識,也就是id是否相同。經過對下面幾個list間的比較,你就會明白is同一性運算符的工做原理:
例2:
>>> x = y = [4,5,6] >>> z = [4,5,6] >>> x == y True >>> x == z True >>> x is y True >>> x is z False >>> >>> print id(x) 3075326572 >>> print id(y) 3075326572 >>> print id(z) 3075328140
前三個例子都是True,這什麼最後一個是False呢?x、y和z的值是相同的,因此前兩個是True沒有問題。至於最後一個爲何是False,看看三個對象的id分別是什麼就會明白了。
下面再來看一個例子,例3中同一類型下的a和b的(a==b)都是爲True,而(a is b)則否則。
>>> a = 1 #a和b爲數值類型 >>> b = 1 >>> a is b True >>> id(a) 14318944 >>> id(b) 14318944 >>> a = 'cheesezh' #a和b爲字符串類型 >>> b = 'cheesezh' >>> a is b True >>> id(a) 42111872 >>> id(b) 42111872 >>> a = (1,2,3) #a和b爲元組類型 >>> b = (1,2,3) >>> a is b False >>> id(a) 15001280 >>> id(b) 14790408 >>> a = [1,2,3] #a和b爲list類型 >>> b = [1,2,3] >>> a is b False >>> id(a) 42091624 >>> id(b) 42082016 >>> a = {'cheese':1,'zh':2} #a和b爲dict類型 >>> b = {'cheese':1,'zh':2} >>> a is b False >>> id(a) 42101616 >>> id(b) 42098736 >>> a = set([1,2,3])#a和b爲set類型 >>> b = set([1,2,3]) >>> a is b False >>> id(a) 14819976 >>> id(b) 14822256
經過例3可看出,is同一性運算符只有數值型和字符串型的狀況下,a is b才爲True,當a和b是tuple(元祖),list,dict或set型時,a is b爲False。
深淺拷貝用法來自copy模塊。
導入模塊:import copy
淺拷貝:copy.copy
深拷貝:copy.deepcopy
字面理解:淺拷貝指僅僅拷貝數據集合的第一層數據,深拷貝指拷貝數據集合的全部層。因此對於只有一層的數據集合來講深淺拷貝的意義是同樣的,好比字符串,數字,還有僅僅一層的字典、列表、元祖等.
對於如下數據深淺拷貝的意義是同樣的(由於數據類型中只有一層):
name = 'beijing' #字符串 age = 12 #數字 list1 = [1,2,3,4] #列表 dic1 = {'name':'beijing','age':20} #字典
從內存地址來理解深淺拷貝:
>>> import copy >>> name="hahah" #字符串 >>> name1=copy.copy(name) >>> >>> name2=copy.deepcopy(name) >>> print(id(name),id(name1),id(name2)) 11577192 11577192 11577192 >>> sum=111 #數字 >>> sum1=copy.copy(sum) >>> >>> sum2=copy.deepcopy(sum) >>> print(id(sum),id(sum1),id(sum2)) 503865568 503865568 503865568
如上圖,對於數字和字符串的深淺拷貝都只是將變量的索引指向了原來的內存地址,例如在sum,sum1,sum2三個變量中,不管修改任意其中一個變量,只是將其指向了另外一個內存地址,其餘兩個變量不會變,字符串同理。所以,對於 數字 和 字符串 而言,賦值、淺拷貝和深拷貝無心義,由於其永遠指向同一個內存地址。
字典(列表)的深淺拷貝
賦值:
import copy n1 = {'k1':'wu','k2':123,'k3':['alex',678]} n2 = n1
淺拷貝:
import copy n1 = {'k1':'wu','k2':123,'k3':['alex',678]} n3 = copy.copy(n1)
深拷貝:
import copy n1 = {'k1':'wu','k2':123,'k3':['alex',678]} n4 = copy.deepcopy(n1)
深淺拷貝的應用場景
好比在CMDB系統中,咱們定義了一個報警模版call給全部的服務器使用,此時有一批特殊應用的服務器須要不通的報警參數,咱們既不想單獨新建模版來一個一個添加報警參數,又不想修改默認模版而影響其餘機器的報警閾值。此時咱們就須要用深拷貝來完成。示例以下:
默認模版:
call = { 'cpu':80, 'mem':80, 'disk':80 }
此時的特殊模版需求是cpu報警閥值要改爲75,而不影響默認模版使用
代碼以下:
import copy #默認模版 call = { 'cpu':[80,], 'mem':[80,], 'disk':[80,] } #新模板 new_call = copy.deepcopy(call) #修改新模版 new_call['cpu'] = 75 #查看新舊模版的值 print('新的模版爲:%s' %(new_call)) print('默認模版爲:%s' %(call)) #打印結果: #新的模版爲:{'mem': 80, 'disk': 80, 'cpu': 75} #默認模版爲:{'mem': 80, 'disk': 80, 'cpu': 80} #上面的代碼顯示咱們只改了新的模版,而默認模版並無修改,而且咱們用了copy而不是單獨新建模版。
假設咱們用淺copy來作結果是這樣的:
import copy #默認模版 call = { 'cpu':[80,], 'mem':[80,], 'disk':[80,] } #新模板 new_call = copy.copy(call) #修改新模版 new_call['cpu'] = 75 #查看新舊模版的值 print('新的模版爲:%s' %(new_call)) print('默認模版爲:%s' %(call)) #打印的結果: #新的模版爲:{'mem': [80], 'disk': [80], 'cpu': [75]} #默認模版爲:{'mem': [80], 'disk': [80], 'cpu': [75]} #默認模版和新模版都被修改了,顯然這不是咱們要的結果
分析緣由:深拷貝的時候python將字典的全部數據在內存中新建了一份,因此若是你修改新的模版的時候老模版不會變。相反,在淺copy 的時候,python僅僅將最外層的內容在內存中新建了一份出來,字典第二層的列表並無在內存中新建,因此你修改了新模版,默認模版也被修改了。
Python中的垃圾回收是以引用計數爲主,標記-清除和分代收集爲輔。
引用計數:Python在內存中存儲每一個對象的引用計數,若是計數變成0,該對象就會消失,分配給該對象的內存就會釋放出來。
標記-清除:一些容器對象,好比list、dict、tuple,instance等可能會出現引用循環,對於這些循環,垃圾回收器會定時回收這些循環(對象之間經過引用(指針)
連在一塊兒,構成一個有向圖,對象構成這個有向圖的節點,而引用關係構成這個有向圖的邊)。
分代收集:Python把內存根據對象存活時間劃分爲三代,對象建立以後,垃圾回收器會分配它們所屬的代。每一個對象都會被分配一個代,而被分配更年輕的代是被優先處理的,所以越晚建立的對象越容易被回收。
Python的每一個對象都分爲可變和不可變
可變:列表、字典
不可變:數字、字符串、元祖
對不可變類型的變量從新賦值,其實是從新建立一個不可變類型的對象,並將原來的變量從新指向新建立的對象(若是沒有其餘變量引用原有對象的話(即引用計數爲0),原有對象就會被回收)。
不可變類型
以int類型爲例:實際上 i += 1 並非真的在原有的int對象上+1,而是從新建立一個value爲6的int對象,i引用自這個新的對象。
>>> i = 5 >>> i += 1 >>> i 6 >>> id(i) 140243713967984 # 經過id函數查看變量i的內存地址進行驗證(使用hex(id(i)) 能夠查看16進制的內存地址) >>> i += 1 >>> i 7 >>> id(i) 140243713967960
能夠看到執行 i += 1 時,內存地址都會變化,由於int 類型是不可變的。
再改改代碼,但多個int類型的變量值相同時,看看它們內存地址是否相同。
>>> i = 5 >>> j = 5 >>> id(i) 140656970352216 >>> id(j) 140656970352216 >>> k = 5 >>> id(k) 140656970352216 >>> x = 6 >>> id(x) 140656970352192 >>> y = 6 >>> id(y) 140656970352192 >>> z = 6 >>> id(z) 140656970352192
對於不可變類型int,不管建立多少個不可變類型,只要值相同,都指向同個內存地址。一樣狀況的還有比較短的字符串。
對於其餘類型則不一樣,以浮點類型爲例,從代碼運行結果能夠看出它是個不可變類型:對i的值進行修改後,指向新的內存地址。
>>> i = 1.5 >>> id(i) 140675668569024 >>> i = i + 1.7 >>> i 3.2 >>> id(i) 140675668568976
修改代碼聲明兩個相同值的浮點型變量,查看它們的id,發現它們並非指向同個內存地址,這點和int類型不一樣(這方面涉及Python內存管理機制,Python對int類型和較短的字符串進行了緩存,不管聲明多少個值相同的變量,實際上都指向同個內存地址。)
>>> i = 2.5 >>> id(i) 140564351733040 >>> j = 2.5 >>> id(j) 140564351733016
可變類型
可變類型的話,以list爲例。list在append以後,仍是指向同個內存地址,由於list是可變類型,能夠在原處修改。
>>> a = [1, 2, 3] >>> id(a) 4385327224 >>> a.append(4) >>> id(a) 4385327224
改改代碼,當存在多個值相同的不可變類型變量時,看看它們是否是跟可變類型同樣指向同個內存地址
>>> a = [1, 2, 3] >>> id(a) 4435060856 >>> b = [1, 2, 3] >>> id(b) 4435102392
從運行結果能夠看出,雖然a、b的值相同,可是指向的內存地址不一樣。咱們也能夠經過b = a 的賦值語句,讓他們指向同個內存地址:
>>> a = [1, 2, 3] >>> id(a) 4435060856 >>> b = [1, 2, 3] >>> id(b) 4435102392 >>> b = a >>> id(b) 4435060856
這個時候須要注意,由於a、b指向同個內存地址,而a、b的類型都是List,可變類型,對a、b任意一個List進行修改,都會影響另一個List的值。
>>> a = [1, 2, 3] >>> id(a) #4435060856 >>> b = [1, 2, 3] >>> id(b) #4435102392 >>> b = a >>> id(b) #4435060856 >>> b.append(4) >>> a [1, 2, 3, 4] >>> b [1, 2, 3, 4] >>> id(a) #4435060856 >>> id(b) #4435060856
代碼中,b變量append(4),對a變量也是影響的。輸出他們的內存地址,仍是指向同個內存地址。
總結:
一、對於不可變類型,不管建立多少個不可變類型,只要值相同,都指向同個內存地址(若值不一樣,那麼必定指向不一樣的內存地址)。
二、對於可變類型,不管建立多少個可變類型,只要值相同,都不指向同個內存地址(除非進行復制操做,那麼他們將會指向同一個地址)。
做用域相關
基於字典的形式獲取局部變量和全局變量
globals()——獲取全局變量的字典
locals()——獲取執行本方法所在命名空間內的局部變量的字典
其餘
字符串類型代碼的執行
http://www.cnblogs.com/Eva-J/articles/7266087.html
輸入輸出相關:
input() 輸入
s = input("請輸入內容 : ") #輸入的內容賦值給s變量 print(s) #輸入什麼打印什麼。數據類型是str
print() 輸出
f = open('tmp_file','w') print(123,456,sep=',',file = f,flush=True)
import time for i in range(0,101,2): time.sleep(0.1) char_num = i//2 #打印多少個'*' per_str = '\r%s%% : %s\n' % (i, '*' * char_num) if i == 100 else '\r%s%% : %s'%(i,'*'*char_num) print(per_str,end='', flush=True) #小越越 : \r 能夠把光標移動到行首但不換行
數據類型相關:
type(o) 返回變量o的數據類型
內存相關:
id(o) o是參數,返回一個變量的內存地址
hash(o) o是參數,返回一個可hash變量的哈希值,不可hash的變量被hash以後會報錯。
t = (1,2,3) l = [1,2,3] print(hash(t)) #可hash print(hash(l)) #會報錯 ''' 結果: TypeError: unhashable type: 'list' '''
*每一次執行程序,內容相同的變量hash值在這一次執行過程當中不會發生改變。
文件操做相關
open() 打開一個文件,返回一個文件操做符(文件句柄)
操做文件的模式有r,w,a,r+,w+,a+ 共6種,每一種方式均可以用二進制的形式操做(rb,wb,ab,rb+,wb+,ab+)
能夠用encoding指定編碼.
模塊操做相關
__import__導入一個模塊
import time
os = __import__('os') print(os.path.abspath('.'))
幫助方法
在控制檯執行help()進入幫助模式。能夠隨意輸入變量或者變量的類型。輸入q退出
或者直接執行help(o),o是參數,查看和變量o有關的操做。。。
和調用相關
callable(o),o是參數,看這個變量是否是可調用。
若是o是一個函數名,就會返回True
def func():pass print(callable(func)) #參數是函數名,可調用,返回True print(callable(123)) #參數是數字,不可調用,返回False
查看參數所屬類型的全部內置方法
dir() 默認查看全局空間內的屬性,也接受一個參數,查看這個參數內的方法或變量
print(dir(list)) #查看列表的內置方法 print(dir(int)) #查看整數的內置方法
和數字相關
數字——數據類型相關:bool,int,float,complex
數字——進制轉換相關:bin,oct,hex
數字——數學運算:abs,divmod,min,max,sum,round,pow
和數據結構相關
序列——列表和元組相關的:list和tuple
序列——字符串相關的:str,format,bytes,bytearry,memoryview,ord,chr,ascii,repr
ret = bytearray('alex',encoding='utf-8') print(id(ret)) print(ret[0]) ret[0] = 65 print(ret) print(id(ret))
ret = memoryview(bytes('你好',encoding='utf-8')) print(len(ret)) print(bytes(ret[:3]).decode('utf-8')) print(bytes(ret[3:]).decode('utf-8'))
序列:reversed,slice
l = (1,2,23,213,5612,342,43) print(l) print(list(reversed(l)))
l = (1,2,23,213,5612,342,43) sli = slice(1,5,2) print(l[sli])
數據集合——字典和集合:dict,set,frozenset
數據集合:len,sorted,enumerate,all,any,zip,filter,map
filter
filter()函數接收一個函數 f 和一個list,這個函數 f 的做用是對每一個元素進行判斷,返回 True或 False,filter()根據判斷結果自動過濾掉不符合條件的元素,返回由符合條件元素組成的新list。
例如,要從一個list [1, 4, 6, 7, 9, 12, 17]中刪除偶數,保留奇數,首先,要編寫一個判斷奇數的函數:
def is_odd(x): return x % 2 == 1
而後,利用filter()過濾掉偶數:
>>>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 >>>filter(is_not_empty, ['test', None, '', 'str', ' ', 'END'])
結果:
['test', 'str', 'END']
注意: s.strip(rm) 刪除 s 字符串中開頭、結尾處的 rm 序列的字符。
當rm爲空時,默認刪除空白符(包括'\n', '\r', '\t', ' '),以下:
>>> a = ' 123'
>>> a.strip()
'123'
>>> a = '\t\t123\r\n'
>>> a.strip()
'123'
map
Python中的map函數應用於每個可迭代的項,返回的是一個結果list。若是有其餘的可迭代參數傳進來,map函數則會把每個參數都以相應的處理函數進行迭代處理。map()函數接收兩個參數,一個是函數,一個是序列,map將傳入的函數依次做用到序列的每一個元素,並把結果做爲新的list返回。
有一個list, L = [1,2,3,4,5,6,7,8],咱們要將f(x)=x^2做用於這個list上,那麼咱們能夠使用map函數處理。
>>> L = [1,2,3,4,] >>> def pow2(x): ... return x*x ... >>> map(pow2,L) [1, 4, 9, 16]
sorted方法:
sorted(iterable, key=None, reverse=False)
Return a new list containing all items from the iterable in ascending order.
A custom key function can be supplied to customise the sort order, and the
reverse flag can be set to request the result in descending order.
l1 = [1,3,5,-2,-4,-6] l2 = sorted(l1,key=abs) print(l1) print(l2)
列表按照每個元素的len排序
l = [[1,2],[3,4,5,6],(7,),'123'] print(sorted(l,key=len))
請務必重點掌握:
其餘:input,print,type,hash,open,import,dir
str類型代碼執行:eval,exec
數字:bool,int,float,abs,divmod,min,max,sum,round,pow
序列——列表和元組相關的:list和tuple
序列——字符串相關的:str,bytes,repr
序列:reversed,slice
數據集合——字典和集合:dict,set,frozenset
數據集合:len,sorted,enumerate,zip,filter,map
通俗的說..都是用在一堆數據(好比一個列表)上..
map是用一樣方法把全部數據都改爲別的..字面意思是映射..好比把列表的每一個數都換成其平方..
reduce是用某種方法依次把全部數據丟進去最後獲得一個結果..字面意思是化簡..好比計算一個列表全部數的和的過程,就是維持一個部分和而後依次把每一個數加進去..
filter是篩選出其中知足某個條件的那些數據..字面意思是過濾..好比挑出列表中全部奇數..
>>> map(lambda x:x*x,[0,1,2,3,4,5,6]) [0, 1, 4, 9, 16, 25, 36] >>> reduce(lambda x,y:x+y,[0,1,2,3,4,5,6]) 21 >>> filter(lambda x:x&1,[0,1,2,3,4,5,6]) [1, 3, 5]
-
print ('\n'.join([' '.join(['%s*%s=%-2s' % (y,x,x*y) for y in range(1,x+1)]) for x in range(1,10)]))
在Python中,安裝第三方模塊,是經過setuptools這個工具完成的。Python有兩個封裝了setuptools的包管理工具:easy_install和pip。目前官方推薦使用pip
若是你正在使用Mac或Linux,安裝pip自己這個步驟就能夠跳過了。
若是你正在使用Windows,確保安裝時勾選了pip和Add python.exe to Path。
在命令提示符窗口下嘗試運行pip,若是Windows提示未找到命令,能夠從新運行安裝程序添加pip。
如今,讓咱們來安裝一個第三方庫——Python Imaging Library,這是Python下很是強大的處理圖像的工具庫。通常來講,第三方庫都會在Python官方的pypi.python.org網站註冊,要安裝一個第三方庫,必須先知道該庫的名稱,能夠在官網或者pypi上搜索,好比Python Imaging Library的名稱叫PIL,所以,安裝Python Imaging Library的命令就是:
pip install PIL
耐心等待下載並安裝後,就能夠使用PIL了。
有了PIL,處理圖片易如反掌。隨便找個圖片生成縮略圖:
>>> import Image >>> im = Image.open('test.png') >>> print im.format, im.size, im.mode PNG (400, 300) RGB >>> im.thumbnail((200, 100)) >>> im.save('thumb.jpg', 'JPEG')
其餘經常使用的第三方庫還有MySQL的驅動:MySQL-python,用於科學計算的NumPy庫:numpy,用於生成文本的模板工具Jinja2,等等。
模塊搜索路徑
當咱們試圖加載一個模塊時,Python會在指定的路徑下搜索對應的.py文件,若是找不到,就會報錯:
>>> import mymodule Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: No module named mymodule
默認狀況下,Python解釋器會搜索當前目錄、全部已安裝的內置模塊和第三方模塊,搜索路徑存放在sys模塊的path變量中:
>>> import sys >>> sys.path ['', '/Library/Python/2.7/site-packages/pycrypto-2.6.1-py2.7-macosx-10.9-intel.egg', '/Library/Python/2.7/site-packages/PIL-1.1.7-py2.7-macosx-10.9-intel.egg', ...]
若是咱們要添加本身的搜索目錄,有兩種方法:
一是直接修改sys.path,添加要搜索的目錄:
這種方法是在運行時修改,運行結束後失效。
第二種方法是設置環境變量PYTHONPATH,該環境變量的內容會被自動添加到模塊搜索路徑中。設置方式與設置Path環境變量相似。注意只須要添加你本身的搜索路徑,Python本身自己的搜索路徑不受影響。
一、Django
二、pip
三、pillow-python
四、pygame
五、pyMysql
六、pytz
七、opencv-python
八、numpy
一、match()函數只檢測RE是否是在string的開始位置匹配,search()會掃描整個string查找匹配;
二、也就是說match()只有在0位置匹配成功的話纔有返回,若是不是開始位置匹配成功的話,match()就返回none。
三、例如:
import re print(re.match('super', 'superstition').span()) # (0,5) print(re.match('super', 'insuperable')) # None
四、search()會掃描整個字符串並返回第一個成功的匹配:
例如:
import re print(re.search('super', 'superstition').span()) #(0,5) print(re.search('super', 'insuperable')) # <_sre.SRE_Match object; span=(2, 7), match='super'>
五、其中span函數定義以下,返回位置信息:
span([group]):
返回(start(group), end(group))。
一、貪婪匹配
老是嘗試匹配儘量多的字符
二、非貪婪匹配
是嘗試匹配儘量少的字符
import re secret_code = 'hadkfalifexxIxxfasdjifja134xxlovexx23345sdfxxyouxx8dfse' b = re.findall('xx.*xx',secret_code) # 貪婪匹配 print (b) # ['xxIxxfasdjifja134xxlovexx23345sdfxxyouxx'] c = re.findall('xx.*?xx',secret_code) # 非貪婪匹配 print(c) # ['xxIxx', 'xxlovexx', 'xxyouxx']
貪婪格式:xx.*xx
非貪婪格式:xx.*?xx
區別重點在:.* 和 .*?
那咱們先經過程序看看這個函數有什麼坑吧!
def func(a,b=[]): b.append(a) print(b) func(1) func(1) func(1) func(1)
看下結果
[1]
[1, 1]
[1, 1, 1]
[1, 1, 1, 1]
函數的第二個默認參數是一個list,當第一次執行的時候實例化了一個list,第二次執行仍是用第一次執行的時候實例化的地址存儲,因此三次執行的結果就是 [1, 1, 1] ,想每次執行只輸出[1] ,默認參數應該設置爲None。
"1,2,3".split(',')
"1,2,3".split(',')
a=['1','2','3'] b=[int(i) for i in a] print(b)
輸出爲:[1, 2, 3]
y = [x*x for x in range (1,11)]
print(y)
輸出爲:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
方法二:
[i*i for i in range(11)]
輸出爲:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
使用set()方法!
list0=['b','c', 'd','b','c','a','a'] print(sorted(set(list0),key=list0.index)) # sorted output
在函數的內部,經過global聲明,使在函數內部中設置一個全局變量,這個全局變量能夠在任意的函數中進行調用!
SOLR_URL='http://solr.org' def tt(): global SOLR_URL SOLR_URL=SOLR_URL+'#aa' def aa(): if __name__=='__main__': tt() print(SOLR_URL) aa() # http://solr.org#aa
經過log的分析,能夠方便用戶瞭解系統或軟件、應用的運行狀況;若是你的應用log足夠豐富,也能夠分析以往用戶的操做行爲、類型喜愛、地域分佈或其餘更多信息;若是一個應用的log同時也分了多個級別,那麼能夠很輕易地分析獲得該應用的健康情況,及時發現問題並快速定位、解決問題,補救損失。
簡單來說就是,咱們經過記錄和分析日誌能夠了解一個系統或軟件程序運行狀況是否正常,也能夠在應用程序出現故障時快速定位問題。好比,作運維的同窗,在接收到報警或各類問題反饋後,進行問題排查時一般都會先去看各類日誌,大部分問題均可以在日誌中找到答案。再好比,作開發的同窗,能夠經過IDE控制檯上輸出的各類日誌進行程序調試。對於運維老司機或者有經驗的開發人員,能夠快速的經過日誌定位到問題的根源。可見,日誌的重要性不可小覷。日誌的做用能夠簡單總結爲如下3點:
若是應用的日誌信息足夠詳細和豐富,還能夠用來作用戶行爲分析,如:分析用戶的操做行爲、類型洗好、地域分佈以及其它更多的信息,由此能夠實現改進業務、提升商業利益。
logging模塊默認定義瞭如下幾個日誌等級,它容許開發人員自定義其餘日誌級別,可是這是不被推薦的,尤爲是在開發供別人使用的庫時,由於這會致使日誌級別的混亂。
日誌等級(level) | 描述 |
---|---|
DEBUG | 最詳細的日誌信息,典型應用場景是 問題診斷 |
INFO | 信息詳細程度僅次於DEBUG,一般只記錄關鍵節點信息,用於確認一切都是按照咱們預期的那樣進行工做 |
WARNING | 當某些不指望的事情發生時記錄的信息(如,磁盤可用空間較低),可是此時應用程序仍是正常運行的 |
ERROR | 因爲一個更嚴重的問題致使某些功能不能正常運行時記錄的信息 |
CRITICAL | 當發生嚴重錯誤,致使應用程序不能繼續運行時記錄的信息 |
開發應用程序或部署開發環境時,能夠使用DEBUG或INFO級別的日誌獲取儘量詳細的日誌信息來進行開發或部署調試;應用上線或部署生產環境時,應該使用WARNING或ERROR或CRITICAL級別的日誌來下降機器的I/O壓力和提升獲取錯誤日誌信息的效率。日誌級別的指定一般都是在應用程序的配置文件中進行指定的。
說明:
- 上面列表中的日誌等級是從上到下依次升高的,即:DEBUG < INFO < WARNING < ERROR < CRITICAL,而日誌的信息量是依次減小的;
- 當爲某個應用程序指定一個日誌級別後,應用程序會記錄全部日誌級別大於或等於指定日誌級別的日誌信息,而不是僅僅記錄指定級別的日誌信息,nginx、php等應用程序以及這裏要提升的python的logging模塊都是這樣的。一樣,logging模塊也能夠指定日誌記錄器的日誌級別,只有級別大於或等於該指定日誌級別的日誌記錄纔會被輸出,小於該等級的日誌記錄將會被丟棄。
- 能夠參考該人的博客:https://www.cnblogs.com/yyds/p/6901864.html
棧和隊列是兩種基本的數據結構,同爲容器類型。二者根本的區別在於:
stack:後進先出
queue:先進先出
PS:stack和queue是不能經過查詢具體某一個位置的元素而進行操做的。可是他們的排列是按順序的
對於stack咱們能夠使用python內置的list實現,由於list是屬於線性數組,在末尾插入和刪除一個元素所使用的時間都是O(1),這很是符合stack的要求。固然,咱們也能夠使用鏈表來實現。
stack的實現代碼(使用python內置的list),實現起來是很是的簡單,就是list的一些經常使用操做
class Stack(object): def __init__(self): self.stack = [] def push(self, value): # 進棧 self.stack.append(value) def pop(self): #出棧 if self.stack: self.stack.pop() else: raise LookupError('stack is empty!') def is_empty(self): # 若是棧爲空 return bool(self.stack) def top(self): #取出目前stack中最新的元素 return self.stack[-1]
咱們定義以下的鏈表來實現隊列數據結構:
定義一個頭結點,左邊指向隊列的開頭,右邊指向隊列的末尾,這樣就能夠保證咱們插入一個元素和取出一個元素都是O(1)的操做,使用這種鏈表實現stack也是很是的方便。實現代碼以下:
class Head(object): def __init__(self): self.left = None self.right = None class Node(object): def __init__(self, value): self.value = value self.next = None class Queue(object): def __init__(self): #初始化節點 self.head = Head() def enqueue(self, value): #插入一個元素 newnode = Node(value) p = self.head if p.right: #若是head節點的右邊不爲None #說明隊列中已經有元素了 #就執行下列的操做 temp = p.right p.right = newnode temp.next = newnode else: #這說明隊列爲空,插入第一個元素 p.right = newnode p.left = newnode def dequeue(self): #取出一個元素 p = self.head if p.left and (p.left == p.right): #說明隊列中已經有元素 #可是這是最後一個元素 temp = p.left p.left = p.right = None return temp.value elif p.left and (p.left != p.right): #說明隊列中有元素,並且不止一個 temp = p.left p.left = temp.next return temp.value else: #說明隊列爲空 #拋出查詢錯誤 raise LookupError('queue is empty!') def is_empty(self): if self.head.left: return False else: return True def top(self): #查詢目前隊列中最先入隊的元素 if self.head.left: return self.head.left.value else: raise LookupError('queue is empty!')
Python的字符串格式化經常使用的有三種!-
第一種:最方便的
缺點:需一個個的格式化
print('hello %s and %s'%('df','another df'))
第二種:最好用的
優勢:不須要一個個的格式化,能夠利用字典的方式,縮短期
print('hello %(first)s and %(second)s'%{'first':'df' , 'second':'another df'})
第三種:最早進的
優勢:可讀性強
print('hello {first} and {second}'.format(first='df',second='another df'))
迭代器:是訪問集合元素的一種方式,從集合的第一個元素開始訪問,直到全部元素被訪問結束。其優勢是不須要事先準備好整個迭代過程當中的全部元素,僅在迭代到某個元素時纔開始計算該元素。適合遍歷比較巨大的集合。__iter__():方法返回迭代器自己, __next__():方法用於返回容器中下一個元素或數據。
生成器:帶有yield的函數再也不是一個普通函數,而是一個生成器。當函數被調用時,返回一個生成器對象。不像通常函數在生成值後退出,生成器函數在生成值後會自動掛起並暫停他們的執行狀態。
'''迭代器''' print('for x in iter([1, 2, 3, 4, 5]):') for x in iter([1, 2, 3, 4, 5]): print(x) '''生成器''' def myyield(n): while n>0: print("開始生成...:") yield n print("完成一次...:") n -= 1 for i in myyield(4): print("遍歷獲得的值:",i)
data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35] def binary_search(dataset,find_num): if len(dataset) > 1: mid = int(len(dataset) / 2) if dataset[mid] == find_num: # find it print("找到數字", dataset[mid]) elif dataset[mid] > find_num: # 找的數在mid左面 print("\033[31;1m找的數在mid[%s]左面\033[0m" % dataset[mid]) return binary_search(dataset[0:mid], find_num) else: # 找的數在mid右面 print("\033[32;1m找的數在mid[%s]右面\033[0m" % dataset[mid]) return binary_search(dataset[mid + 1:], find_num) else: if dataset[0] == find_num: # find it print("找到數字啦", dataset[0]) else: print("沒的分了,要找的數字[%s]不在列表裏" % find_num) binary_search(data,20)
再說說閉包以前,先說一說什麼是外函數,什麼是內函數?
外函數:函數A的內部定義了函數B,那麼函數A就叫作外函數
內函數:函數B就叫作內函數
什麼是閉包?
在一個外函數中定義了一個內函數,內函數裏運用了外函數的臨時變量,而且外函數的返回值是內函數的引用。這樣就構成了一個閉包。
通常狀況下,在咱們認知當中,若是一個函數結束,函數的內部全部東西都會釋放掉,還給內存,局部變量都會消失。可是閉包是一種特殊狀況,若是外函數在結束的時候發現有本身的臨時變量未來會在內部函數中用到,就把這個臨時變量綁定給了內部函數,而後本身再結束。
def outer(a): b = 10 def inner(): print(a+b) return inner if __name__ == '__main__': demo = outer(5) demo() demo2 = outer(7) demo2()
ys模塊主要是用於提供對python解釋器相關的操做
a) 屏幕輸出a
sys.arg 獲取位置參數 print(sys.argv) 執行該腳本,加參數的打印結果 python3 m_sys.py 1 2 3 4 5 ['m_sys.py', '1', '2', '3', '4', '5'] 能夠發現 sys.arg返回的是整個位置參數,相似於shell的$0 $1... sys.exit(n) 程序退出,n是退出是返回的對象 sys.version 獲取python版本 >>> sys.version '3.5.1 (v3.5.1:37a07cee5969, Dec 5 2015, 21:12:44) \n[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]' sys.path 返回模塊的搜索路徑列表,可經過添加自定義路徑,來添加自定義模塊 >>> sys.path ['', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python35.zip', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages'] sys.platform 返回當前系統平臺 linux平臺返回linux,windows平臺返回win32,MAC返回darwin >>> sys.platform 'darwin sys.stdout.write() 輸出內容 >>> sys.stdout.write('asd') asd3 >>> sys.stdout.write('asd') asd3 >>> sys.stdout.write('as') as2
進度條:
#!/usr/bin/env python # -*- coding: UTF-8 -*- #pyversion:python3.5 #owner:fuzj """ sys 和python解析器相關 """ import sys import time def view_bar(num,total): rate = num / total rate_num = int(rate * 100) #r = '\r %d%%' %(rate_num) r = '\r%s>%d%%' % ('=' * rate_num, rate_num,) sys.stdout.write(r) sys.stdout.flush if __name__ == '__main__': for i in range(0, 101): time.sleep(0.1) view_bar(i, 100) 效果: ====================================================================================================>100%
os模塊
OS模塊是Python標準庫中的一個用於訪問操做系統功能的模塊,使用OS模塊中提供的接口,能夠實現跨平臺訪問
用於提供系統級別的操做
dirname) 改變當前腳本工做目錄;至關於shell下cd
oldname,
new) 重命名文件/目錄
\,Linux下爲
/
\t\n,Linux下爲
\n
bash command) 運行shell命令,直接顯示
答:random模塊
隨機整數:random.randint(a,b):返回隨機整數x,a<=x<=b
random.randrange(start,stop,[,step]):返回一個範圍在(start,stop,step)之間的隨機整數,不包括結束值。
隨機實數:random.random( ):返回0到1之間的浮點數
random.uniform(a,b):返回指定範圍內的浮點數。
import random # 隨機模塊 data = list(range(10)) print(data) # 打印有序的列表 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] random.shuffle(data) # 使有序變爲無序 print(data) # 打印無序的列表 [4, 2, 5, 1, 6, 3, 9, 8, 0, 7]
若想利用python刪除windows裏的文件,這裏須要使用os模塊!那接下來就看看利用os模塊是如何刪除文件的!
具體實現方法以下!
os.remove(path)
刪除文件 path. 若是path是一個目錄, 拋出 OSError錯誤。若是要刪除目錄,請使用rmdir().
remove() 同 unlink() 的功能是同樣的
在Windows系統中,刪除一個正在使用的文件,將拋出異常。在Unix中,目錄表中的記錄被刪除,但文件的存儲還在。
import os my_file = 'D:/text.txt' # 文件路徑 if os.path.exists(my_file): # 若是文件存在 #刪除文件,可以使用如下兩種方法。 os.remove(my_file) # 則刪除 #os.unlink(my_file) else: print('no such file:%s'%my_file)
os.removedirs(path)
遞歸地刪除目錄。相似於rmdir(), 若是子目錄被成功刪除, removedirs() 將會刪除父目錄;但子目錄沒有成功刪除,將拋出錯誤。
例如, os.removedirs(「foo/bar/baz」) 將首先刪除baz目錄,而後再刪除bar和 foo, 若是他們是空的話,則子目錄不能成功刪除,將拋出 OSError異常
os.rmdir(path)
刪除目錄 path,要求path必須是個空目錄,不然拋出OSError錯誤
import os for root, dirs, files in os.walk(top, topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name))
方法2:
代碼以下:
import shutil shutil.rmtree()
什麼是封裝?
所謂的面向對象就是將咱們的程序模塊化,對象化,把具體事物的特性屬性和經過這些屬性來實現一些動做的具體方法放到一個類裏面,這就是封裝。封裝是咱們所說的面相對象編程的特徵之一。除此以外還有繼承和多態。
什麼是繼承?
繼承有點相似與咱們生物學上的遺傳,就是子類的一些特徵是來源於父類的,兒子遺傳了父親或母親的一些性格,或者相貌,又或者是運動天賦。有點種瓜得瓜種豆得豆的意思。面向對象裏的繼承也就是父類的相關的屬性,能夠被子類重複使用,子類沒必要再在本身的類裏面從新定義一回,父類裏有點咱們只要拿過來用就行了。而對於本身類裏面須要用到的新的屬性和方法,子類就能夠本身來擴展了。
什麼是多態?
咱們在有一些方法在父類已經定義好了,可是子類咱們本身再用的時候,發現,其實,咱們的雖然都是計算工資的,可是普通員工的工資計算方法跟經理的計算方法是不同的,因此這個時候,咱們就不能直接調用父類的這個計算工資的方法了。這個時候咱們就須要用到面向對象的另外一個特性,多態。咱們要在子類裏面把父類裏面定義計算工資的方法在子類裏面從新實現一遍。多態包含了重載和重寫。
什麼是重寫?
重寫很簡單就是把子類從父親類裏繼承下來的方法從新寫一遍,這樣,父類裏相同的方法就被覆蓋了,固然啦,你仍是能夠經過super.CaculSalary方法來調用父類的工資計算方法。
什麼是重載?
重載就是類裏面相同方法名,不一樣形參的狀況,能夠是形參類型不一樣或者形參個數不一樣,或者形參順序不一樣,可是不能使返回值類型不一樣。
繼承的優勢:
一、建造系統中的類,避免重複操做。
二、新類常常是基於已經存在的類,這樣就能夠提高代碼的複用程度。
繼承的特色:
一、在繼承中基類的構造(__init__()方法)不會被自動調用,它須要在其派生類的構造中親自專門調用。有別於C#
二、在調用基類的方法時,須要加上基類的類名前綴,且須要帶上self參數變量。區別於在類中調用普通函數時並不須要帶上self參數
三、Python老是首先查找對應類型的方法,若是它不能在派生類中找到對應的方法,它纔開始到基類中逐個查找。(先在本類中查找調用的方法,找不到纔去基類中找)。
什麼是super?
super() 函數是用於調用父類(超類)的一個方法。
super 是用來解決多重繼承問題的,直接用類名調用父類方法在使用單繼承的時候沒問題,可是若是使用多繼承,會涉及到查找順序(MRO)、重複調用(鑽石繼承)等種種問題。
MRO 就是類的方法解析順序表, 其實也就是繼承父類方法時的順序表。
語法
如下是 super() 方法的語法:
super(type[, object-or-type])
參數
·type -- 類。
·object-or-type -- 類,通常是 self
Python3.x 和 Python2.x 的一個區別是: Python 3 能夠使用直接使用 super().xxx 代替 super(Class, self).xxx :
Python3.x 實例:
class A: pass class B(A): def add(self, x): super().add(x)
Python2.x 實例:
class A(object): # Python2.x 記得繼承 object pass class B(A): def add(self, x): super(B, self).add(x)
具體應用示例:
舉個例子
class Foo: def bar(self, message): print(message) >>> Foo().bar("Hello, Python.") Hello, Python.
當存在繼承關係的時候,有時候須要在子類中調用父類的方法,此時最簡單的方法是把對象調用轉換成類調用,須要注意的是這時self參數須要顯式傳遞,例如:
class FooParent: def bar(self, message): print(message) class FooChild(FooParent): def bar(self, message): FooParent.bar(self, message) >>> FooChild().bar("Hello, Python.") Hello, Python.
這樣作有一些缺點,好比說若是修改了父類名稱,那麼在子類中會涉及多處修改,另外,Python是容許多繼承的語言,如上所示的方法在多繼承時就須要重複寫屢次,顯得累贅。爲了解決這些問題,Python引入了super()機制,例子代碼以下:
class FooParent: def bar(self, message): print(message) class FooChild(FooParent): def bar(self, message): super(FooChild, self).bar(message) >>> FooChild().bar("Hello, Python.") Hello, Python
表面上看 super(FooChild, self).bar(message)方法和FooParent.bar(self, message)方法的結果是一致的,實際上這兩種方法的內部處理機制大大不一樣,當涉及多繼承狀況時,就會表現出明顯的差別來,直接給例子:
代碼一:
class A: def __init__(self): print("Enter A") print("Leave A") class B(A): def __init__(self): print("Enter B") A.__init__(self) print("Leave B") class C(A): def __init__(self): print("Enter C") A.__init__(self) print("Leave C") class D(A): def __init__(self): print("Enter D") A.__init__(self) print("Leave D") class E(B, C, D): def __init__(self): print("Enter E") B.__init__(self) C.__init__(self) D.__init__(self) print("Leave E") E()
結果爲:
Enter E
Enter B
Enter A
Leave A
Leave B
Enter C
Enter A
Leave A
Leave C
Enter D
Enter A
Leave A
Leave D
Leave E
執行順序很好理解,惟一須要注意的是公共父類A被執行了屢次。
代碼二:
class A: def __init__(self): print("Enter A") print("Leave A") class B(A): def __init__(self): print("Enter B") super(B, self).__init__() print("Leave B") class C(A): def __init__(self): print("Enter C") super(C, self).__init__() print("Leave C") class D(A): def __init__(self): print("Enter D") super(D, self).__init__() print("Leave D") class E(B, C, D): def __init__(self): print("Enter E") super(E, self).__init__() print("Leave E") E()
結果:
Enter E
Enter B
Enter C
Enter D
Enter A
Leave A
Leave D
Leave C
Leave B
Leave E
在super機制裏能夠保證公共父類僅被執行一次,至於執行的順序,是按照MRO(Method Resolution Order):方法解析順序 進行的。
functools模塊介紹
functools用於高階函數:指那些做用於函數或者返回其餘函數的函數。一般狀況下,只要是能夠被當作函數調用的對象就是這個模塊的目標。
functools模塊的功能
python 中提供一種用於對函數固定屬性的函數(與數學上的偏函數不同)
# 一般會返回10進制 int('12345') # print 12345 # 使用參數 返回 8進制 int('11111', 8) # print 4681
每次都得添加參數比較麻煩, functools提供了partial的方法
import functools foo = functools.partial(int, base=8) foo('11111') # print 4681
經過這種方法生成一個固定參數的新函數
def int2(x, base=2): return int(x, base)
這樣,咱們轉換二進制就很是方便了:
>>> int2('1000000') 64 >>> int2('1010101') 85
functools.partial就是幫助咱們建立一個偏函數的,不須要咱們本身定義int2(),能夠直接使用下面的代碼建立一個新的函數int2:
>>> import functools >>> int2 = functools.partial(int, base=2) >>> int2('1000000') 64 >>> int2('1010101') 85
因此,簡單總結functools.partial的做用就是,把一個函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,調用這個新函數會更簡單。
注意到上面的新的int2函數,僅僅是把base參數從新設定默認值爲2,但也能夠在函數調用時傳入其餘值:
>>> int2('1000000', base=10) 1000000
最後,建立偏函數時,實際上能夠接收函數對象、*args和**kwargs這3個參數,當傳入:
int2 = functools.partial(int, base=2)
實際上固定了int()函數的關鍵字參數base,也就是:
int2('10010')
至關於:
kw = { 'base': 2 } int('10010', **kwargs)
當傳入:
max2 = functools.partial(max, 10)
實際上會把10做爲*args的一部分自動加到左邊,也就是:
max2(5, 6, 7)
至關於:
args = (10, 5, 6, 7) max(*args)
__new__
和__init__
的區別這個__new__
確實不多見到,先作了解吧.
__new__
是一個靜態方法,而__init__
是一個實例方法.__new__
方法會返回一個建立的實例,而__init__
什麼都不返回.__new__
返回一個cls的實例時後面的__init__
才能被調用.__new__
,初始化一個實例時用__init__
.
函數:
函數是封裝了一些獨立的功能,能夠直接調用,python內置了許多函數,同時能夠自建函數來使用。
方法:
方法和函數相似,一樣封裝了獨立的功能,可是方法是須要經過對象來調用的,表示針對這個對象要作的操做,使用時採用點方法。
單例模式是一種經常使用的軟件設計模式。在它的核心結構中只包含一個被稱爲單例類的特殊類。經過單例模式能夠保證系統中一個類只有一個實例並且該實例易於外界訪問,從而方便對實例個數的控制並節約系統資源。若是但願在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。
__new__()
在__init__()
以前被調用,用於生成實例對象。利用這個方法和類的屬性的特色能夠實現設計模式的單例模式。單例模式是指建立惟一對象,單例模式設計的類只能實例 這個絕對常考啊.絕對要記住1~2個方法,當時面試官是讓手寫的.
__new__
方法class Singleton(object): def __new__(cls, *args, **kw): if not hasattr(cls, '_instance'): orig = super(Singleton, cls) cls._instance = orig.__new__(cls, *args, **kw) return cls._instance class MyClass(Singleton): a = 1
建立實例時把全部實例的__dict__
指向同一個字典,這樣它們具備相同的屬性和方法.
class Borg(object): _state = {} def __new__(cls, *args, **kw): ob = super(Borg, cls).__new__(cls, *args, **kw) ob.__dict__ = cls._state return ob class MyClass2(Borg): a = 1
def singleton(cls): instances = {} def getinstance(*args, **kw): if cls not in instances: instances[cls] = cls(*args, **kw) return instances[cls] return getinstance @singleton class MyClass: ...
做爲python的模塊是自然的單例模式
# mysingleton.py class My_Singleton(object): def foo(self): pass my_singleton = My_Singleton() # to use from mysingleton import my_singleton my_singleton.foo()
Python 除了擁有實例方法外,還擁有靜態方法和類方法,跟Java相比須要理解這個類方法的含義。
class Foo(object): def test(self): #定義了實例方法 print("object") @classmethod # 裝飾器 def test2(clss): #定義了類方法 print("class") @staticmethod # 裝飾器 def test3(): #定義了靜態方法 print("static")
實例方法訪問方式:
ff=Foo() ff.test();//經過實例調用 Foo.test(ff)//直接經過類的方式調用,可是須要本身傳遞實例引用
類方法訪問方式:
Foo.test2();
若是Foo有了子類而且子類覆蓋了這個類方法,最終調用會調用子類的方法並傳遞的是子類的類對象。
class Foo2(Foo): @classmethod def test2(self): print(self) print("foo2 object") f2=Foo2() print(f2.test2())
輸出結果:
<class '__main__.Foo2'>
foo2 object
None
靜態方法調用方式:
Foo.test3();//直接靜態方式調用
總結:
其實經過以上能夠看出:
實例方法,類方法,靜態方法均可以經過實例或者類調用,只不過實例方法經過類調用時須要傳遞實例的引用(python 3能夠傳遞任意對象,其餘版本會報錯)。
三種方法從不一樣層次上來對方法進行了描述:實例方法針對的是實例,類方法針對的是類,他們均可以繼承和從新定義,而靜態方法則不能繼承,能夠認爲是全局函數。
>>> class MyClass(): ... def __init__(self): ... self.__superprivate = "Hello" ... self._semiprivate = ", world!" ... >>> mc = MyClass() >>> print mc.__superprivate Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: myClass instance has no attribute '__superprivate' >>> print mc._semiprivate , world! >>> print mc.__dict__ {'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}
__foo__
:一種約定,Python內部的名字,用來區別其餘用戶自定義的命名,以防衝突,就是例如__init__()
,__del__()
,__call__()
這些特殊方法
_foo
:一種約定,用來指定變量私有.程序員用來指定私有變量的一種方式.不能用from module import * 導入,其餘方面和公有同樣訪問;
__foo
:這個有真正的意義:解析器用_classname__foo
來代替這個名字,以區別和其餘類相同的命名,它沒法直接像公有成員同樣隨便訪問,經過對象名._類名__xxx這樣的方式能夠訪問.
或者: http://www.zhihu.com/question/19754941
1. __doc__ 描述類的信息
class Foo(object): # 單引號和雙引號均可以 """這裏描述類的信息""" def func(self): pass print(Foo.__doc__)
顯示的結果:
2. __call__ 對象後面加括號,觸發執行
# __call__方法的執行是由對象加括號觸發的,即:對象()或者 類()() class Foo(object): def __call__(self, *args, **kwargs): print("running call", args, kwargs) foo = Foo() foo(1, 2, 3, name = "UserPython") Foo()(1, 2, 3, name = "UserPython")
顯示的結果:
3. __dict__ 查看類或對象中的全部成員
class Foo(object): def __init__(self, name, age): self.name = name self.age = age foo = Foo("UserPython", 17) print(Foo.__dict__) #打印類裏的全部屬性,不包括實例屬性 print(foo.__dict__) #打印全部實例屬性,不包括類屬性
顯示的結果:
{'__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__init__': <function Foo.__init__ at 0x0000000000BB0730>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__module__': '__main__', '__doc__': None}
{'name': 'UserPython', 'age': 17}
4. __str__ 若是一個類中定義了__str__方法,那麼在打印對象時,默認輸出該方法的返回值
class Foo(object): def __init__(self, name, age): self.name = name self.age = age def __str__(self): return "<obj:%s>" % self.name foo = Foo("UserPython", 17) print(foo) #>>><obj:UserPython>
顯示的效果爲:
<obj:UserPython>
5. __getitem__ 、 __setitem__ 、__delitem__ 用於索引操做,如字典。分別表示獲取、設置、刪除數據
class Foo(object): def __getitem__(self, key): print("__getitem__", key) def __setitem__(self, key, value): print("__setitem__", key, value) def __delitem__(self, key): print("__delitem__", key) foo = Foo() foo["name"] = "UserPython" #>>>__setitem__ name UserPython 觸發__setitem__ foo["name"] #>>>__getitem__ name 觸發__getitem__ del foo["name"] #>>>__delitem__ name 觸發__delitem__
顯示的結果爲:
6. __new__ 、__metaclass__
class Foo(object): def __init__(self, name): self.name = name foo = Foo("UserPython") ''''' 上述代碼中,foo是經過Foo類實例化的對象,其實,不只foo是一個對象,Foo類自己也是一個對象,由於在Python中一切事物都是對象。 若是按照一切事物都是對象的理論:foo對象時經過執行Foo類的構造方法建立,那麼Foo類對象應該也是經過執行某個類的構造方法建立。 ''' print(type(foo)) print(type(Foo)) # 因此,foo對象是Foo類的一個實例,Foo類對象是type類的一個實例,即:Foo類對象是經過type類的構造方法建立。那麼,建立類就能夠有兩種方式了
顯示的結果爲:
# 普通方式 class Foo(object): def func(self): print("hello UserPython")
# 特殊方式 def func(self): print("hello %s" % self.name) def __init__(self, name, age): #構造方法 self.name = name self.age = age # 建立了一個type類,而後用type類實例化了一個Foo類,因爲Foo自己是一個類,因此Foo又實例化了一個對象foo Foo = type('Foo', (object, ), {"func" : func, "__init__" : __init__}) foo = Foo("UserPython", 19) foo.func() print(type(Foo))
顯示的結果爲:
對編程語言比較熟悉的朋友,應該知道「反射」這個機制。Python做爲一門動態語言,固然不會缺乏這一重要功能。然而,在網絡上卻不多見到有詳細或者深入的剖析論文。下面結合一個web路由的實例來闡述python的反射機制的使用場景和核心本質。
1、前言
def f1(): print("f1是這個函數的名字!") s = "f1" print("%s是個字符串" % s)
在上面的代碼中,咱們必須區分兩個概念,f1和「f1"。前者是函數f1的函數名,後者只是一個叫」f1「的字符串,二者是不一樣的事物。咱們能夠用f1()的方式調用函數f1,但咱們不能用"f1"()的方式調用函數。說白了就是,不能經過字符串來調用名字看起來相同的函數!
2、web實例
考慮有這麼一個場景,根據用戶輸入的url的不一樣,調用不一樣的函數,實現不一樣的操做,也就是一個url路由器的功能,這在web框架裏是核心部件之一。下面有一個精簡版的示例:
首先,有一個commons模塊,它裏面有幾個函數,分別用於展現不一樣的頁面,代碼以下:
def login(): print("這是一個登錄頁面!") def logout(): print("這是一個退出頁面!") def home(): print("這是網站主頁面!")
其次,有一個visit模塊,做爲程序入口,接受用戶輸入,展現相應的頁面,代碼以下:(這段代碼是比較初級的寫法)
import commons def run(): inp = input("請輸入您想訪問頁面的url: ").strip() if inp == "login": commons.login() elif inp == "logout": commons.logout() elif inp == "home": commons.home() else: print("404") if __name__ == '__main__': run()
咱們運行visit.py,輸入:home,頁面結果以下:
請輸入您想訪問頁面的url: home
這是網站主頁面!
這就實現了一個簡單的WEB路由功能,根據不一樣的url,執行不一樣的函數,得到不一樣的頁面。
然而,讓咱們考慮一個問題,若是commons模塊裏有成百上千個函數呢(這很是正常)?。難道你在visit模塊裏寫上成百上千個elif?顯然這是不可能的!那麼怎麼破?
3、反射機制
仔細觀察visit中的代碼,咱們會發現用戶輸入的url字符串和相應調用的函數名好像!若是能用這個字符串直接調用函數就行了!可是,前面咱們已經說了字符串是不能用來調用函數的。爲了解決這個問題,python爲咱們提供一個強大的內置函數:getattr!咱們將前面的visit修改一下,代碼以下:
import commons def run(): inp = input("請輸入您想訪問頁面的url: ").strip() func = getattr(commons,inp) func() if __name__ == '__main__': run()
首先說明一下getattr函數的使用方法:它接收2個參數,前面的是一個對象或者模塊,後面的是一個字符串,注意了!是個字符串!
例子中,用戶輸入儲存在inp中,這個inp就是個字符串,getattr函數讓程序去commons這個模塊裏,尋找一個叫inp的成員(是叫,不是等於),這個過程就至關於咱們把一個字符串變成一個函數名的過程。而後,把得到的結果賦值給func這個變量,實際上func就指向了commons裏的某個函數。最後經過調用func函數,實現對commons裏函數的調用。這徹底就是一個動態訪問的過程,一切都不寫死,所有根據用戶輸入來變化。
執行上面的代碼,結果和最開始的是同樣的。
這就是python的反射,它的核心本質其實就是利用字符串的形式去對象(模塊)中操做(查找/獲取/刪除/添加)成員,一種基於字符串的事件驅動!
這段話,不必定準確,但大概就是這麼個意思。
4、進一步完善
上面的代碼還有個小瑕疵,那就是若是用戶輸入一個非法的url,好比jpg,因爲在commons裏沒有同名的函數,確定會產生運行錯誤,具體以下:
請輸入您想訪問頁面的url: jpg Traceback (most recent call last): File "F:/Python/pycharm/s13/reflect/visit.py", line 16, in <module> run() File "F:/Python/pycharm/s13/reflect/visit.py", line 11, in run func = getattr(commons,inp) AttributeError: module 'commons' has no attribute 'jpg'
那怎麼辦呢?其實,python考慮的很全面了,它一樣提供了一個叫hasattr
的內置函數,用於判斷commons中是否具備某個成員。咱們將代碼修改一下:
import commons def run(): inp = input("請輸入您想訪問頁面的url: ").strip() if hasattr(commons,inp): func = getattr(commons,inp) func() else: print("404") if __name__ == '__main__': run()
經過hasattr
的判斷,能夠防止非法輸入錯誤,並將其統必定位到錯誤頁面。
其實,研究過python內置函數的朋友,應該注意到還有delattr
和setattr
兩個內置函數。從字面上已經很好理解他們的做用了。
python的四個重要內置函數:getattr
、hasattr
、delattr
和setattr
較爲全面的實現了基於字符串的反射機制。他們都是對內存內的模塊進行操做,並不會對源文件進行修改。
5、動態導入模塊
上面的例子是在某個特定的目錄結構下才能正常實現的,也就是commons和visit模塊在同一目錄下,而且全部的頁面處理函數都在commons模塊內。以下圖:
但在現實使用環境中,頁面處理函數每每被分類放置在不一樣目錄的不一樣模塊中,也就是以下圖:
難道咱們要在visit模塊裏寫上一大堆的import 語句逐個導入account、manage、commons模塊嗎?要是有1000個這種模塊呢?
剛纔咱們分析完了基於字符串的反射,實現了動態的函數調用功能,咱們不由會想那麼能不能動態導入模塊呢?這徹底是能夠的!
python提供了一個特殊的方法:__import__(字符串參數)。
經過它,咱們就能夠實現相似的反射功能。__import__()方法會根據參數,動態的導入同名的模塊。
咱們再修改一下上面的visit模塊的代碼。
def run(): inp = input("請輸入您想訪問頁面的url: ").strip() modules, func = inp.split("/") obj = __import__(modules) if hasattr(obj, func): func = getattr(obj, func) func() else: print("404") if __name__ == '__main__': run()
運行一下:
請輸入您想訪問頁面的url: commons/home
這是網站主頁面!
請輸入您想訪問頁面的url: account/find
這是一個查找功能頁面!
咱們來分析一下上面的代碼:
首先,咱們並無定義任何一行import語句;
其次,用戶的輸入inp被要求爲相似「commons/home」這種格式,其實也就是模擬web框架裏的url地址,斜槓左邊指向模塊名,右邊指向模塊中的成員名。
而後,modules,func = inp.split("/")
處理了用戶輸入,使咱們得到的2個字符串,並分別保存在modules和func變量裏。
接下來,最關鍵的是obj = __import__(modules)
這一行,它讓程序去導入了modules這個變量保存的字符串同名的模塊,並將它賦值給obj變量。
最後的調用中,getattr去modules模塊中調用func成員的含義和之前是同樣的。
總結:經過__import__
函數,咱們實現了基於字符串的動態的模塊導入。
一樣的,這裏也有個小瑕疵!
若是咱們的目錄結構是這樣的:
那麼在visit的模塊調用語句中,必須進行修改,咱們想固然地會這麼作:
def run(): inp = input("請輸入您想訪問頁面的url: ").strip() modules, func = inp.split("/") obj = __import__("lib." + modules) #注意字符串的拼接 if hasattr(obj, func): func = getattr(obj, func) func() else: print("404") if __name__ == '__main__': run()
改了這麼一個地方:obj = __import__("lib." + modules),
看起來彷佛沒什麼問題,和import lib.commons
的傳統方法相似,但實際上運行的時候會有錯誤。
請輸入您想訪問頁面的url: commons/home
404
請輸入您想訪問頁面的url: account/find
404
爲何呢?由於對於lib.xxx.xxx.xxx這一類的模塊導入路徑,__import__默認只會導入最開頭的圓點左邊的目錄,也就是「lib」。咱們能夠作個測試,在visit同級目錄內新建一個文件,代碼以下:
obj = __import__("lib.commons") print(obj)
執行結果:
<module 'lib' (namespace)>
這個問題怎麼解決呢?加上fromlist = True參數便可!
def run(): inp = input("請輸入您想訪問頁面的url: ").strip() modules, func = inp.split("/") obj = __import__("lib." + modules, fromlist=True) # 注意fromlist參數 if hasattr(obj, func): func = getattr(obj, func) func() else: print("404") if __name__ == '__main__': run()
至此,動態導入模塊的問題基本都解決了,只剩下最後一個,那就是萬一用戶輸入錯誤的模塊名呢?好比用戶輸入了somemodules/find
,因爲實際上不存在somemodules
這個模塊,必然會報錯!那有沒有相似上面hasattr內置函數這麼個功能呢?答案是沒有!碰到這種,你只能經過異常處理來解決。
6、最後的思考
可能有人會問python不是有兩個內置函數exec
和eval
嗎?他們一樣可以執行字符串。好比:
exec("print('haha')")
結果:
haha
那麼直接使用它們不行嗎?非要那麼費勁地使用getattr
, __import__
幹嗎?
其實,在上面的例子中,圍繞的核心主題是如何利用字符串驅動不一樣的事件,好比導入模塊、調用函數等等,這些都是python的反射機制,是一種編程方法、設計模式的體現,凝聚了高內聚、鬆耦合的編程思想,不能簡單的用執行字符串來代替。固然,exec和eval也有它的舞臺,在web框架裏也常常被使用。
Python 的模塊就是自然的單例模式,由於模塊在第一次導入時,會生成 .pyc
文件,當第二次導入時,就會直接加載 .pyc
文件,而不會再次執行模塊代碼。
#foo1.py class Singleton(object): def foo(self): pass singleton = Singleton() #foo.py from foo1 import singleton
直接在其餘文件中導入此文件中的對象,這個對象便是單例模式的對象
先執行了類的__new__方法(咱們沒寫時,默認調用object.__new__),實例化對象;而後再執行類的__init__方法,對這個對象進行初始化,全部咱們能夠基於這個,實現單例模式。
class Singleton(object): def __new__(cls,a): if not hasattr(cls, '_instance'): cls._instance = object.__new__(cls) return cls._instance def __init__(self,a): self.a = a def aa(self): print(self.a) a = Singleton("a")
變種:利用類的靜態方法或者類方法,實現對函數初始化的控制。該方法須要手動調用靜態方法實現實例。本質上是手動版的__new__方法。
3、元類方法
此方法是在__new__方法的更上層對實例化過程進行控制。
原理:執行元類的 元類的__new__方法和__init__方法用來實例化類對象,__call__ 方法用來對實例化的對象的實例即類的對象進行控制。__call__方法會調用實例類的 __new__方法,用於建立對象。返回對象給__call__方法,而後調用類對象的 __init__方法,用於對對象初始化。
class Singleton1(type): def __init__(self, *args, **kwargs): self.__instance = None super(Singleton1,self).__init__(*args, **kwargs) def __call__(self, *args, **kwargs): if self.__instance is None: self.__instance = super(Singleton1,self).__call__(*args, **kwargs) return self.__instance class Singleton2(type): _inst = {} def __call__(cls, *args, **kwargs): print(cls) if cls not in cls._inst: cls._inst[cls] = super(Singleton2, cls).__call__(*args) return cls._inst[cls] class C(metaclass=Singleton1): pass
4、裝飾器
原理:裝飾器用來控制類調用__call__方法。
def singleton(cls, *args, **kw): instance = {} def _singleton(args): if cls not in instance: instance[cls] = cls(*args, **kw) return instance[cls] return _singleton @singleton class A: pass
Python 中,一個變量的做用域老是由在代碼中被賦值的地方所決定的。
當 Python 遇到一個變量的話他會按照這樣的順序進行搜索:
本地做用域(Local)→當前做用域被嵌入的本地做用域(Enclosing locals)→全局/模塊做用域(Global)→內置做用域(Built-in)
閉包(closure)是函數式編程的重要的語法結構。閉包也是一種組織代碼的結構,它一樣提升了代碼的可重複使用性。
當一個內嵌函數引用其外部做做用域的變量,咱們就會獲得一個閉包. 總結一下,建立一個閉包必須知足如下幾點:
感受閉包仍是有難度的,幾句話是說不明白的,仍是查查相關資料.
重點是函數運行後並不會被撤銷,就像16題的instance字典同樣,當函數運行完後,instance並不被銷燬,而是繼續留在內存空間裏.這個功能相似類裏的類變量,只不過遷移到了函數上.
閉包就像個空心球同樣,你知道外面和裏面,但你不知道中間是什麼樣.
應用場景:
一、受權(Authorization)
裝飾器能有助於檢查某我的是否被受權去使用一個web應用的端點(endpoint)。它們被大量使用於Flask和Django web框架中。這裏是一個例子來使用基於裝飾器的受權:
from functools import wraps # 最新版python引用是 import functools def requires_auth(f): # f 就是咱們須要裝飾的函數,一看就是不帶參數的裝飾器 @wraps(f) # 新版python寫法 @functools.wraps(f) def decorated(*args, **kwargs): auth = request.authorization if not auth or not check_auth(auth.username, auth.password): authenticate() return f(*args, **kwargs) return decorated # 該裝飾器需相關配置才能運行,這裏是截取代碼展現應用
日誌是裝飾器運用的另外一個亮點。這是個例子:
from functools import wraps def logit(func): @wraps(func) def with_logging(*args, **kwargs): print(func.__name__ + " was called") return func(*args, **kwargs) return with_logging @logit def addition_func(x): """Do some math.""" return x + x result = addition_func(4)
我敢確定你已經在思考裝飾器的一個其餘聰明用法了。
帶參數的裝飾器是典型的閉包函數
咱們回到日誌的例子,並建立一個包裹函數,能讓咱們指定一個用於輸出的日誌文件。
from functools import wraps def logit(logfile='out.log'): def logging_decorator(func): @wraps(func) def wrapped_function(*args, **kwargs): log_string = func.__name__ + " was called" print(log_string) # 打開logfile,並寫入內容 with open(logfile, 'a') as opened_file: # 如今將日誌打到指定的logfile opened_file.write(log_string + '\n') return func(*args, **kwargs) return wrapped_function return logging_decorator @logit() def myfunc1(): pass myfunc1() # Output: myfunc1 was called # 如今一個叫作 out.log 的文件出現了,裏面的內容就是上面的字符串 @logit(logfile='func2.log') def myfunc2(): pass myfunc2() # Output: myfunc2 was called # 如今一個叫作 func2.log 的文件出現了,裏面的內容就是上面的字符串
5.、裝飾器類
如今咱們有了能用於正式環境的logit裝飾器,但當咱們的應用的某些部分還比較脆弱時,異常也許是須要更緊急關注的事情。比方說有時你只想打日誌到一個文件。而有時你想把引發你注意的問題發送到一個email,同時也保留日誌,留個記錄。這是一個使用繼承的場景,但目前爲止咱們只看到過用來構建裝飾器的函數。
幸運的是,類也能夠用來構建裝飾器。那咱們如今以一個類而不是一個函數的方式,來從新構建logit。
isinstance做用:來判斷一個對象是不是一個已知的類型;
其第一個參數(object)爲對象,第二個參數爲類型名(int...)或類型名的一個列表((int,list,float)是一個列表)。其返回值爲布爾型(True or flase)。
若對象的類型與參數二的類型相同則返回True。若參數二爲一個元組,則若對象類型與元組中類型名之一相同即返回True。
簡單來講就是判斷object是否與第二個參數的類型相同,舉例以下:
# -*- coding: utf-8 -*- p = '123' print "1.",isinstance(p,str)#判斷P是不是字符串類型 a = "中國" print isinstance(a,unicode) #判斷a是不是Unicode編碼 print isinstance(a,(unicode,str))#判斷a所屬類型是否包含在元組中 list1 = [1,2,3,4,5] print isinstance(list1,list)#判斷list1是不是列表的類型
Python的assert是用來檢查一個條件,若是它爲真,就不作任何事。若是它爲假,則會拋出AssertError而且包含錯誤信息。例如:
x = 23 assert x > 0, "x is not zero or negative" assert x%2 == 0, "x is not an even number"
結果顯示:
不少人用assert做爲一個很快和容易的方法來在參數錯誤的時候拋出異常。但這樣作是錯的,很是錯誤,有兩個緣由。首先AssertError不是在測試參數時應該拋出的錯誤。你不該該像這樣寫代碼:
x = "14" if not isinstance(x, int): raise AssertionError("not an int")
顯示結果:
你應該拋出TypeError的錯誤,assert會拋出錯誤的異常。
那何時應該使用assert?沒有特定的規則,斷言應該用於:
☆防護型的編程
☆運行時檢查程序邏輯
☆檢查約定
☆程序常量
☆檢查文檔
(在測試代碼的時候使用斷言也是可接受的,是一種很方便的單元測試方法,你接受這些測試在用-O標誌運行時不會作任何事。我有時在代碼裏使用assert False來標記沒有寫完的代碼分支,我但願這些代碼運行失敗。儘管拋出NotImplementedError可能會更好。)
關於斷言的意見有不少,由於它能確保代碼的正確性。若是你肯定代碼是正確的,那麼就沒有用斷言的必要了,由於他們歷來不會運行失敗,你能夠直接移除這些斷言。若是你肯定檢查會失敗,那麼若是你不用斷言,代碼就會經過編譯並忽略你的檢查。
在以上兩種狀況下會頗有意思,當你比較確定代碼可是不是絕對確定時。可能你會錯過一些很是古怪的狀況。在這個狀況下,額外的運行時檢查能幫你確保任何錯誤都會盡早地被捕捉到
另外一個好的使用斷言的方式是檢查程序的不變量。一個不變量是一些你須要依賴它爲真的狀況,除非一個bug致使它爲假。若是有bug,最好可以儘早發現,因此咱們爲它進行一個測試,可是又不想減慢代碼運行速度。因此就用斷言,由於它能在開發時打開,在產品階段關閉。
一個非變量的例子多是,若是你的函數但願在它開始時有數據庫的鏈接,而且承諾在它返回的時候仍然保持鏈接,這就是函數的不變量:
def some_function(arg): assert not DB.closed() ... # code goes here assert not DB.closed() return result
斷言自己就是很好的註釋,賽過你直接寫註釋:
# when we reach here, we know that n > 2
你能夠經過添加斷言來確保它:
assert n > 2
斷言也是一種防護型編程。你不是讓你的代碼防護如今的錯誤,而是防止在代碼修改後引起的錯誤。理想狀況下,單元測試能夠完成這樣的工做,但是須要面對的現實是,它們一般是沒有完成的。人們可能在提交代碼前會忘了運行測試代碼。有一個內部檢查是另外一個阻擋錯誤的防線,尤爲是那些不明顯的錯誤,卻致使了代碼出問題而且返回錯誤的結果。
加入你有一些if…elif 的語句塊,你知道在這以前一些須要有一些值
# target is expected to be one of x, y, or z, and nothing else. if target == x: run_x_code() elif target == y: run_y_code() else: run_z_code()
假設代碼如今是徹底正確的。但它會一直是正確的嗎?依賴的修改,代碼的修改。若是依賴修改爲 target = w 會發生什麼,會關係到run_w_code函數嗎?若是咱們改變了代碼,但沒有修改這裏的代碼,可能會致使錯誤的調用 run_z_code 函數並引起錯誤。用防護型的方法來寫代碼會很好,它能讓代碼運行正確,或者立馬執行錯誤,即便你在將來對它進行了修改。
在代碼開頭的註釋很好的一步,可是人們常常懶得讀或者更新註釋。一旦發生這種狀況,註釋會變得沒用。但有了斷言,我能夠同時對代碼塊的假設書寫文檔,而且在它們違反的時候觸發一個乾淨的錯誤
assert target in (x, y, z) if target == x: run_x_code() elif target == y: run_y_code() else: assert target == z run_z_code()
這樣,斷言是一種防護型編程,同時也是一種文檔。我想到一個更好的方案:
if target == x: run_x_code() elif target == y: run_y_code() elif target == z: run_z_code() else: # This can never happen. But just in case it does... raise RuntimeError("an unexpected error occurred")
按約定進行設計是斷言的另外一個好的用途。咱們想象函數與調用者之間有個約定,好比下面的:
「若是你傳給我一個非空字符串,我保證傳會字符串的第一個字母並將其大寫。」
若是約定被函數或調用這破壞,代碼就會出問題。咱們說函數有一些前置條件和後置條件,因此函數就會這麼寫:
def first_upper(astring): assert isinstance(astring, str) and len(astring) > 0 result = astring[0].upper() assert isinstance(result, str) and len(result) == 1 assert result == result.upper() return result
按約定設計的目標是爲了正確的編程,前置條件和後置條件是須要保持的。這是斷言的典型應用場景,由於一旦咱們發佈了沒有問題的代碼到產品中,程序會是正確的,而且咱們能安全的移除檢查。
下面是我建議的不要用斷言的場景:
☆不要用它測試用戶提供的數據
☆不要用斷言來檢查你以爲在你的程序的常規使用時會出錯的地方。斷言是用來檢查很是罕見的問題。你的用戶不該該看到任何斷言錯誤,若是他們看到了,這是一個bug,修復它。
☆有的狀況下,不用斷言是由於它比精確的檢查要短,它不該該是懶碼農的偷懶方式。
☆不要用它來檢查對公共庫的輸入參數,由於它不能控制調用者,因此不能保證調用者會不會打破雙方的約定。
☆不要爲你以爲能夠恢復的錯誤用斷言。換句話說,不用改在產品代碼裏捕捉到斷言錯誤。
☆不要用太多斷言以致於讓代碼很晦澀。
python中的with語句是用來幹嗎的?有什麼做用?
with語句的做用是經過某種方式簡化異常處理,它是所謂的上下文管理器的一種
用法舉例以下:
with open('output.txt', 'w') as f: f.write('Hi there!')
當你要成對執行兩個相關的操做的時候,這樣就很方便,以上即是經典例子,with語句會在嵌套的代碼執行以後,自動關閉文件。這種作法的還有另外一個優點就是,不管嵌套的代碼是以何種方式結束的,它都關閉文件。若是在嵌套的代碼中發生異常,它可以在外部exception handler catch異常前關閉文件。若是嵌套代碼有return/continue/break語句,它一樣可以關閉文件。
咱們也可以本身構造本身的上下文管理器
咱們能夠用contextlib中的context manager修飾器來實現,好比能夠經過如下代碼暫時改變當前目錄而後執行必定操做後返回。
from contextlib import contextmanager import os @contextmanager def working_directory(path): current_dir = os.getcwd() os.chdir(path) try: yield finally: os.chdir(current_dir) with working_directory("data/stuff"): # do something within data/stuff # here I am back again in the original working directory
一、可迭代對象與迭代器的區別
可迭代對象:指的是具有可迭代的能力,即enumerable. 在Python中指的是能夠經過for-in 語句去逐個訪問元素的一些對象,好比元組tuple,列表list,字符串string,文件對象file 等。
迭代器:指的是經過另外一種方式去一個一個訪問可迭代對象中的元素,即enumerator。在python中指的是給內置函數iter()傳遞一個可迭代對象做爲參數,返回的那個對象就是迭代器,而後經過迭代器的next()方法逐個去訪問。
二、生成器
生成器的本質就是一個逐個返回元素的函數,即「本質——函數」
生成器有什麼好處?
最大的好處在於它是「延遲加載」,即對於處理長序列問題,更加的節省存儲空間。即生成器每次在內存中只存儲一個值,好比打印一個斐波拉切數列:原始的方法能夠以下所示:
def fab(max): n, a, b = 0, 0, 1 L = [] while n < max: L.append(b) a, b = b, a + b n = n + 1 return L
這樣作最大的問題在於將全部的元素都存儲在了L裏面,很佔用內存,而使用生成器則以下所示
def fab(max): n, a, b = 0, 0, 1 while n < max: yield b #每次迭代時值加載這一個元素,並且替換掉以前的那一個元素,這樣就大大節省了內存。並且程序在碰見yield語句時會 停下來,這是後面使用yield阻斷原理進行多線程編程的一個啓發,(python協程編程會在後面講到) a, b = b, a + b n = n + 1
生成器其實就是下面這個樣子,寫得簡單一些就是一次返回一條,以下:
def generator(): for i in range(5): yield i def generator_1(): yield 1 yield 2 yield 3 yield 4 yield 5
上面這兩種方式是徹底等價的,只不過前者更簡單一些。
三、什麼又是yield from呢?
簡單地說,yield from generator 。實際上就是返回另一個生成器。以下所示:
def generator1(): item = range(10) for i in item: yield i def generator2(): yield 'a' yield 'b' yield 'c' yield from generator1() #yield from iterable本質上等於 for item in iterable: yield item的縮寫版 yield from [11,22,33,44] yield from (12,23,34) yield from range(3) for i in generator2() : print(i)
從上面的代碼能夠看書,yield from 後面能夠跟的式子有「 生成器 元組 列表等可迭代對象以及range()函數產生的序列」
上面代碼運行的結果爲:
a
b
c
0
1
2
3
4
5
6
7
8
9
11
22
33
44
12
23
34
0
1
2
一、使用生成器,由於能夠節約大量內存
二、循環代碼優化,避免過多重複代碼的執行
三、核心模塊用Cython PyPy等,提升效率
四、多進程、多線程、協程
五、多個if elif條件判斷,能夠把最有可能先發生的條件放到前面寫,這樣能夠減小程序判斷的次數,提升效率
IOError:輸入輸出異常
AttributeError:試圖訪問一個對象沒有的屬性
ImportError:沒法引入模塊或包,基本是路徑問題
IndentationError:語法錯誤,代碼沒有正確的對齊
IndexError:下標索引超出序列邊界
KeyError:試圖訪問你字典裏不存在的鍵
SyntaxError:Python代碼邏輯語法出錯,不能執行
NameError:使用一個還未賦予對象的變量