對于Python這么語言,可以當作一門興趣或愛好來學習,但是若是想找到份好的作業還是謹慎為主,這也是為什么不建議你搞Python的原因,
如果說,有些讀者的學歷非常牛逼,然后學習能力也非常強,那么選擇人工智能、機器學習、資料分析,我覺得前途是光明的,既能賺錢,待遇又好,還不可替代,不學 Python 絕對虧,
如果說,有些讀者學歷一般,做程式員僅僅是為了糊口飯吃,那么我覺得可以把 Python 作為第二語言來學,不要當做主語言,搞點范圍許可內的爬蟲,自動化測驗,我就覺得挺好的,況且 Python 這門語言本身是非常優秀的,不然怎么搞人工智能,海量資料分析,對吧?

文章目錄
- 1、輸入輸出
- 2、縮進和注釋
- 3、資料型別和變數
- 整數
- 浮點數
- 4、字串
- 5、布林值
- 6、空值
- 7、變數
- 8、常量
- 9、除法計算結果是浮點數
- 10、Python中的is和==
- 11、編碼
- \u則代表unicode編碼,是一個字符
- 12、Python3注釋
- 13、運算子
- 邏輯運算子
- 位運算子
- 賦值運算子
- 算術運算子
- 比較運算子
- 身份運算子
- 成員運算子
- 14、數字型別轉換
- 15、list-串列
- 串列腳本運算子
- 串列截取與拼接
- 串列函式&方法
- 16、元組-tuple
- 17、if else
- 再議 input
- 18、回圈
- 19、break、continue和pass
- 20、Dictionary--字典
- 21、函式
- python 傳不可變物件實體
- 傳可變物件實體
- 命名關鍵字引數
- 關鍵字引數
- 命名關鍵字引數
- 默認引數
- 可變引數
- **`*nums`表示把`nums`這個list的所有元素作為可變引數傳進去,這種寫法相當有用,而且很常見,**
- 匿名函式
- 語法
- 實體(Python 2.0+)
- 回傳多個值
- 全域變數和區域變數
- 實體(Python 2.0+)
- 函式別名
- Import 與 from import
- 包的概念 packa包中
- 22、切片
- 23、迭代
- 24、串列生成式
- 25、生成器
- 26、迭代器
- 27、函式式編程
- 1、高階函式
- 變數可以指向函式
- 函式名也是變數
- 2、map/reduce
- sorted
- 3、回傳函式
- 閉包
- 4、lambda
- 5、裝飾器
- 6、偏函式
- 28、模塊
- 私有變數和函式
- 第三方模塊
- 模塊搜索路徑
- 29、類和實體
- 30、訪問限制
- 31、繼承和多型
- 32、獲取物件資訊
- 使用isinstance()
- dir()
- 33、__slots__限制實體系結任何屬性和方法
- 34、@property @xxx.setter
- 35、多繼承
- 36、定制類
- __str__
1、輸入輸出
-
輸出
-
print()在括號中加上字串,就可以向螢屏上輸出指定的文字 -
print()函式也可以接受多個字串,用逗號“,”隔開,就可以連成一串輸出;-
print('The quick brown fox', 'jumps over', 'the lazy dog') The quick brown fox jumps over the lazy dogprint()會依次列印每個字串,遇到逗號“,”會輸出一個空格
-
-
print()也可以列印整數,或者計算結果-
print('100 + 200 =', 100 + 200)
-
-
-
輸入
-
Python提供了一個
input(),可以讓用戶輸入字串,并存放到一個變數里-
name = input()
-
-
2、縮進和注釋
- 以
#開頭的陳述句是注釋 - 每一行都是一個陳述句,當陳述句以冒號
:結尾時,縮進的陳述句視為代碼塊; - 縮進有利有弊,好處是強迫你寫出格式化的代碼,但沒有規定縮進是幾個空格還是Tab,按照約定俗成的慣例,應該始終堅持使用4個空格的縮進,縮進的另一個好處是強迫你寫出縮進較少的代碼,你會傾向于把一段很長的代碼拆分成若干函式,從而得到縮進較少的代碼,縮進的壞處就是“復制-粘貼”功能失效了,這是最坑爹的地方,當你重構代碼時,粘貼過去的代碼必須重新檢查縮進是否正確,此外,IDE很難像格式化Java代碼那樣格式化Python代碼,
- Python程式是大小寫敏感的,如果寫錯了大小寫,程式會報錯
3、資料型別和變數
-
整數
-
Python可以處理任意大小的整數,當然包括負整數,在程式中的表示方法和數學上的寫法一模一樣
-
dec = int(input("輸入數字:")) print("十進制數為:", dec) print("轉換為二進制為:", bin(dec)) print("轉換為八進制為:", oct(dec)) # 0o開頭 print("轉換為十六進制為", hex(dec)) # 0x開
-
-
浮點數
-
https://www.cnblogs.com/qinchao0317/p/10699717.html
-
保留小數點位數
-
使用字串格式化
-
-
x = 1234.56789 print(format(x, '0.2f')) 1234.57占位符 替換內容 %d 整數 %f 浮點數 %s 字串 %x 十六進制整數
-
-
使用round內置函式
-
dec = float(input("輸入的數字")) print("dec*dec =",round(dec*dec,2));
-
-
-
數值型別轉換
-
int(x [,base ]) 將x轉換為一個整數 long(x [,base ]) 將x轉換為一個長整數 float(x ) 將x轉換到一個浮點數 complex(real [,imag ]) 創建一個復數 str(x ) 將物件 x 轉換為字串 repr(x ) 將物件 x 轉換為運算式字串 eval(str ) 用來計算在字串中的有效Python運算式,并回傳一個物件 tuple(s ) 將序列 s 轉換為一個元組 list(s ) 將序列 s 轉換為一個串列 chr(x ) 將一個整數轉換為一個字符 unichr(x ) 將一個整數轉換為Unicode字符 ord(x ) 將一個字符轉換為它的整數值 hex(x ) 將一個整數轉換為一個十六進制字串 oct(x ) 將一個整數轉換為一個八進制字串
-
-
4、字串
-
字串是以單引號
'或雙引號"括起來的任意文本;如果'本身也是一個字符,那就可以用""括起來,比如"I'm OK"包含的字符是I,',m,空格,O,K這6個字符,如果字串內部既包含'又包含"怎么辦?可以用轉義字符\來標識;-
print("i'm okss") print('i\'m \" ok')
轉義字符
\可以轉義很多字符,比如\n表示換行,\t表示制表符,字符\本身也要轉義,所以\\表示的字符就是\,print('i\nam\nok')Python還允許用
r''表示''內部的字串默認不轉義print(r'xxjis\nosk\t')如果字串內部有很多換行,用
\n寫在一行里不好閱讀,為了簡化,Python允許用'''...'''的格式表示多行內容print('''zzxsx zxsaxa sasads s''') -
5、布林值
-
個布林值只有
True、False兩種值,要么是True,要么是False -
and、 or、 not
-
print(True and True) print(not 1>2)
6、空值
-
None表示空值,它是一個特殊 Python 物件, None的型別是NoneType
-
print(type(None)) #<class 'NoneType'>
-
7、變數
-
變數在程式中就是用一個變數名表示了,變數名必須是大小寫英文、數字和
_的組合,且不能用數字開頭 -
變數本身型別不固定的語言稱之為動態語言,與之對應的是靜態語言,靜態語言在定義變數時必須指定變數型別,如果賦值的時候型別不匹配,就會報錯,例如Java是靜態語言
-
int a = 123; // a是整數型別變數 a = "ABC"; // 錯誤:不能把字串賦給整型變數
-
8、常量
-
所謂常量就是不能變的變數,比如常用的數學常數π就是一個常量,在Python中,通常用全部大寫的變數名表示常量:
-
PI = 3.14159265359但事實上
PI仍然是一個變數,Python根本沒有任何機制保證PI不會被改變,所以,用全部大寫的變數名表示常量只是一個習慣上的用法,如果你一定要改變變數PI的值,也沒人能攔住你
-
-
賦值
-
a,b,c=1,2,3 a,b=b,a+b print(a,b)
-
9、除法計算結果是浮點數
-
print(10/3) print(10//3)/除法計算結果是浮點數,即使是兩個整數恰好整除,結果也是浮點數還有一種除法是
//,稱為地板除,兩個整數的除法仍然是整數%為取余計算
10、Python中的is和==
-
==是python標準運算子中的比較運算子,用來比較判斷兩個物件的value(值)是否相等
-
is是比較參考地址是否相等
-
Python中,萬物皆物件!
每個物件包含3個屬性,id,type,value
id就是物件地址,可以通過內置函式id()查看物件參考的地址,
type就是物件型別,可以通過內置函式type()查看物件的型別,
value就是物件的值,
11、編碼
-
Python 3版本中,字串是以Unicode編碼的
-
對于單個字符的編碼,Python提供了
ord()函式獲取字符的整數表示,chr()函式把編碼轉換為對應的字符:-
print(ord('a')) print(chr(97)) -
\u則代表unicode編碼,是一個字符
-
print('\u4e2d\u6587') #表示中文的意思
-
-
-
Python對
bytes型別的資料用帶b前綴的單引號或雙引號表示-
print('ABC'.encode('ascii')) //b'ABC'區分
'ABC'和b'ABC',前者是str,后者雖然內容顯示得和前者一樣,但bytes的每個字符都只占用一個位元組,
-
-
>>> b'ABC'.decode('ascii') 'ABC' >>> '中文'.encode('utf-8') b'\xe4\xb8\xad\xe6\x96\x87' >>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8') '中文'如果我們從網路或磁盤上讀取了位元組流,那么讀到的資料就是
bytes,要把bytes變為str,就需要用decode()方法:>>> b'ABC'.decode('ascii') 'ABC' >>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8') '中文'如果
bytes中只有一小部分無效的位元組,可以傳入errors='ignore'忽略錯誤的位元組:>>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore') '中'要計算
str包含多少個字符,可以用len()函式:>>> len('ABC') 3 >>> len('中文') 2 -
由于Python源代碼也是一個文本檔案,所以,當你的源代碼中包含中文的時候,在保存源代碼時,就需要務必指定保存為UTF-8編碼,當Python解釋器讀取源代碼時,為了讓它按UTF-8編碼讀取,我們通常在檔案開頭寫上這兩行:
-
#!/usr/bin/env python3 # -*- coding: utf-8 -*- 或者 #!/usr/bin/env python3 #coding=utf-8
-
12、Python3注釋
- # 這是一個注釋
- 多行注釋用三個單引號 ‘’’ 或者三個雙引號 “”" 將注釋括起來
13、運算子
-
邏輯運算子
-
運算子 邏輯運算式 描述 實體 and x and y 布爾"與" - 如果 x 為 False,x and y 回傳 False,否則它回傳 y 的計算值, (a and b) 回傳 20, or x or y 布爾"或" - 如果 x 是 True,它回傳 x 的值,否則它回傳 y 的計算值, (a or b) 回傳 10, not not x 布爾"非" - 如果 x 為 True,回傳 False ,如果 x 為 False,它回傳 True, not(a and b) 回傳 False
-
-
位運算子
-
& 按位與運算子:參與運算的兩個值,如果兩個相應位都為1,則該位的結果為1,否則為0 (a & b) 輸出結果 12 ,二進制解釋: 0000 1100 | 按位或運算子:只要對應的二個二進位有一個為1時,結果位就為1, (a | b) 輸出結果 61 ,二進制解釋: 0011 1101 ^ 按位異或運算子:當兩對應的二進位相異時,結果為1 (a ^ b) 輸出結果 49 ,二進制解釋: 0011 0001 ~ 按位取反運算子:對資料的每個二進制位取反,即把1變為0,把0變為1,~x 類似于 -x-1 (~a ) 輸出結果 -61 ,二進制解釋: 1100 0011, 在一個有符號二進制數的補碼形式, << 左移動運算子:運算元的各二進位全部左移若干位,由"<<"右邊的數指定移動的位數,高位丟棄,低位補0, a << 2 輸出結果 240 ,二進制解釋: 1111 0000 >> 右移動運算子:把">>“左邊的運算元的各二進位全部右移若干位,”>>"右邊的數指定移動的位數 a >> 2 輸出結果 15 ,二進制解釋: 0000 1111
-
-
賦值運算子
-
運算子 描述 實體 = 簡單的賦值運算子 c = a + b 將 a + b 的運算結果賦值為 c += 加法賦值運算子 c += a 等效于 c = c + a -= 減法賦值運算子 c -= a 等效于 c = c - a *= 乘法賦值運算子 c *= a 等效于 c = c * a /= 除法賦值運算子 c /= a 等效于 c = c / a %= 取模賦值運算子 c %= a 等效于 c = c % a **= 冪賦值運算子 c **= a 等效于 c = c ** a //= 取整除賦值運算子 c //= a 等效于 c = c // a := 海象運算子,可在運算式內部為變數賦值,Python3.8 版本新增運算子, 在這個示例中,賦值運算式可以避免呼叫 len() 兩次: if (n := len(a)) > 10: print(f"List is too long ({n} elements, expected <= 10)")
-
-
算術運算子
-
運算子 描述 實體 + 加 - 兩個物件相加 a + b 輸出結果 31 - 減 - 得到負數或是一個數減去另一個數 a - b 輸出結果 -11 * 乘 - 兩個數相乘或是回傳一個被重復若干次的字串 a * b 輸出結果 210 / 除 - x 除以 y b / a 輸出結果 2.1 % 取模 - 回傳除法的余數 b % a 輸出結果 1 ** 冪 - 回傳x的y次冪 a**b 為10的21次方 // 取整除 - 向下取接近商的整數 >>> 9//2 4 >>> -9//2 -5
-
-
比較運算子
-
運算子 描述 實體 == 等于 - 比較物件是否相等 (a == b) 回傳 False, != 不等于 - 比較兩個物件是否不相等 (a != b) 回傳 True, > 大于 - 回傳x是否大于y (a > b) 回傳 False, < 小于 - 回傳x是否小于y,所有比較運算子回傳1表示真,回傳0表示假,這分別與特殊的變數True和False等價,注意,這些變數名的大寫, (a < b) 回傳 True, >= 大于等于 - 回傳x是否大于等于y, (a >= b) 回傳 False, <= 小于等于 - 回傳x是否小于等于y, (a <= b) 回傳 True,
-
-
身份運算子
-
運算子 描述 實體 is is 是判斷兩個識別符號是不是參考自一個物件 x is y, 類似 id(x) == id(y) , 如果參考的是同一個物件則回傳 True,否則回傳 False is not is not 是判斷兩個識別符號是不是參考自不同物件 x is not y , 類似 id(a) != id(b),如果參考的不是同一個物件則回傳結果 True,否則回傳 False,
-
-
成員運算子
-
運算子 描述 實體 in 如果在指定的序列中找到值回傳 True,否則回傳 False, x 在 y 序列中 , 如果 x 在 y 序列中回傳 True, not in 如果在指定的序列中沒有找到值回傳 True,否則回傳 False, x 不在 y 序列中 , 如果 x 不在 y 序列中回傳 True,
-
14、數字型別轉換
- int(x) 將x轉換為一個整數,
- float(x) 將x轉換到一個浮點數,
15、list-串列
-
序列是Python中最基本的資料結構,序列中的每個元素都分配一個數字 - 它的位置,或索引,第一個索引是0,第二個索引是1,依此類推,
Python有6個序列的內置型別,但最常見的是串列和元組,
序列都可以進行的操作包括索引,切片,加,乘,檢查成員,
此外,Python已經內置確定序列的長度以及確定最大和最小的元素的方法,
-
串列的資料項不需要具有相同的型別
-
list是一種有序的集合,可以隨時添加和洗掉其中的元素,
-
訪問串列 可正負
-
list1=['1','zz','333','444'] print(list1[0]) print(list1[-1])
-
-
list是一個可變的有序表,所以,可以往list中追加元素到末尾:
-
list1.append("zzdc") list1.append("zdc2") print(list1)也可以把元素插入到指定的位置,比如索引號為
1的位置 -
list1.insert(1,222) print(list1)要洗掉list末尾的元素,用
pop()或pop(i)方法 pop會把刪去的元素回傳 -
print(list1.pop()) print(list1.pop(0))要把某個元素替換成別的元素,可以直接賦值給對應的索引位置:
-
list1[0]='mzd' print(list1)list元素也可以是另一個list
-
list1[0]=['1',2,3,4] print(list1)>>> p = ['asp', 'php'] >>> s = ['python', 'java', p, 'scheme']
-
-
串列腳本運算子
-
Python 運算式 結果 描述 len([1, 2, 3]) 3 長度 [1, 2, 3] + [4, 5, 6] [1, 2, 3, 4, 5, 6] 組合 [‘Hi!’] * 4 [‘Hi!’, ‘Hi!’, ‘Hi!’, ‘Hi!’] 重復 3 in [1, 2, 3] True 元素是否存在于串列中 for x in [1, 2, 3]: print(x, end=" ") 1 2 3 迭代 遍歷
-
for var in list1: print(var)
-
-
-
串列截取與拼接
-
操作:
Python 運算式 結果 描述 L[2] ‘Taobao’ 讀取第三個元素 L[-2] ‘Runoob’ 從右側開始讀取倒數第二個元素: count from the right L[1:] [‘Runoob’, ‘Taobao’] 輸出從第二個元素開始后的所有元素 -
變數[頭下標:尾下標] 變數[頭下標:尾下標:步長] list=['123','abc',0,True] x=list[1:] y=list[:3] z=list[2:3] print(x) print(y) print list=['123','abc',0,True] x=list[-3:] y=list[:-2] z=list[-3:-1] print(x) print(y) print(z) list=['123','abc',0,True,"12345"] x=list[1:4:2] print(x)
-
-
串列函式&方法
Python包含以下函式:
序號 函式 1 len(list) 串列元素個數 2 max(list) 回傳串列元素最大值 3 min(list) 回傳串列元素最小值 4 list(seq) 將元組轉換為串列 Python包含以下方法:
序號 方法 1 list.append(obj) 在串列末尾添加新的物件 2 list.count(obj) 統計某個元素在串列中出現的次數 3 list.extend(seq) 在串列末尾一次性追加另一個序列中的多個值(用新串列擴展原來的串列) 4 list.index(obj) 從串列中找出某個值第一個匹配項的索引位置 5 list.insert(index, obj) 將物件插入串列 6 [list.pop(index=-1]) 移除串列中的一個元素(默認最后一個元素),并且回傳該元素的值 7 list.remove(obj) 移除串列中某個值的第一個匹配項 8 list.reverse() 反向串列中元素 9 list.sort( key=None, reverse=False) 對原串列進行排序 10 list.clear() 清空串列 11 list.copy() 復制串列
16、元組-tuple
-
tuple和list非常類似,但是tuple一旦初始化就不能修改
-
不可變的tuple有什么意義?因為tuple不可變,所以代碼更安全,如果可能,能用tuple代替list就盡量用tuple,
-
tuple的陷阱:當你定義一個tuple時,在定義的時候,tuple的元素就必須被確定下來
-
t = (1, 2) t = () #如果要定義一個空的tuple要定義一個只有1個元素的tuple,如果你這么定義
>>> t = (1) >>> t 1定義的不是tuple,是
1這個數!這是因為括號()既可以表示tuple,又可以表示數學公式中的小括號,這就產生了歧義,因此,Python規定,這種情況下,按小括號進行計算,計算結果自然是1,所以,只有1個元素的tuple定義時必須加一個逗號
,,來消除歧義:>>> t = (1,) >>> t (1,)一個“可變的”tuple:
>>> t = ('a', 'b', ['A', 'B']) >>> t[2][0] = 'X' >>> t[2][1] = 'Y' >>> t ('a', 'b', ['X', 'Y'])這個tuple定義的時候有3個元素,分別是
'a','b'和一個list,不是說tuple一旦定義后就不可變了嗎?怎么后來又變了?[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gT70crDv-1612773531303)(1.assets/0.png)]
-
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Qusc6dYV-1612773531305)(1.assets/0-20200713003324698.png)]
表面上看,tuple的元素確實變了,但其實變的不是tuple的元素,而是list的元素,tuple一開始指向的list并沒有改成別的list,所以,tuple所謂的“不變”是說,tuple的每個元素,指向永遠不變,即指向'a',就不能改成指向'b',指向一個list,就不能改成指向其他物件,但指向的這個list本身是可變的!
要創建一個內容也不變的tuple怎么做?那就必須保證tuple的每一個元素本身也不能變
-
以對元組進行連接組合
-
tup1 = (12, 34.56) tup2 = ('abc', 'xyz') # 以下修改元組元素操作是非法的, # tup1[0] = 100 # 創建一個新的元組 tup3 = tup1 + tup2
17、if else
-
if 判斷條件: 執行陳述句…… else: 執行陳述句……當判斷條件為多個值時
if 判斷條件1: 執行陳述句1…… elif 判斷條件2: 執行陳述句2…… elif 判斷條件3: 執行陳述句3…… else: 執行陳述句4……if判斷條件還可以簡寫,比如寫:if x: print('True')只要
x是非零數值、非空字串、非空list等,就判斷為True,否則為False,再議 input
最后看一個有問題的條件判斷,很多同學會用
input()讀取用戶的輸入,這樣可以自己輸入,程式運行得更有意思:birth = input('birth: ') if birth < 2000: print('00前') else: print('00后')輸入
1982,結果報錯:Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unorderable types: str() > int()這是因為
input()回傳的資料型別是str,str不能直接和整數比較,必須先把str轉換成整數,Python提供了int()函式來完成這件事情:s = input('birth: ') birth = int(s) if birth < 2000: print('00前') else: print('00后')再次運行,就可以得到正確地結果,但是,如果輸入
abc呢?又會得到一個錯誤資訊:Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: invalid literal for int() with base 10: 'abc'原來
int()函式發現一個字串并不是合法的數字時就會報錯,程式就退出了,
18、回圈
-
while 判斷條件(condition): 執行陳述句(statements)……
回圈使用 else 陳述句
? 在 python 中,while … else 在回圈條件為 false 時執行 else 陳述句塊:
-
count = 0 while count < 5: print count, " is less than 5" count = count + 1 else: print count, " is not less than 5"
for 回圈陳述句
-
for iterating_var in sequence: statements(s)
在 python 中,for … else 表示這樣的意思,for 中的陳述句和普通的沒有區別,else 中的陳述句會在回圈正常執行完(即 for 不是通過 break 跳出而中斷的)的情況下執行,while … else 也是一樣,
Python 語言允許在一個回圈體里面嵌入另一個回圈,
for iterating_var in sequence:
for iterating_var in sequence:
statements(s)
statements(s)
while expression:
while expression:
statement(s)
statement(s)
19、break、continue和pass
-
Python continue 陳述句跳出本次回圈,而break跳出整個回圈,
-
Python pass 是空陳述句,是為了保持程式結構的完整性,
pass 不做任何事情,一般用做占位陳述句,
-
在 Python3.x 的時候 pass 可以寫或不寫,
python2.x:
def function(): # 空函式在Python2.x版本中pass是必須的 passpython3.x
def function(): # 在Python3.x的時候pass可以寫或不寫 pass
-
20、Dictionary–字典
-
字典是另一種可變容器模型,且可存盤任意型別物件,
字典的每個鍵值 key=>value 對用冒號 : 分割,每個鍵值對之間用逗號 , 分割,整個字典包括在花括號 {} 中 ,格式如下所示:
d = {key1 : value1, key2 : value2 }如果key不存在,dict就會報錯:
>>> d['Thomas'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'Thomas'要避免key不存在的錯誤,有兩種辦法,一是通過
in判斷key是否存在:>>> 'Thomas' in d False二是通過dict提供的
get()方法,如果key不存在,可以回傳None,或者自己指定的value:>>> d.get('Thomas') >>> d.get('Thomas', -1) -1注意:回傳
None的時候Python的互動環境不顯示結果,要洗掉一個key,用
pop(key)方法,對應的value也會從dict中洗掉:>>> d.pop('Bob') 75 >>> d {'Michael': 95, 'Tracy': 85}請務必注意,dict內部存放的順序和key放入的順序是沒有關系的,
和list比較,dict有以下幾個特點:
- 查找和插入的速度極快,不會隨著key的增加而變慢;
- 需要占用大量的記憶體,記憶體浪費多,
而list相反:
- 查找和插入的時間隨著元素的增加而增加;
- 占用空間小,浪費記憶體很少,
所以,dict是用空間來換取時間的一種方法,
這是因為dict根據key來計算value的存盤位置,如果每次計算相同的key得出的結果不同,那dict內部就完全混亂了,這個通過key計算位置的演算法稱為哈希演算法(Hash),
要保證hash的正確性,作為key的物件就不能變,在Python中,字串、整數等都是不可變的,因此,可以放心地作為key,而list是可變的,就不能作為key:
21、函式
-
定義函式
-
在Python中,定義一個函式要使用
def陳述句,依次寫出函式名、括號、括號中的引數和冒號:,然后,在縮進塊中撰寫函式體,函式的回傳值用return陳述句回傳, -
def my_abs(x): if x >= 0: return x else: return -x如果你已經把
my_abs()的函式定義保存為abstest.py檔案了,那么,可以在該檔案的當前目錄下啟動Python解釋器,用from abstest import my_abs來匯入my_abs()函式,注意abstest是檔案名(不含.py擴展名):
-
-
引數的可變與不可變
-
在 python 中,strings, tuples, 和 numbers 是不可更改的物件,而 list,dict 等則是可以修改的物件,
- **不可變型別:**變數賦值 a=5 后再賦值 a=10,這里實際是新生成一個 int 值物件 10,再讓 a 指向它,而 5 被丟棄,不是改變a的值,相當于新生成了a,
- **可變型別:**變數賦值 la=[1,2,3,4] 后再賦值 la[2]=5 則是將 list la 的第三個元素值更改,本身la沒有動,只是其內部的一部分值被修改了,
python 函式的引數傳遞:
- **不可變型別:**類似 c++ 的值傳遞,如 整數、字串、元組,如fun(a),傳遞的只是a的值,沒有影響a物件本身,比如在 fun(a)內部修改 a 的值,只是修改另一個復制的物件,不會影響 a 本身,
- **可變型別:**類似 c++ 的參考傳遞,如 串列,字典,如 fun(la),則是將 la 真正的傳過去,修改后fun外部的la也會受影響
python 中一切都是物件,嚴格意義我們不能說值傳遞還是參考傳遞,我們應該說傳不可變物件和傳可變物件,
-
-
python 傳不可變物件實體
-
#!/usr/bin/python # -*- coding: UTF-8 -*- def ChangeInt( a ): a = 10 b = 2 ChangeInt(b) print b # 結果是 2
-
-
傳可變物件實體
-
!/usr/bin/python # -*- coding: UTF-8 -*- # 可寫函式說明 def changeme( mylist ): "修改傳入的串列" mylist.append([1,2,3,4]) print "函式內取值: ", mylist return # 呼叫changeme函式 mylist = [10,20,30] changeme( mylist ) print "函式外取值: ", mylist
-
-
命名關鍵字引數
-
關鍵字引數和函式呼叫關系緊密,函式呼叫使用關鍵字引數來確定傳入的引數值,
使用關鍵字引數允許函式呼叫時引數的順序與宣告時不一致,因為 Python 解釋器能夠用引數名匹配引數值
-
#!/usr/bin/python # -*- coding: UTF-8 -*- #可寫函式說明 def printme( str ): "列印任何傳入的字串" print str return #呼叫printme函式 printme( str = "My string")
-
-
-
關鍵字引數
**可變引數允許你傳入0個或任意個引數,這些可變引數在函式呼叫時自動組裝為一個tuple,而關鍵字引數允許你傳入0個或任意個含引數名的引數,這些關鍵字引數在函式內部自動組裝為一個dict,**請看示例:
def person(name, age, **kw): print('name:', name, 'age:', age, 'other:', kw)函式
person除了必選引數name和age外,還接受關鍵字引數kw,在呼叫該函式時,可以只傳入必選引數:>>> person('Michael', 30) name: Michael age: 30 other: {}也可以傳入任意個數的關鍵字引數:
>>> person('Bob', 35, city='Beijing') name: Bob age: 35 other: {'city': 'Beijing'} >>> person('Adam', 45, gender='M', job='Engineer') name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}關鍵字引數有什么用?它可以擴展函式的功能,比如,在
person函式里,我們保證能接收到name和age這兩個引數,但是,如果呼叫者愿意提供更多的引數,我們也能收到,試想你正在做一個用戶注冊的功能,除了用戶名和年齡是必填項外,其他都是可選項,利用關鍵字引數來定義這個函式就能滿足注冊的需求,和可變引數類似,也可以先組裝出一個dict,然后,把該dict轉換為關鍵字引數傳進去:
>>> extra = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, city=extra['city'], job=extra['job']) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}當然,上面復雜的呼叫可以用簡化的寫法:
>>> extra = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, **extra) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}**extra表示把extra這個dict的所有key-value用關鍵字引數傳入到函式的**kw引數,kw將獲得一個dict,注意kw獲得的dict是extra的一份拷貝,對kw的改動不會影響到函式外的extra, -
命名關鍵字引數
對于關鍵字引數,函式的呼叫者可以傳入任意不受限制的關鍵字引數,至于到底傳入了哪些,就需要在函式內部通過
kw檢查,仍以
person()函式為例,我們希望檢查是否有city和job引數:def person(name, age, **kw): if 'city' in kw: # 有city引數 pass if 'job' in kw: # 有job引數 pass print('name:', name, 'age:', age, 'other:', kw)但是呼叫者仍可以傳入不受限制的關鍵字引數:
>>> person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456)**如果要限制關鍵字引數的名字,就可以用命名關鍵字引數,例如,只接收
city和job作為關鍵字引數,**這種方式定義的函式如下:def person(name, age, *, city, job): print(name, age, city, job)和關鍵字引數
**kw不同,命名關鍵字引數需要一個特殊分隔符*,*后面的引數被視為命名關鍵字引數,呼叫方式如下:
>>> person('Jack', 24, city='Beijing', job='Engineer') Jack 24 Beijing Engineer如果函式定義中已經有了一個可變引數,后面跟著的命名關鍵字引數就不再需要一個特殊分隔符
*了:def person(name, age, *args, city, job): print(name, age, args, city, job)命名關鍵字引數必須傳入引數名,這和位置引數不同,如果沒有傳入引數名,呼叫將報錯:
>>> person('Jack', 24, 'Beijing', 'Engineer') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: person() takes 2 positional arguments but 4 were given由于呼叫時缺少引數名
city和job,Python解釋器把這4個引數均視為位置引數,但person()函式僅接受2個位置引數,命名關鍵字引數可以有預設值,從而簡化呼叫:
def person(name, age, *, city='Beijing', job): print(name, age, city, job)由于命名關鍵字引數
city具有默認值,呼叫時,可不傳入city引數:>>> person('Jack', 24, job='Engineer') Jack 24 Beijing Engineer使用命名關鍵字引數時,要特別注意,如果沒有可變引數,就必須加一個
*作為特殊分隔符,如果缺少*,Python解釋器將無法識別位置引數和命名關鍵字引數:def person(name, age, city, job): # 缺少 *,city和job被視為位置引數 pass -
默認引數
-
呼叫函式時,默認引數的值如果沒有傳入,則被認為是默認值,下例會列印默認的age,如果age沒有被傳入:
-
#!/usr/bin/python # -*- coding: UTF-8 -*- #可寫函式說明 def printinfo( name, age = 35 ): "列印任何傳入的字串" print "Name: ", name print "Age ", age return #呼叫printinfo函式 printinfo( age=50, name="miki" ) printinfo( name="miki" )定義默認引數要牢記一點:默認引數必須指向不變物件!
def add_end(L=[]): L.append('END') return L >>> add_end() ['END'] >>> add_end() ['END', 'END'] >>> add_end() ['END', 'END', 'END']很多初學者很疑惑,默認引數是
[],但是函式似乎每次都“記住了”上次添加了'END'后的list,原因解釋如下:
Python函式在定義的時候,默認引數
L的值就被計算出來了,即[],因為默認引數L也是一個變數,它指向物件[],每次呼叫該函式,如果改變了L的內容,則下次呼叫時,默認引數的內容就變了,不再是函式定義時的[]了,
-
-
可變引數
在Python函式中,還可以定義可變引數,顧名思義,可變引數就是傳入的引數個數是可變的,可以是1個、2個到任意個,還可以是0個,
我們以數學題為例子,給定一組數字a,b,c……,請計算a2 + b2 + c2 + ……,
要定義出這個函式,我們必須確定輸入的引數,由于引數個數不確定,我們首先想到可以把a,b,c……作為一個list或tuple傳進來,這樣,函式可以定義如下:
def calc(numbers): sum = 0 for n in numbers: sum = sum + n * n return sum但是呼叫的時候,需要先組裝出一個list或tuple:
>>> calc([1, 2, 3]) 14 >>> calc((1, 3, 5, 7)) 84如果利用可變引數,呼叫函式的方式可以簡化成這樣:
>>> calc(1, 2, 3) 14 >>> calc(1, 3, 5, 7) 84所以,我們把函式的引數改為可變引數:
def calc(*numbers): sum = 0 for n in numbers: sum = sum + n * n return sum定義可變引數和定義一個list或tuple引數相比,僅僅在引數前面加了一個
*號,在函式內部,引數numbers接收到的是一個tuple,因此,函式代碼完全不變,但是,呼叫該函式時,可以傳入任意個引數,包括0個引數:>>> calc(1, 2) 5 >>> calc() 0如果已經有一個list或者tuple,要呼叫一個可變引數怎么辦?可以這樣做:
>>> nums = [1, 2, 3] >>> calc(nums[0], nums[1], nums[2]) 14這種寫法當然是可行的,問題是太繁瑣,所以Python允許你在list或tuple前面加一個
*號,把list或tuple的元素變成可變引數傳進去:>>> nums = [1, 2, 3] >>> calc(*nums) 14*nums表示把nums這個list的所有元素作為可變引數傳進去,這種寫法相當有用,而且很常見,def change(*numbers): sum=0; for i in numbers: sum+=i; return sum; print(change(1,2,3)) st=(1,2,3,4,5,6,7) print(change(*st))在Python中定義函式,可以用必選引數、默認引數、可變引數和關鍵字引數,這4種引數都可以一起使用,或者只用其中某些,但是請注意,引數定義的順序必須是:必選引數、默認引數、可變引數和關鍵字引數,
def func(a, b, c=0, *args, **kw): print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw) >>> func(1, 2) a = 1 b = 2 c = 0 args = () kw = {} >>> func(1, 2, c=3) a = 1 b = 2 c = 3 args = () kw = {} >>> func(1, 2, 3, 'a', 'b') a = 1 b = 2 c = 3 args = ('a', 'b') kw = {} >>> func(1, 2, 3, 'a', 'b', x=99) a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99} -
匿名函式
-
python 使用 lambda 來創建匿名函式,
- lambda只是一個運算式,函式體比def簡單很多,
- lambda的主體是一個運算式,而不是一個代碼塊,僅僅能在lambda運算式中封裝有限的邏輯進去,
- lambda函式擁有自己的命名空間,且不能訪問自有引數串列之外或全域命名空間里的引數,
- 雖然lambda函式看起來只能寫一行,卻不等同于C或C++的行內函式,后者的目的是呼叫小函式時不占用堆疊記憶體從而增加運行效率,
語法
lambda函式的語法只包含一個陳述句,如下:
lambda [arg1 [,arg2,.....argn]]:expression如下實體:
實體(Python 2.0+)
#!/usr/bin/python # -- coding: UTF-8 -- # 可寫函式說明 sum = lambda arg1, arg2: arg1 + arg2 # 呼叫sum函式 print "相加后的值為 : ", sum( 10, 20 ) print "相加后的值為 : ", sum( 20, 20 )
以上實體輸出結果:
相加后的值為 : 30 相加后的值為 : 40
-
-
回傳多個值
-
import math def move(x, y, step, angle=0): nx = x + step * math.cos(angle) ny = y - step * math.sin(angle) return nx, ny x, y = move(100, 100, 60, math.pi / 6)
-
-
全域變數和區域變數
定義在函式內部的變數擁有一個區域作用域,定義在函式外的擁有全域作用域,
區域變數只能在其被宣告的函式內部訪問,而全域變數可以在整個程式范圍內訪問,呼叫函式時,所有在函式內宣告的變數名稱都將被加入到作用域中,如下實體:
實體(Python 2.0+)
#!/usr/bin/python # -*- coding: UTF-8 -*- total = 0 # 這是一個全域變數 # 可寫函式說明 def sum( arg1, arg2 ): #回傳2個引數的和." total = arg1 + arg2 # total在這里是區域變數. print "函式內是區域變數 : ", total return total #呼叫sum函式 sum( 10, 20 ) print "函式外是全域變數 : ", total默認引數一定要用不可變物件,如果是可變物件,程式運行時會有邏輯錯誤!
要注意定義可變引數和關鍵字引數的語法:
*args是可變引數,args接收的是一個tuple;kw是關鍵字引數,kw接收的是一個dict,以及呼叫函式時如何傳入可變引數和關鍵字引數的語法:
可變引數既可以直接傳入:
func(1, 2, 3),又可以先組裝list或tuple,再通過*args傳入:func(*(1, 2, 3));關鍵字引數既可以直接傳入:
func(a=1, b=2),又可以先組裝dict,再通過**kw傳入:func(**{'a': 1, 'b': 2}),**使用
*args和**kw是Python的習慣寫法,當然也可以用其他引數名,但最好使用習慣用法,命名的關鍵字引數是為了限制呼叫者可以傳入的引數名,同時可以提供默認值,
定義命名的關鍵字引數在沒有可變引數的情況下不要忘了寫分隔符
*,否則定義的將是位置引數, -
函式別名
-
函式名其實就是指向一個函式物件的參考,完全可以把函式名賦給一個變數,相當于給這個函式起了一個“別名
-
>>> a = abs # 變數a指向abs函式 >>> a(-1) # 所以也可以通過a呼叫abs函式 1
-
-
Import 與 from import
-
>>> import datetime >>> print(datetime.datetime.now() >>> from datetime import datetime >>> print(datetime.now())
-
-
包的概念 packa包中
-
from packa.a import a from packa.b import b a() b()
-
22、切片
lis=[1,2,3,4,5,6,7,8]
print(lis[0:3])
print(lis[:4])
print(lis[-5:-3])
print(lis[-5:])
23、迭代
-
在Python中,迭代是通過
for ... in來完成的-
for num in lis: print(num)
-
-
默認情況下,dict迭代的是key,如果要迭代value,可以用
for value in d.values(),如果要同時迭代key和value,可以用for k, v in d.items()-
d = {'a': 1, 'b': 2, 'c': 3} for k in d: print(k) for v in d.values(): print(v) for k,v in d.items(): print('k=',k,'v=',v)
-
-
字串也是可迭代物件
-
for ch in 'ABCD': print(ch)
-
-
如何判斷一個物件是可迭代物件呢?方法是通過collections模塊的Iterable型別判斷:
-
>>> from collections import Iterable >>> isinstance('abc', Iterable) # str是否可迭代 True >>> isinstance([1,2,3], Iterable) # list是否可迭代 True >>> isinstance(123, Iterable) # 整數是否可迭代 False
-
-
如果要對list實作類似Java那樣的下標回圈怎么辦?Python內置的
enumerate函式可以把一個list變成索引-元素對,這樣就可以在for回圈中同時迭代索引和元素本身:-
for i,v in enumerate(lis): print(i,v)
-
24、串列生成式
-
串列生成式即List Comprehensions,是Python內置的非常簡單卻強大的可以用來創建list的生成式,
-
python range() 函式可創建一個整數串列,一般用在 for 回圈中,
-
print(list(range(1,20))) l=[] for i in range(1,20): l.append(i*i) print(l) print([x * x for x in range(1,5)]) #串列生成式 print([x * x for x in range(1, 10) if x%2==1]) print([d for d in os.listdir('/')]) #import os寫串列生成式時,把要生成的元素
x * x放到前面,后面跟for回圈,就可以把list創建出來,十分有用,多寫幾次,很快就可以熟悉這種語法,for回圈后面還可以加上if判斷,
還可以使用兩層回圈,可以生成全排列:
print([x+y for x in 'abc' for y in 'bdf']) -
串列生產式也可以同時使用多個變數
-
d = {'x': 'A', 'y': 'B', 'z': 'C'} print([k+'='+v for k,v in d.items()])
-
-
串列生成式中的if…else
-
print([[x if x % 2 == 0 else -x for x in range(1, 11)]]) print([x if x>2 else 100 for x in range(1,5)])在一個串列生成式中,
for前面的if ... else是運算式,而for后面的if是過濾條件,不能帶else,
-
25、生成器
-
通過串列生成式,我們可以直接創建一個串列,但是,受到記憶體限制,串列容量肯定是有限的,而且,創建一個包含100萬個元素的串列,不僅占用很大的存盤空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了,
所以,如果串列元素可以按照某種演算法推算出來,那我們是否可以在回圈的程序中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間,在Python中,這種一邊回圈一邊計算的機制,稱為生成器:generator,
要創建一個generator,有很多種方法,第一種方法很簡單,只要把一個串列生成式的
[]改成(),就創建了一個generatorg=(x*x for x in range(10)); print(next(g)) print(next(g)) print(next(g)) for i in g: print(i)generator保存的是演算法,每次呼叫
next(g),就計算出g的下一個元素的值,直到計算到最后一個元素,沒有更多的元素時,拋出StopIteration的錯誤,我們創建了一個generator后,基本上永遠不會呼叫
next(),而是通過for回圈來迭代它,并且不需要關心StopIteration的錯誤, -
generator非常強大,如果推算的演算法比較復雜,用類似串列生成式的
for回圈無法實作的時候,還可以用函式來實作,-
def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done'如果一個函式定義中包含
yield關鍵字,那么這個函式就不再是一個普通函式,而是一個generator;函式是順序執行,遇到
return陳述句或者最后一行函式陳述句就回傳,而變成generator的函式,在每次呼叫next()的時候執行,遇到yield陳述句回傳,再次執行時從上次回傳的yield陳述句處繼續執行,def odd(): print('step 1') yield 1 print('step 2') yield(3) print('step 3') yield(5)但是用
for回圈呼叫generator時,發現拿不到generator的return陳述句的回傳值,如果想要拿到回傳值,必須捕獲StopIteration錯誤,回傳值包含在StopIteration的value中:>>> g = fib(6) >>> while True: ... try: ... x = next(g) ... print('g:', x) ... except StopIteration as e: ... print('Generator return value:', e.value) ... break ... g: 1 g: 1 g: 2 g: 3 g: 5 g: 8 Generator return value: done
-
26、迭代器
-
一類是集合資料型別,如
list、tuple、dict、set、str等;一類是
generator,包括生成器和帶yield的generator function,這些可以直接作用于
for回圈的物件統稱為可迭代物件:Iterable,可以使用
isinstance()判斷一個物件是否是Iterable物件 -
而生成器不但可以作用于
for回圈,還可以被next()函式不斷呼叫并回傳下一個值,直到最后拋出StopIteration錯誤表示無法繼續回傳下一個值了,可以被
next()函式呼叫并不斷回傳下一個值的物件稱為迭代器:Iterator, -
生成器都是
Iterator物件,但list、dict、str雖然是Iterable,卻不是Iterator,把
list、dict、str等Iterable變成Iterator可以使用iter()函式:>>> isinstance(iter([]), Iterator) True >>> isinstance(iter('abc'), Iterator) True你可能會問,為什么
list、dict、str等資料型別不是Iterator?這是因為Python的
Iterator物件表示的是一個資料流,Iterator物件可以被next()函式呼叫并不斷回傳下一個資料,直到沒有資料時拋出StopIteration錯誤,可以把這個資料流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函式實作按需計算下一個資料,所以Iterator的計算是惰性的,只有在需要回傳下一個資料時它才會計算,Iterator甚至可以表示一個無限大的資料流,例如全體自然數,而使用list是永遠不可能存盤全體自然數的, -
凡是可作用于
for回圈的物件都是Iterable型別;凡是可作用于
next()函式的物件都是Iterator型別,它們表示一個惰性計算的序列;集合資料型別如
list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函式獲得一個Iterator物件,Python的
for回圈本質上就是通過不斷呼叫next()
27、函式式編程
1、高階函式
-
變數可以指向函式
-
函式名也是變數
-
既然變數可以指向函式,函式的引數能接收變數,那么一個函式就可以接收另一個函式作為引數,這種函式就稱之為高階函式
-
f=abs print(f) abs=10 #abs本來是內嵌函式 print(abs) def absAdd(x,y,f): return f(x)+f(y) print(absAdd(-1,-2,f))
2、map/reduce
-
map
-
def pow(x): return x*x # 回傳的是一個Iterator 因此通過list()函式讓它把整個序列都計算出來并回傳一個list, lis = map(pow, [1, 2, 3, 4, 5]) print(list(lis)) print(list(map(str,[1,2,3,4,5]))) #回傳字串
-
-
reduce
-
from functools import reduce def add(x,y): return x+y print(reduce(add,[1,2,3,4,5]))reduce把一個函式作用在一個序列[x1, x2, x3, ...]上,這個函式必須接收兩個引數,re**duce把結果繼續和序列的下一個元素做累積計算**,其效果就是:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
-
-
filter
-
Python內建的
filter()函式用于過濾序列,和map()類似,filter()也接收一個函式和一個序列,和map()不同的是,filter()把傳入的函式依次作用于每個元素,然后根據回傳值是True還是False決定保留還是丟棄該元素,-
def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 結果: [1, 5, 9, 15]
-
-
-
sorted
-
>>> sorted([36, 5, -12, 9, -21]) [-21, -12, 5, 9, 36]此外,
sorted()函式也是一個高階函式,它還可以接收一個key函式來實作自定義的排序,例如按絕對值大小排序:>>> sorted([36, 5, -12, 9, -21], key=abs) [5, 9, -12, -21, 36]key指定的函式將作用于list的每一個元素上,并根據key函式回傳的結果進行排序,
-
3、回傳函式
-
def lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sum f = lazy_sum(1, 3, 5, 7, 9) print(f())當我們呼叫
lazy_sum()時,回傳的并不是求和結果,而是求和函式:在函式
lazy_sum中又定義了函式sum,并且,內部函式sum可以參考外部函式lazy_sum的引數和區域變數,當lazy_sum回傳函式sum時,相關引數和變數都保存在回傳的函式中,這種稱為“閉包(Closure)”的程式結構擁有極大的威力,
閉包
-
當一個函式回傳了一個函式后,其內部的區域變數還被新函式參考
-
回傳的函式并沒有立刻執行,而是直到呼叫了
f()才執行 -
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count() 在上面的例子中,每次回圈,都創建了一個新的函式,然后,把創建的3個函式都回傳了, 你可能認為呼叫f1(),f2()和f3()結果應該是1,4,9,但實際結果是: >>> f1() 9 >>> f2() 9 >>> f3() 9def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count()在上面的例子中,每次回圈,都創建了一個新的函式,然后,把創建的3個函式都回傳了,
你可能認為呼叫
f1(),f2()和f3()結果應該是1,4,9,但實際結果是:>>> f1() 9 >>> f2() 9 >>> f3() 9全部都是
9!原因就在于回傳的函式參考了變數i,但它并非立刻執行,等到3個函式都回傳時,它們所參考的變數i已經變成了3,因此最終結果為9,回傳閉包時牢記一點:回傳函式不要參考任何回圈變數,或者后續會發生變化的變數,
4、lambda
-
f=lambda x:x*x-2; f1=lambda x,y:x+y print(f(2)) print(f1(3,4))
5、裝飾器
-
def log(f): def decrotor(*args,**kw): print('call %s():' % f.__name__) return f(*args, **kw) return decrotor @log def tes(): print('tes') tes()把
@log放到now()函式的定義處,相當于執行了陳述句:now = log(now)由于
log()是一個decorator,回傳一個函式,所以,原來的now()函式仍然存在,只是現在同名的now變數指向了新的函式,于是呼叫now()將執行新函式,即在log()函式中回傳的wrapper()函式,wrapper()函式的引數定義是(*args, **kw),因此,wrapper()函式可以接受任意引數的呼叫,在wrapper()函式內,首先列印日志,再緊接著呼叫原始函式, -
如果decorator本身需要傳入引數,那就需要撰寫一個回傳decorator的高階函式,寫出來會更復雜,比如,要自定義log的文本:
def log(text): def decorator(func): def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator這個3層嵌套的decorator用法如下:
@log('execute') def now(): print('2015-3-25')執行結果如下:
>>> now() execute now(): 2015-3-25和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:
>>> now = log('execute')(now)我們來剖析上面的陳述句,首先執行
log('execute'),回傳的是decorator函式,再呼叫回傳的函式,引數是now函式,回傳值最終是wrapper函式,以上兩種decorator的定義都沒有問題,但還差最后一步,因為我們講了函式也是物件,它有
__name__等屬性,但你去看經過decorator裝飾之后的函式,它們的__name__已經從原來的'now'變成了'wrapper':>>> now.__name__ 'wrapper'因為回傳的那個
wrapper()函式名字就是'wrapper',所以,需要把原始函式的__name__等屬性復制到wrapper()函式中,否則,有些依賴函式簽名的代碼執行就會出錯,不需要撰寫
wrapper.__name__ = func.__name__這樣的代碼,Python內置的functools.wraps就是干這個事的,所以,一個完整的decorator的寫法如下:import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper或者針對帶引數的decorator:
import functools def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decoratorimport functools是匯入functools模塊,模塊的概念稍候講解,現在,只需記住在定義wrapper()的前面加上@functools.wraps(func)即可,
6、偏函式
-
Python的
functools模塊提供了很多有用的功能,其中一個就是偏函式(Partial function), -
nt()函式可以把字串轉換為整數,當僅傳入字串時,int()函式默認按十進制轉換:>>> int('12345') 12345但
int()函式還提供額外的base引數,默認值為10,如果傳入base引數,就可以做N進制的轉換:>>> int('12345', base=8) 5349 >>> int('12345', 16) 74565假設要轉換大量的二進制字串,每次都傳入
int(x, base=2)非常麻煩,于是,我們想到,可以定義一個int2()的函式,默認把base=2傳進去:def int2(x, base=2): return int(x, base)這樣,我們轉換二進制就非常方便了:
>>> int2('1000000') 64 >>> int2('1010101') 85functools.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和**kw這3個引數,當傳入:int2 = functools.partial(int, base=2)實際上固定了int()函式的關鍵字引數
base,也就是:int2('10010')相當于:
kw = { 'base': 2 } int('10010', **kw)當傳入:
max2 = functools.partial(max, 10)實際上會把
10作為*args的一部分自動加到左邊,也就是:max2(5, 6, 7)相當于:
args = (10, 5, 6, 7) max(*args)結果為
10,
28、模塊
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Michael Liao'
import sys
def test():
args = sys.argv
if len(args)==1:
print('Hello, world!')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
if __name__=='__main__':
test()
第1行和第2行是標準注釋,第1行注釋可以讓這個hello.py檔案直接在Unix/Linux/Mac上運行,第2行注釋表示.py檔案本身使用標準UTF-8編碼;
第4行是一個字串,表示模塊的檔案注釋,任何模塊代碼的第一個字串都被視為模塊的檔案注釋;
第6行使用__author__變數把作者寫進去,這樣當你公開源代碼后別人就可以瞻仰你的大名;
以上就是Python模塊的標準檔案模板,當然也可以全部刪掉不寫,但是,按標準辦事肯定沒錯,
你可能注意到了,使用sys模塊的第一步,就是匯入該模塊:
import sys
匯入sys模塊后,我們就有了變數sys指向該模塊,利用sys這個變數,就可以訪問sys模塊的所有功能,
sys模塊有一個argv變數,用list存盤了命令列的所有引數,argv至少有一個元素,因為第一個引數永遠是該.py檔案的名稱,例如:
運行python3 hello.py獲得的sys.argv就是['hello.py'];
運行python3 hello.py Michael獲得的sys.argv就是['hello.py', 'Michael'],
最后,注意到這兩行代碼:
if __name__=='__main__':
test()
**當我們在命令列運行hello模塊檔案時,Python解釋器把一個特殊變數__name__置為__main__,**而如果在其他地方匯入該hello模塊時,if判斷將失敗,因此,這種if測驗可以讓一個模塊通過命令列運行時執行一些額外的代碼,最常見的就是運行測驗,
私有變數和函式
-
正常的函式和變數名是公開的(public),可以被直接參考,比如:
abc,x123,PI等;類似
__xxx__這樣的變數是特殊變數,可以被直接參考,但是有特殊用途,比如上面的__author__,__name__就是特殊變數,hello模塊定義的檔案注釋也可以用特殊變數__doc__訪問,我們自己的變數一般不要用這種變數名;**類似
_xxx和__xxx**這樣的函式或變數就是非公開的(private),不應該被直接參考,比如_abc,__abc等; -
類似
_xxx和__xxx這樣的函式或變數就是非公開的(private),不應該被直接參考,比如_abc,__abc等;之所以我們說,private函式和變數“不應該”被直接參考,而不是“不能”被直接參考,是因為Python并沒有一種方法可以完全限制訪問private函式或變數,但是,從編程習慣上不應該參考private函式或變數,
在Python中,有以下幾種方式來定義變數:
- xx:公有變數
- _xx:單前置下劃線,私有化屬性或方法,類物件和子類可以訪問,from somemodule import *禁止匯入
- __xx:雙前置下劃線,私有化屬性或方法,無法在外部直接訪問(名字重整所以訪問不到)
- xx:雙前后下劃線,系統定義名字(不要自己發明這樣的名字)
- xx_:單后置下劃線,用于避免與Python關鍵詞的沖突
使用不同方法匯入模塊,模塊中私有變數的使用區別
在使用不同方法匯入模塊后,是否能使用模塊中的私有屬性和方法,有以下兩種情況
- 在使用 from somemodule import * 匯入模塊的情況下,不能匯入或使用私有屬性和方法
- 在使用 import somemodule 匯入模塊的情況下,能匯入并使用私有屬性和方法
第三方模塊
-
在Python中,安裝第三方模塊,是通過包管理工具pip完成的,
-
模塊搜索路徑
當我們試圖加載一個模塊時,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/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', ..., '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages']如果我們要添加自己的搜索目錄,有兩種方法:
一是直接修改
sys.path,添加要搜索的目錄:>>> import sys >>> sys.path.append('/Users/michael/my_py_scripts')這種方法是在運行時修改,運行結束后失效,
第二種方法是設定環境變數
PYTHONPATH,該環境變數的內容會被自動添加到模塊搜索路徑中,設定方式與設定Path環境變數類似,注意只需要添加你自己的搜索路徑,Python自己本身的搜索路徑不受影響,
29、類和實體
-
類
-
class Student(object): def __init__(self, name, score): self.name = name self.score = score
-
class后面緊接著是類名,即Student,類名通常是大寫開頭的單詞,緊接著是(object),表示該類是從哪個類繼承下來的,繼承的概念我們后面再講,通常,如果沒有合適的繼承類,就使用object類,這是所有類最終都會繼承的類,
由于類可以起到模板的作用,因此,可以在創建實體的時候,把一些我們認為必須系結的屬性強制填寫進去,通過定義一個特殊的__init__方法,在創建實體的時候,就把name,score等屬性綁上去
注意到**__init__方法的第一個引數永遠是self,表示創建的實體本身,因此,在__init__方法內部,就可以把各種屬性系結到self,因為self就指向創建的實體本身,**
有了__init__方法,在創建實體的時候,就不能傳入空的引數了,必須傳入與__init__方法匹配的引數,但self不需要傳,Python解釋器自己會把實體變數傳進去
和普通的函式相比,在類中定義的函式只有一點不同,就是第一個引數永遠是實體變數self,并且,呼叫時,不用傳遞該引數,除此之外,類的方法和普通函式沒有什么區別,所以,你仍然可以用默認引數、可變引數、關鍵字引數和命名關鍵字引數,
可以自由地給一個實體變數系結屬性
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def get_grade(self):
if self.score >= 90:
return 'A'
elif self.score >= 60:
return 'B'
else:
return 'C'
方法就是與實體系結的函式,和普通函式不同,方法可以直接訪問實體的資料;
stu=Student('zdc',29)
def min(a,b):
if a>b:
return b
else:
return a
print(stu)
stu.min=min
print(stu.min(1,2))
stu.zz='ssd'
print(stu.zz)
實體可以任意添加屬性和方法
30、訪問限制
class Student(object):
def __init__(self,name,score):
self.__name=name
self.__score=score
stu=Student('zdc',29)
print(stu._Student__name)#可以訪問
print(stu.__name)
如果要讓內部屬性不被外部訪問,可以把屬性的名稱前加上兩個下劃線__,在Python中,實體的變數名如果以__開頭,就變成了一個私有變數(private),只有內部可以訪問,外部不能訪問,所以,我們把Student類改一改:
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
改完后,對于外部代碼來說,沒什么變動,但是已經無法從外部訪問實體變數.__name和實體變數.__score了:
>>> bart = Student('Bart Simpson', 59)
>>> bart.__name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'
這樣就確保了外部代碼不能隨意修改物件內部的狀態,這樣通過訪問限制的保護,代碼更加健壯,
但是如果外部代碼要獲取name和score怎么辦?可以給Student類增加get_name和get_score這樣的方法:
class Student(object):
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
如果又要允許外部代碼修改score怎么辦?可以再給Student類增加set_score方法:
class Student(object):
...
def set_score(self, score):
self.__score = score
你也許會問,原先那種直接通過bart.score = 99也可以修改啊,為什么要定義一個方法大費周折?因為在方法中,可以對引數做檢查,避免傳入無效的引數:
class Student(object):
...
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('bad score')
需要注意的是,在Python中,變數名類似__xxx__的,也就是以雙下劃線開頭,并且以雙下劃線結尾的,是特殊變數,特殊變數是可以直接訪問的,不是private變數,所以,不能用__name__、__score__這樣的變數名
有些時候,你會看到以一個下劃線開頭的實體變數名,比如_name,這樣的實體變數外部是可以訪問的,但是,按照約定俗成的規定,當你看到這樣的變數時,意思就是,“雖然我可以被訪問,但是,請把我視為私有變數,不要隨意訪問”,
雙下劃線開頭的實體變數是不是一定不能從外部訪問呢?其實也不是,不能直接訪問__name是因為Python解釋器對外把__name變數改成了_Student__name,所以,仍然可以通過_Student__name來訪問__name變數:
31、繼承和多型
-
class Animal(object): def run(self): print('Animal is running...') class Dog(Animal): pass class Cat(Animal): pass dog = Dog() dog.run()繼承的第二個好處需要我們對代碼做一點改進,你看到了,無論是
Dog還是Cat,它們run()的時候,顯示的都是Animal is running...,符合邏輯的做法是分別顯示Dog is running...和Cat is running...,因此,對Dog和Cat類改進如下:class Dog(Animal): def run(self): print('Dog is running...') class Cat(Animal): def run(self): print('Cat is running...')再次運行,結果如下:
Dog is running... Cat is running...當子類和父類都存在相同的
run()方法時,我們說,子類的run()覆寫了父類的run(),在代碼運行的時候,總是會呼叫子類的run(),這樣,我們就獲得了繼承的另一個好處:多型,def run_twice(animal): animal.run()
32、獲取物件資訊
-
基本型別都可以用
type()判斷>>> type(123) <class 'int'> >>> type('str') <class 'str'> >>> type(None) <type(None) 'NoneType'>如果一個變數指向函式或者類,也可以用
type()判斷:>>> type(abs) <class 'builtin_function_or_method'> >>> type(a) <class '__main__.Animal'>但是
type()函式回傳的是什么型別呢?它回傳對應的Class型別,如果我們要在if陳述句中判斷,就需要比較兩個變數的type型別是否相同:>>> type(123)==type(456) True >>> type(123)==int True >>> type('abc')==type('123') True >>> type('abc')==str True >>> type('abc')==type(123) False判斷基本資料型別可以直接寫
int,str等,但如果要判斷一個物件是否是函式怎么辦?可以使用types模塊中定義的常量:>>> import types >>> def fn(): ... pass ... >>> type(fn)==types.FunctionType True >>> type(abs)==types.BuiltinFunctionType True >>> type(lambda x: x)==types.LambdaType True >>> type((x for x in range(10)))==types.GeneratorType True -
使用isinstance()
-
對于class的繼承關系來說,使用
type()就很不方便,我們要判斷class的型別,可以使用isinstance()函式, -
isinstance()就可以告訴我們,一個物件是否是某種型別,先創建3種型別的物件: >>> a = Animal() >>> d = Dog() >>> h = Husky() 然后,判斷: >>> isinstance(h, Husky) True >>> isinstance(h, Dog) True #但由于Husky是從Dog繼承下來的,所以,h也還是Dog型別
-
-
dir()
-
如果要獲得一個物件的所有屬性和方法,可以使用
dir()函式,它回傳一個包含字串的list,比如,獲得一個str物件的所有屬性和方法:>>> dir('ABC') ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']僅僅把屬性和方法列出來是不夠的,配合**
getattr()、setattr()以及hasattr()**,我們可以直接操作一個物件的狀態:>>> hasattr(obj, 'x') # 有屬性'x'嗎? True >>> obj.x 9 >>> hasattr(obj, 'y') # 有屬性'y'嗎? False >>> setattr(obj, 'y', 19) # 設定一個屬性'y' >>> hasattr(obj, 'y') # 有屬性'y'嗎? True >>> getattr(obj, 'y') # 獲取屬性'y' 19 >>> obj.y # 獲取屬性'y' 19如果試圖獲取不存在的屬性,會拋出AttributeError的錯誤:
>>> getattr(obj, 'z') # 獲取屬性'z' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'MyObject' object has no attribute 'z'可以傳入一個default引數,如果屬性不存在,就回傳默認值:
>>> getattr(obj, 'z', 404) # 獲取屬性'z',如果不存在,回傳默認值404 404也可以獲得物件的方法:
>>> hasattr(obj, 'power') # 有屬性'power'嗎? True >>> getattr(obj, 'power') # 獲取屬性'power' <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>> >>> fn = getattr(obj, 'power') # 獲取屬性'power'并賦值到變數fn >>> fn # fn指向obj.power <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>> >>> fn() # 呼叫fn()與呼叫obj.power()是一樣的 81
-
33、實體屬性和類屬性
-
class Demo: count=100; #類變數 def __init__(self,name,age): self.__name=name self.__age=age d=Demo('zz',22) d2=Demo('dd',11) d.count=222 # 給實體系結count屬性 類的count被隱藏了 print(d2.count) #100 因為實體并沒有count屬性,所以會繼續查找class的count屬性 print(d.count) #222 print(Demo.count) #100 del d.count #洗掉d的實體屬性 此時可以訪問到類屬性 print(d.count)在撰寫程式的時候,千萬不要對實體屬性和類屬性使用相同的名字,因為相同名稱的實體屬性將屏蔽掉類屬性,但是當你洗掉實體屬性后,再使用相同的名稱,訪問到的將是類屬性
33、__slots__限制實體系結任何屬性和方法
-
常情況下,當我們定義了一個class,創建了一個class的實體后,我們可以給該實體系結任何屬性和方法,這就是動態語言的靈活性,但是,給一個實體系結的方法,對另一個實體是不起作用的
-
但是,如果我們想要限制實體的屬性怎么辦?比如,只允許對Student實體添加
name和age屬性,為了達到限制的目的,Python允許在定義class的時候,定義一個特殊的
__slots__變數,來限制該class實體能添加的屬性:class Student(object): __slots__ = ('name', 'age') # 用tuple定義允許系結的屬性名稱 >>> s.score = 99 # 系結屬性'score' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score'除非在子類中也定義
__slots__,這樣,子類實體允許定義的屬性就是自身的__slots__加上父類的__slots__, -
len
-
__len__ 如果一個類表現得像一個list,要獲取有多少個元素,就得用 len() 函式, 要讓 len() 函式作業正常,類必須提供一個特殊方法__len__(),它回傳元素的個數, 例如,我們寫一個 Students 類,把名字傳進去: class Students(object): def __init__(self, *args): self.names = args def __len__(self): return len(self.names) 只要正確實作了__len__()方法,就可以用len()函式回傳Students實體的“長度”:
-
34、@property @xxx.setter
-
class Person(object): @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): return 2015 - self._birth p = Person() p.birth = 100 # p.age=11 #age為只讀 print(p.birth) print(p.age)把一個getter方法變成屬性,只需要加上
@property就可以了,此時,@property本身又創建了另一個裝飾器@score.setter,負責把一個setter方法變成屬性賦值
35、多繼承
- Python允許使用多重繼承
36、定制類
-
str
我們先定義一個
Student類,列印一個實體:>>> class Student(object): ... def __init__(self, name): ... self.name = name ... >>> print(Student('Michael')) <__main__.Student object at 0x109afb190>列印出一堆
<__main__.Student object at 0x109afb190>,不好看,怎么才能列印得好看呢?只需要定義好
__str__()方法,回傳一個好看的字串就可以了:>>> class Student(object): ... def __init__(self, name): ... self.name = name ... def __str__(self): ... return 'Student object (name: %s)' % self.name ... >>> print(Student('Michael')) Student object (name: Michael)這樣列印出來的實體,不但好看,而且容易看出實體內部重要的資料,
怎么說呢,python 的特性太膠水了,不適合作為專案主力,所以它是生產力技能而不是業務技能,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/258434.html
標籤:python
