pyton review
-
運行
- 在Windows上運行Python時拷况,請先啟動命令行悉罕,然后運行python泼差。
- 在Mac和Linux上運行Python時,請打開終端,然后運行python3。(本身有Python2版本文件, 為區(qū)分)
python 為解釋型語言
-
解釋器種類
- 默認為CPython,
- IPython 基于CPython
- PyPy追求執(zhí)行速度執(zhí)行動態(tài)編譯 與CPythin可能相同代碼不同執(zhí)行結(jié)果
- Jython 運行于Java平臺的Python解釋器,編譯Python代碼為Java字節(jié)碼
- IronPython運行于.Net
可以直接 命令行中 python hello.py 執(zhí)行python文件 hello.py
-
Mac與Linux可以直接運行.py文件, win不行需要在文件第一行, 還要命令給文件已執(zhí)行權(quán)限chmod a+x hello.py
#!/usr/bin/env python3
print()可接受多個字符串, 用逗號','隔開輸出連接逗號轉(zhuǎn)變成空格
input()輸入, input('please enter your name:')參數(shù)是友好提示
數(shù)字 十六進制0xff00, 浮點數(shù) 1.23e-8,計算機由于使用二進制秀姐,所以,有時候用十六進制表示整數(shù)比較方便若贮,十六進制用0x前綴和0-9省有,a-f表示,例如:0xff00谴麦,0xa5b4c3d2蠢沿,等等。
字符串 r''內(nèi)部字符串不轉(zhuǎn)義
-
print('''...''')格式表示多行內(nèi)容與r'''...'''可組合使用
print('''line1 ... line2 ... line3''') line1 line2 line3
boolean值 True,False , 可用 and, or , not 運算
None值
/浮點除, // 地板除, %取余 10/3 浮點型 10//3 整數(shù) 除地板
編碼ASCII,Unicode,為傳輸存儲速度發(fā)明了可變長編碼UTF-8, A 65, a 97, 0 48
-
python字符串
- python 字符串 ord('A') chr(20013), 也可以十六進制直接寫出'\u0030'
- 'dkjKJDFL'.lower(), 'adf'.upper()
- b'ABC'直接轉(zhuǎn)化為bytes,非單字節(jié)字符, 需用'中文'.encode('utf-8');
- 如果encode無法顯示為ASCII,用\x##表示, 字節(jié)流轉(zhuǎn)義用 decode()
b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
- 計算字符長度len('中文'),用轉(zhuǎn)換為bytes.len()可計算字節(jié)數(shù)
- 為避免編碼問題,保存源碼指定保存為UTF-8編碼讀取, 通常在文件開頭寫入
#!/usr/bin/env python3 # -*- coding: utf-8 -*-
- 格式化占位符用% 'hello, %s' % 'world', 順序?qū)?s替換字符串, %d替換整數(shù) %f浮點數(shù) %x 十六進制整數(shù), %%轉(zhuǎn)義為%, 可定義整數(shù)補零位數(shù)%05d, 浮點數(shù)小數(shù)點后保留位數(shù)%.3f
'Hi, %s, you have $%d.' % ('Michael', 1000000) 'Hi, Michael, you have $1000000.'
-
list
- 定義 classmates = ['eric','eric2','eric3']
- 訪問 classmats[1], 最后一個元素下標 len(classmates)-1
- 添加元素, classmates.apppend('eric4'), classmates.insert(1,'Jack')
- 刪除元素, classmates.pop(), 指定下標 classmates.pop(2)
- 內(nèi)含元素可以不同類型
-
tuple
- 一旦定義不可修改 tp = ('eric','eric2','eric3')
- 定義 可簡化(), 多變量可接收 tuple值,按位置賦值
- 訪問操作相同 tp[1]
- ps: 一個元素(1), 括號優(yōu)先為規(guī)避此問題, 一個元素 加逗號(1,)
-
分支結(jié)構(gòu)判斷
a = 100 if a>=0: print(a) elif a>-10: print(a) else: print(-a)
if x: 只要x為非零數(shù)值, 非空字符串, 非空list等, 就判斷為True, 否則為False
提供int()用于轉(zhuǎn)int型
-
循環(huán)結(jié)構(gòu)
- 簡單 for name in classmates: print(name), 遍歷集合數(shù)組
- list(range(101)),生成[0,1,2,3,...100]
- while n > 0: n = n + 2
-
dict
- 定義 d = {'eric':0,'eric2':2, 'eric3':3}
- 訪問 d['eric2'],
- 判斷元素, 訪問不存在會報錯, 可以在訪問前, 判斷元素 'eric' in d, 返回 boolean, d.get('eric')存在返回value不存在返回None(ps: 返回值為None時交互命令行不顯示結(jié)果), 或者自己指定的返回值, d.get('eric',-1)
- 添加元素 d['eric4'] = 4, 存在原元素則進行替換
- 刪除元素, d.pop('eric2') 同時返回value
-
set 不重復 是一組key的集合, 不儲存value, key不重復
- 定義 s = {1,2,3,3,3,3,4,5} s = {1,2,3,4,5}
> s = set([1,2,3,3,4,5]) > s > {1,2,3,4,5}
- 添加元素 s.add(6), 重復添加無效果
- 刪除元素 s.remove(key)
- set可以做數(shù)學意義上的交并集操作 s1 & s2, s1 | s2
- 定義 s = {1,2,3,3,3,3,4,5} s = {1,2,3,4,5}
調(diào)用函數(shù) abs(22), int('22'), hex(22), bool(22)
-
定義函數(shù) def my_abs(x):
-
如果定義一個什么也不做的空函數(shù), 可以用pass語句,缺少pass運行有語法錯誤
def nop(): pass
當沒有return語句時, 函數(shù)執(zhí)行結(jié)束也會返回結(jié)果, 結(jié)果為None,
return None, 可簡化為return
-
函數(shù)可返回多個值
return nx, my, x,y = move(100,100,60, math.pi/6)
Ps:返回多個值實際為假象, 多個值打包為tuple, 語法上返回一個tuple可以省略括號, 多個標量接受tuple, 按位置賦值, 對書寫的簡化
math.pi, math.sqrt, math.pow
-
-
數(shù)據(jù)類型檢查 isinstance(obj, class_or_tuple) 拋出異常
raise TypeError('bad operand type')
-
函數(shù)的參數(shù)
- 位置參數(shù), 對參數(shù)提供默認值, 則是默認參數(shù)Ps: 默認參數(shù)在必選參數(shù)后, 調(diào)用函數(shù)時, 不按照位置, 需要提供參數(shù)名Ps2: 默認參數(shù)必須是不可變對象, 否則默認參數(shù)可能改變
- 可變參數(shù)
def sum(*num): sum(3,4,5,6), sum(*[2,3,4])
- 關鍵字參數(shù) 傳入多個, 內(nèi)部整合為dict
- 命名關鍵詞參數(shù), 命名關鍵詞需要一個特殊的分隔符*, 有可變參數(shù), 后面為命名關鍵詞參數(shù)不再需要分隔符*
- 參數(shù)組合順序, 必選參數(shù), 默認參數(shù), 可變參數(shù), 命名關鍵詞參數(shù), 關鍵詞參數(shù)
- 方法調(diào)用傳參可通過func(*args,**kw), *args是可變參數(shù)匾效,args接收的是一個tuple舷蟀;**kw是關鍵字參數(shù),kw接收的是一個dict面哼。以及調(diào)用函數(shù)時如何傳入可變參數(shù)和關鍵字參數(shù)的語法:可變參數(shù)既可以直接傳入:func(1, 2, 3)雪侥,又可以先組裝list或tuple,再通過*args傳入:func(*(1, 2, 3))精绎;關鍵字參數(shù)既可以直接傳入:func(a=1, b=2)速缨,又可以先組裝dict,再通過**kw傳入:func(**{'a': 1, 'b': 2})代乃。使用*args和**kw是Python的習慣寫法旬牲,當然也可以用其他參數(shù)名仿粹,但最好使用習慣用法。
-
遞歸函數(shù)
- 調(diào)用本身, 注意遞歸結(jié)束條件
- 尾遞歸與循環(huán)等價, 對尾遞歸優(yōu)化可以解決棧溢出問題
-
切片
- 切片取前三個元素L[start:end:n],Ps: 第一個元素為0可省略,第二個元素默認為最后一個元素,第三個元素指n元素取1含頭不含尾
- tuple 切片操作返回tuple
- string 切片返回string
- -1為最后一個元素
-
列表生成式
- list(range(4,10))
- [x*x for x in range(1,11)]
- 可以循環(huán)嵌套, 生成全排列 [m + n for m in 'ABC' for n in 'XYZ']
- 可以多變量生成列表生成器 [k + '=' + v for k,v in d.items()]
isinstance(obj, class_or_tuple) 判斷變量類型
-
生成器 generator
- 定義 列表生成器 [] 換為()
- 獲取 next(iterator[,default])
- 遍歷 for n in g:
- 多變量賦值 a,b = b, a+b 相當于省略臨時變量
t = (b,a+b) a = t[0] b = t[1]
- 可以用函數(shù)作為generator, 如果函數(shù)包含yield關鍵詞, 那么函數(shù)將不再是普通函數(shù), 而是一個generator, generator在調(diào)用next()的時候執(zhí)行, 遇到y(tǒng)ield語句返回, 再次執(zhí)行從上次yield語句出繼續(xù)執(zhí)行
-
迭代器 Iterable, from collections import Iterable
- 可以用 isinstance函數(shù)判斷
- 生成器為Iterator對象, 但list,dict,str 雖然是Iterable, 卻不是Iterator
- Iterator 對象表示的是一個數(shù)據(jù)流, next()函數(shù)函數(shù)調(diào)用返回下一個數(shù)據(jù),Iterator計算是惰性的,
- 凡是可作用于for 循環(huán)的對象都是Iterable類型:
- 凡是可作用于next()函數(shù)的對象都是Iterator類型, 他們表示一個惰性計算序列;
- 集合類型, 可以通過iter()函數(shù)獲得一個Iterator對象
-
函數(shù)式編程
- 函數(shù)編程是一種抽象程度很高的編程范式, 純粹的函數(shù)式編程函數(shù)沒有變量, 因此, 只要輸入是確定的, 輸出就是確定的,
- 函數(shù)式編程特點, 可以允許把函數(shù)本身作為參數(shù)傳入另一個函數(shù), 還允許返回一個函數(shù)
- python對函數(shù)式編程部分支持, 由于Python允許使用變量, 因此Python不是純函數(shù)式編程語言
高階函數(shù): 變量可以指向函數(shù), 一個函數(shù)可以接收另一個函數(shù)作為參數(shù), 這樣的函數(shù)就稱之為高階函數(shù), 函數(shù)式編程指這種高度抽象的編程范式
-
遞歸是對問題的描述, 循環(huán)是對問題的求解, 相當于方程組和計算式的關系
- 線性遞歸, 樹形遞歸, 尾遞歸(可優(yōu)化為循環(huán))
map : map(func, *iterables) --> map object
reduce : map(fucntion, sequence[,initial]) --> value
-
filter : filter(function or None , iterable) --> filter object
- 求素數(shù), 先定義一個生成器, 埃氏篩法, 不斷嵌套filter, 篩選下一個素數(shù)
-
sorted : sorted(iterable, key=None, reverse=False)
- key 是提供一個映射關系, 對原值進行映射, 返回結(jié)果, 以返回結(jié)果排序?qū)υ颠M行排序
-
返回函數(shù) 以函數(shù)作為返回值, lazy_sum 閉包結(jié)構(gòu)
- 返回一個函數(shù)時, 牢記該函數(shù)并未執(zhí)行 返回的函數(shù)中不要引入任何可能變化的變量
-
lambda x,y : x*10+y
- 只能有一個表達式, 不用寫return, 返回值就是該表達式的結(jié)果
- 匿名函數(shù)也是函數(shù)對象, 可以復制給一個變量, 再用變量來調(diào)用該函數(shù)
- 匿名函數(shù)也是函數(shù)對象, 也可以作為返回值
- python支持有限, 只有一些簡單情況可以使用
10**3 = 1000, 代表冪計算
-
裝飾器 : 在運行期間動態(tài)增加功能, 又不修改原函數(shù)的定義, 這種方式我們稱之為裝飾器(Decorator)
- 函數(shù)對象可以被賦值給其他變量, __name__可以拿到函數(shù)原名
- @語法 log定義在now函數(shù)上,相當于log(now),
def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper
- 多一層@log('eric'), 相當于 log('eric')(now)
def log(text): def decorator(func): def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator
- 解決__name__變?yōu)閣rapper, import functools 使用@functionls.wraps(func)
-
偏函數(shù) 是引入已有方法, 創(chuàng)建一個新函數(shù)可以固定住原函數(shù)的部分參數(shù), 從而使調(diào)用時更簡單
- import functools
- int2 = functools.partial(int, base=2)
- help : partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.
-
模塊
- 按目錄組織模塊, 包Package
- 包中有一個__init__.py文件, 這個文件必須存在, 否則Python就會把這個目錄當成普通目錄, 而不是一個包, 這個文件可以是空文件, 也可以有Python代碼, 因為__init__本身就是一個模塊, 而模塊名就是包名
- 可以有多級目錄, 組成多級層次包結(jié)構(gòu)
- 注意不要和Python自帶模塊名沖突
-
使用模塊
- sys模塊, 內(nèi)部argv變量, 存儲命令行參數(shù), python hello.py 獲得的 sys.argv就是['hello.py'];, 運行python hello.py Eric 獲得的sys.argv就是['hello.py','Eric']
- if __name__ == '__main__':test(), 當我們命令行運行hello模塊式, Python解釋器將一個特殊變量__name__置為__main__,而其他地方導入hello模塊, if判斷將失敗, 因此, 這種if測試可以讓一個模塊通過命令行運行時執(zhí)行一些額外代碼, 最常見就是用于運行測試
- 作用域
- abc, x123, PI 為public
- _xxx, __xxx為非公開(private),
- __name__等為特殊變量, 可以直接引用, 但是有特殊用途,__author__,文檔注釋可以用__doc__訪問等
-
第三庫
- 通過pip來下載 , pip 軟件包管理系統(tǒng)
- pip --version
- pip install Pillow,pip install some-package-name
- pip uninstall some-package-name
- 加載模塊, Python在指定路徑下搜索對于.py文件, 找不到,就報錯
- 默認搜索當前目錄, 所有已安裝內(nèi)置模塊和第三方模塊, 搜索路徑存放于sys模塊path變量
import sys sys.path
- 可修改sys.path
sys.paht.append('/u...'), 運行時生效, 運行結(jié)束后失效
- 修改環(huán)境變量PYTHONPATH, 永久修改
-
面向?qū)ο缶幊?數(shù)據(jù)封裝, 繼承, 多態(tài)
- __init__為特殊實例化方法, 可以在創(chuàng)建實例時將認為必須綁定的數(shù)據(jù)強制寫進去
- class Student(object) , 括號內(nèi)時父類繼承類, 默認object
- class 內(nèi)方法, 第一個參數(shù)為對象本身
- 與靜態(tài)語言不通, Python允許對實例對象綁定任何數(shù)據(jù), 也就是說, 對于兩個實例變量, 雖然他們都是一個類的不同實例, 但擁有的變量名稱可能不同
-
訪問限制
- 雙下劃線前綴代表private變量, 而單下劃線前綴的實例變量外部時可以訪問的, 但是, 按照約定俗稱的規(guī)定, 當你看到這樣的變量時, 意思是, '雖然我可以被訪問, 但是, 請把我視為私有變量, 不要隨意訪問'
- 雙下劃線不能直接訪問, 是因為Python解釋器對外將Student類, 將__name變量改成了_Student__name, 所以仍然可以通過改變后的變量名訪問到,但不要這樣做, 不同版本解釋器變量名可能不同
- 表面上看原茅,外部代碼“成功”地設置了__name變量吭历,但實際上這個__name變量和class內(nèi)部的__name變量不是一個變量!內(nèi)部的__name變量已經(jīng)被Python解釋器自動改成了_Student__name擂橘,而外部代碼給bart新增了一個__name變量
- Python本身沒有有效高強制的訪問限制, 需要自覺遵守規(guī)范
- 私有變量, get set方法
-
繼承 Animal(object) Cat(Animal) Dog(Animal)
- 獲得父類方法
- 多態(tài) 對父類方法改進
- 靜態(tài)語言和動態(tài)語言,對于靜態(tài)語言(例如Java)來說晌区,如果需要傳入Animal類型,則傳入的對象必須是Animal類型或者它的子類通贞,否則朗若,將無法調(diào)用run()方法。對于Python這樣的動態(tài)語言來說昌罩,則不一定需要傳入Animal類型哭懈。我們只需要保證傳入的對象有一個run()方法就可以了:
-
獲取對象信息
type help : type(object) -> the object's type
isinstance help : isinstance(obj, class_or_tuple) -> return whether an object is an instance of a class or of a subclass thereof
dir help : dir([object]) -> list of strings, If called without an argument, return the names in the current scope. Else, return an alphabetized list of names comprising (some of) the attributes of the given object, and of attributes of the given object, and of attributes reachable from it.
類似xxx的屬性和方法在Python中都是有特殊用途的,比如len方法返回長度茎用。在Python中遣总,如果你調(diào)用len()函數(shù)試圖獲取一個對象的長度,實際上轨功,在len()函數(shù)內(nèi)部旭斥,它自動去調(diào)用該對象的len()方法,我們自己寫的類,如果也想用len(myObj)的話古涧,就自己寫一個len()方法:
也可以把一個對象的方法賦予一個變量
可以用getattr(), setattr()以及hasattr()直接對一個對象的狀態(tài)進行操作
-
通過內(nèi)置一系列函數(shù), 我們可以對任意一個Python對象進行讀取, 拿到其內(nèi)部的數(shù)據(jù). 要注意的是, 只有不知道對象信息的時候, 我們才會去獲取對象的信息. 如果知道可以直接寫: 而不是下面第二種寫法
> sum = obj.x + obj.y > sum = getattr(obj, 'x') + getattr(obj,'y')
對象未知時先判斷, 鴨子類型
def readImage(fp): if hasattr(fp, 'read') return readData(fp) return None
-
實例屬性和類屬性
- self. 為實例屬性, 寫在class中的為類屬性
- 相同名字的實例樹形將屏蔽類屬性, 但是當你刪除實例屬性后, 在使用相同的名稱, 訪問到的將是類屬性
- 同名屬性實例屬性優(yōu)先級高
- 刪除 屬性
del ss.name
面向?qū)ο蟾呒?多重繼承, 定制類, 元類
-
可動態(tài)綁定方法, 為實例綁定方法 instance
from types import MethodType s.set_age = MethodType(set_age, s)
-
為了給所有實例綁定的方法, 可以給class綁定方法
Student.set_score = set_score
-
限制實例屬性
class Student(object): __slots__ = ('name','age')
- slots 定義的屬性只對當前class的instance 起作用, 對集成的子類是不起作用的
- 子類也定義__slots__, 這樣子類實例可以定義的屬性就是自身的__slots__加上父類的__slots__.
-
@property 對對象屬性, 便捷性和控制的解決方案
- 為檢查參數(shù), 設置get, set方法, 屬性設為私有
- 但調(diào)用方法又略顯復雜垂券,沒有直接用屬性這么直接簡單
class Student(object): @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value
- @property的實現(xiàn)比較復雜,我們先考察如何使用蒿褂。把一個getter方法變成屬性圆米,只需要加上@property就可以了卒暂,此時啄栓,@property本身又創(chuàng)建了另一個裝飾器@score.setter,負責把一個setter方法變成屬性賦值也祠,于是昙楚,我們就擁有一個可控的屬性操作
>>> s = Student() >>> s.score = 60 # OK,實際轉(zhuǎn)化為s.set_score(60) >>> s.score # OK诈嘿,實際轉(zhuǎn)化為s.get_score() 60 >>> s.score = 9999 Traceback (most recent call last): ... ValueError: score must between 0 ~ 100!
-
多重繼承 MixIn
- class Dog(Mammal, Runnable):
- 由于Python允許使用多重繼承堪旧,因此,MixIn就是一種常見的設計奖亚。只允許單一繼承的語言(如Java)不能使用MixIn的設計
- 多繼承的好處在于不需要復雜而龐大的繼承鏈, 只要選擇組合不同的類的功能, 就可以快速構(gòu)造出所需的子類
- 多繼承, 順序C3算法, MRO算法計算優(yōu)先級序列
- 本地優(yōu)先級: 指生命父類的順序, 比如C(A,B), 如果訪問C類對象屬性時, 應該根據(jù)聲明順序, 優(yōu)先查找A類, 然后再查找B類.
- 單調(diào)性: 如果在C的解析順序中, A排在B的前面, 那么在C的所有子類里, 也必須滿足這個順序
- 如果繼承至多個基類 class B(A1,A2,A3 ...), 這時B的mro序列 mro(B) = [B] + merge(mro(A1), mro(A2), mro(A3) ..., [A1,A2,A3])
- merge操作就是C3算法的核心淳梦。遍歷執(zhí)行merge操作的序列,如果一個序列的第一個元素昔字,在其他序列中也是第一個元素爆袍,或不在其他序列出現(xiàn)首繁,則從所有執(zhí)行merge操作序列中刪除這個元素,合并到當前的mro中陨囊。merge操作后的序列弦疮,繼續(xù)執(zhí)行merge操作,直到merge操作的序列為空蜘醋。如果merge操作的序列無法為空胁塞,則說明不合法。
-[E]+ merge([A,D,O],[B,O],[C,D,O],[D,O],[A,B,C,D]) = [E,A,B,C,D]
- 定制類
- __str__ , 相當于java, toString print時 打印自己定制的信息
- __repr__ , 直接敲變量不用print,還是不好看, 這是因為直接顯示變量對用的不是__str__返回用戶看到的字符串, 而__repr__ 返回程序開發(fā)者看到的字符串, 就是說, __repr__()是為調(diào)試服務的
- __iter__ , __next__如果一個類想被用于for ... in循環(huán)压语,類似list或tuple那樣啸罢,就必須實現(xiàn)一個iter()方法,該方法返回一個迭代對象无蜂,然后伺糠,Python的for循環(huán)就會不斷調(diào)用該迭代對象的next()方法拿到循環(huán)的下一個值,直到遇到StopIteration錯誤時退出循環(huán)斥季。
class Fib(object): def __init__(self): self.a, self.b = 0,1 def __iter__(self): return self # 實例對象本身就是迭代對象 def __next__(self): self.a, self.b = self.b, self.a +self.b if self.a >100000: raise StopIteration() return self.a
- __getitem__ 要表現(xiàn)的像list那樣下標去除元素, 需要 實現(xiàn) __getitem__() def __getitem__方法 (self, n):
- 切片功能list[1:4:2], 是指傳入?yún)?shù)可能為int值, 也可能為slice對象
- __setitem__ 方法, 把對象視為list或dict來對集合賦值,
- __delitem__方法, 用于刪除某個元素.
- 通過上述定制方法, 我買的自己定義的類表現(xiàn)的和Python自帶的list,tuple,dict沒什么分別, 這完全歸功與動態(tài)語言的"鴨子類型", 不需要強制集成那個接口
- __getattr__ 當調(diào)用不存在的屬性時训桶,比如score,Python解釋器會試圖調(diào)用getattr(self, 'score')來嘗試獲得屬性酣倾,這樣舵揭,我們就有機會返回score的值,返回函數(shù)也是完全可以的:注意,只有在沒有找到屬性的情況下躁锡,才調(diào)用getattr午绳,已有的屬性,比如name映之,不會在getattr中查找拦焚。此外,注意到任意調(diào)用如s.abc都會返回None杠输,這是因為我們定義的getattr默認返回就是None赎败。要讓class只響應特定的幾個屬性,我們就要按照約定蠢甲,拋出AttributeError的錯誤:
- __call__() 任何類, 只需要定義一個__call__()方法, 就可以直接再實例本身調(diào)用, __call__()還可以定義函數(shù), 對實例進行直接調(diào)用就好比對一個函數(shù)調(diào)用一樣, 所以你完全可以把對象堪稱函數(shù), 把函數(shù)漢城對象, 因為兩者之間本沒有根本區(qū)別. 那么, 怎么判斷一個變量是對象還是函數(shù)? 其實, 更多的時候, 我買的需要判斷一個對象是否能被調(diào)用, 能被調(diào)用的就是一個Callable對象, 比如函數(shù)和我買的上面定義的帶有__call__ 的類實例
- 枚舉類
- 定義 , value屬性則是自動賦予成員變量的int常量, 默認從1 開始計數(shù)
from enum import Enum Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
- 這樣我們就獲得了Month類型的枚舉類, 可以直接使用Month.Jan 來引用一個變量, 或者枚舉他的所有成員
for name, member in Month.__members__.items(): print(name, '=>' , member, ',', member.value)
- 精確定義, 如果需要更精確的控制枚舉類型, 可以從Enum派生自定義類
- py文件名不能有數(shù)字
- 定義 , value屬性則是自動賦予成員變量的int常量, 默認從1 開始計數(shù)
-
調(diào)試
- 調(diào)試的方式:
- 用print()把可能有問題的變量打印出來看看
- 使用assert(斷言)
- 使用logging(日志)僵刮,logging不會拋出錯誤,而且可以輸出到文件鹦牛。
- pdb(Python的調(diào)試器)搞糕,讓程序以單步的方式運行
- pdb.set_trace()
- IDE打斷點
- 調(diào)試的方式:
-
idea 開發(fā)
- 安裝python plugin
- new project create virtual env
-
文檔調(diào)試
- 將運行結(jié)果寫在文檔注釋中, 更直接, 需要用doctest組件
def abs(n): ''' Function to get absolute value of number. Example: >>> abs(1) 1 >>> abs(-1) 1 >>> abs(0) 0 ''' return n if n >= 0 else (-n) if __name__=='__main__': import doctest doctest.testmod()
- 文檔注釋的測試方式,無疑更明確地告訴函數(shù)的調(diào)用者該函數(shù)的期望輸入和輸出。并且曼追,Python內(nèi)置的“文檔測試”(doctest)模塊可以直接提取注釋中的代碼并執(zhí)行測試窍仰。doctest嚴格按照Python交互式命令行的輸入和輸出來判斷測試結(jié)果是否正確。只有測試異常的時候礼殊,可以用...表示中間一大段煩人的輸出驹吮。
- doctest非常有用鲫忍,不但可以用來測試,還可以直接作為示例代碼钥屈。通過某些文檔生成工具悟民,就可以自動把包含doctest的注釋提取出來。用戶看文檔的時候篷就,同時也看到了doctest射亏。
- 將運行結(jié)果寫在文檔注釋中, 更直接, 需要用doctest組件
-
IO編程
- IO在計算機中指Input/Output,也就是輸入和輸出竭业。由于程序和運行時數(shù)據(jù)是在內(nèi)存中駐留智润,由CPU這個超快的計算核心來執(zhí)行,涉及到數(shù)據(jù)交換的地方未辆,通常是磁盤窟绷、網(wǎng)絡等,就需要IO接口咐柜。
- 很明顯兼蜈,使用異步IO來編寫程序性能會遠遠高于同步IO,但是異步IO的缺點是編程模型復雜拙友。想想看为狸,你得知道什么時候通知你“漢堡做好了”,而通知你的方法也各不相同遗契。如果是服務員跑過來找到你辐棒,這是回調(diào)模式,如果服務員發(fā)短信通知你牍蜂,你就得不停地檢查手機漾根,這是輪詢模式■昃海總之辐怕,異步IO的復雜度遠遠高于同步IO。
- 操作IO的能力都是由操作系統(tǒng)提供的贡茅,每一種編程語言都會把操作系統(tǒng)提供的低級C接口封裝起來方便使用秘蛇,Python也不例外其做。我們后面會詳細討論Python的IO編程接口顶考。
-
文件讀寫
- 讀文件, 使用Python內(nèi)置open()函數(shù), 傳入文件名和標示符:
f = open('./test.txt','r') f.read()
- 前邊講的默認都是讀取文本文件, 并且是UTF-8編碼的文本文件, 要讀取二進制文件,比如圖片, 視頻等等, 用'rb'模式打開文件即可:
- 要讀取非UTF-8編碼的文本文件, 需要給open()函數(shù)傳入enconding參數(shù), 例如讀取GBK編碼的文件:
f = open('/Users/michael/test.txt','r', encoding='gbk', errors='ignore')
- IO操作需要調(diào)用close()方法關閉文件, 文件使用完畢后必須關閉, 因為文件對象繪制用操作系統(tǒng)的資源, 并且操作系統(tǒng)同一時間能打開的文件數(shù)量也是有限的, 文件讀寫易產(chǎn)生IOError, 一旦出錯, 后面的f.close()就不會調(diào)用, 所以為保證無論是否錯誤都能正確的關閉文件, 我們可以使用 try ... finally來實現(xiàn)
try: f = open('/path/to/file', 'r') print(f.read()) finally: if f: f.close()
- 這樣寫太繁瑣, 所以, Python引入了with語句來自動幫我們調(diào)用close()方法:
with open('/path/to/file','r') as f: print(f.read())
- 調(diào)用read()會一次性讀取文件的全部內(nèi)容,如果文件有10G妖泄,內(nèi)存就爆了驹沿,所以,要保險起見蹈胡,可以反復調(diào)用read(size)方法渊季,每次最多讀取size個字節(jié)的內(nèi)容朋蔫。另外,調(diào)用readline()可以每次讀取一行內(nèi)容却汉,調(diào)用readlines()一次讀取所有內(nèi)容并按行返回list驯妄。因此,要根據(jù)需要決定怎么調(diào)用合砂。如果文件很小青扔,read()一次性讀取最方便;如果不能確定文件大小翩伪,反復調(diào)用read(size)比較保險微猖;如果是配置文件,調(diào)用readlines()最方便:
- file-like object: 像open()函數(shù)返回的這種有個read()方法的對象, 在Python中統(tǒng)稱為file-like Object. 出了file外, 還可以是內(nèi)存的字節(jié)流, 網(wǎng)絡流, 自定義流等等. file-like Object不要求從特定類繼承, 只要寫個read()方法就行.
- StringIO就是在內(nèi)存中創(chuàng)建的file-like Object, 常用作臨時緩沖.
- 寫文件: 寫文件和讀文件是一樣的, 唯一區(qū)別是調(diào)用open()函數(shù)時, 傳入標識符'w'或者'wb'表示寫文本文件或?qū)懚M制文件:
f = open('/Users/michael/test.txt','w') f.write('Hello, world') f.close()
- 讀文件, 使用Python內(nèi)置open()函數(shù), 傳入文件名和標示符:
-
StringIO和BytesIO: StringIO和BytesIO是在內(nèi)存中操作str和bytes的方法缘屹,使得和讀寫文件具有一致的接口凛剥。
from io import BytesIO fff = BytesIO() fff.write('中文ss'.encode('utf-8')) print(fff.getvalue())
from io import StringIO f = StringIO(' Hello! \nHi!\nGoodbye!') while True: s = f.readline() if s == '': break print(s.strip())
-
操作文件和目錄
import os
- os.name # 操作系統(tǒng)類型 posix ,說明為Linux, Unit 或 Mac OS X, 如果是 nt, 就是windows系統(tǒng), 詳細信息用os.uname函數(shù), window函數(shù)Windows上不提供, os模塊的某些函數(shù)是跟操作系統(tǒng)相關的.
- 環(huán)境變量: os.environ, 過去某個環(huán)境變量的值, 可以調(diào)用os.envirsion.get('key')
os.path.abspath('.') # 查看當前目錄的絕對路徑 os.path.join(os.path.abspath('.'),'testdir') # 把兩個路徑拼接,不直接拼字符串, 而是通過 os.path.join()函數(shù), 這樣可以正確處理不同操作系統(tǒng)的路徑分隔符Linux/Unix/Mac '/' windows '\' os.path.split(os.path.abspath('.')) # 同樣的道理,要拆分路徑時轻姿,也不要直接去拆字符串犁珠,而要通過os.path.split()函數(shù),這樣可以把一個路徑拆分為兩部分互亮,后一部分總是最后級別的目錄或文件名: os.makedir(os.path.join(os.path.abspath('.'),'testdir')) # 創(chuàng)建目錄 os.rmdir(os.path.join(os.path.abspath('.'),'testdir')) #刪除一個目錄 os.path.splitext() # 可以直接讓你得到文件擴展名, 很多時候非常方便 os.rename('test.txt','test.py') # 對文件重命名 os.remove('test.py') # 刪掉文件
- 文件賦值可以通過shutil模塊的copyfile()函數(shù), shutil中海油很多使用函數(shù), 可以叫做os模塊的補充
- 列出當前目錄下的所有目錄
[x for x in os.listdir('.') if os.path.isdir(x)]
- 列出當前目錄下的所有.py文件
[x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1] == '.py']
- Python的os模塊封裝了操作系統(tǒng)的目錄和文件操作盲憎,要注意這些函數(shù)有的在os模塊中,有的在os.path模塊中胳挎。
-
序列化: 將數(shù)據(jù)從內(nèi)存變成可存儲或傳輸?shù)倪^程稱為序列化, 在Python中成為pickling, 在其他語言中稱為serialization, marshalling, flattening, 都是一個意思.
- 獲取序列化數(shù)據(jù):
import pickle d = dict(name='Bob', age=20, score=88) pickle.dumps(d)
- 將序列化數(shù)據(jù)寫入文件:
f = open('dump.txt','rb') d = pickle.dump(d,f) f.close()
- 將對象從磁盤讀到內(nèi)存時, 可以先將內(nèi)容讀到一個bytes, 然后用pickle.loads()方法反序列化出對象, 也可以直接pickle.load()方法從一個file-like Object中直接反序列化出對象
f = open('dump.txt','rb') d = pickle.load(f) f.close() d
- Pickle的問題和其他編程語言的反序列化問題一樣, 就是他只能用于Python, 并且可能不同版本的Python彼此都不兼容, 因此, 只能用Pickle保存那些不重要的數(shù)據(jù), 不能充公的反序列化也沒關系
- 為傳遞對象, 可以用JSON作為標準存儲格式
import json fff = open('dump.txt','w') d = dict(name='Bob', age=20, score=88) json.dumps(d) # '{"age": 20, "score": 88, "name": "Bob"}' json.dump(d,fff) # 類似的饼疙,dump()方法可以直接把JSON寫入一個file-like Object。 json_str = '{"age": 20, "score": 88, "name": "Bob"}' json.loads(json_str) json.load(open('dump.txt','r').read())
- 對象的序列化和反序列化通過dict進行轉(zhuǎn)換
def student2dict(std): return{ 'name': std.name, 'age': std.age, 'score': std.score } json.dumps(s, default=student2dict) # 對象的序列化 json.dumps(s, default=lambda obj: obj.__dict__)
def dict2student(d): return Student(d['name'], d['age'], d['score']) print(json.loads(json_str, object_hook=dict2student)) #<__main__.Student object at 0x10cd3c190> 打印出反序列化的Student實例對象
- Python語言特定的序列化模塊是pickle慕爬,但如果要把序列化搞得更通用窑眯、更符合Web標準,就可以使用json模塊医窿。json模塊的dumps()和loads()函數(shù)是定義得非常好的接口的典范磅甩。當我們使用時,只需要傳入一個必須的參數(shù)姥卢。但是卷要,當默認的序列化或反序列機制不滿足我們的要求時,我們又可以傳入更多的參數(shù)來定制序列化或反序列化的規(guī)則独榴,既做到了接口簡單易用僧叉,又做到了充分的擴展性和靈活性。
- 獲取序列化數(shù)據(jù):
進程和線程:線程是最小的執(zhí)行單元棺榔,而進程由至少一個線程組成瓶堕。如何調(diào)度進程和線程,完全由操作系統(tǒng)決定症歇,程序自己不能決定什么時候執(zhí)行郎笆,執(zhí)行多長時間谭梗。多進程和多線程的程序涉及到同步、數(shù)據(jù)共享的問題宛蚓,編寫起來更復雜激捏。
process: 在Unix/Linux下,可以使用fork()調(diào)用實現(xiàn)多進程凄吏。要實現(xiàn)跨平臺的多進程缩幸,可以使用multiprocessing模塊。進程間通信是通過Queue竞思、Pipes等實現(xiàn)的表谊。
Thread, 多線程編程, 模型復雜, 容易發(fā)生沖突, 必須用鎖加以隔離, 同時, 又要小心死鎖的發(fā)生, Python解釋器由于設計時有GIL全局鎖, 導致了多線程無法利用多核, 多線程的并發(fā)在Python 就是一個美麗的夢.
-
ThreadLocal對象:一個ThreadLocal變量雖然是全局變量,但每個線程都只能讀寫自己線程的獨立副本盖喷,互不干擾爆办。ThreadLocal解決了參數(shù)在一個線程中各個函數(shù)之間互相傳遞的問題。
- 全局變量local_school就是一個ThreadLocal對象课梳,每個Thread對它都可以讀寫student屬性距辆,但互不影響。你可以把local_school看成全局變量暮刃,但每個屬性如local_school.student都是線程的局部變量跨算,可以任意讀寫而互不干擾,也不用管理鎖的問題椭懊,ThreadLocal內(nèi)部會處理诸蚕。可以理解為全局變量local_school是一個dict氧猬,不但可以用local_school.student背犯,還可以綁定其他變量,如local_school.teacher等等盅抚。ThreadLocal最常用的地方就是為每個線程綁定一個數(shù)據(jù)庫連接漠魏,HTTP請求,用戶身份信息等妄均,這樣一個線程的所有調(diào)用到的處理函數(shù)都可以非常方便地訪問這些資源柱锹。
-
多進程vs多線程
- 多進程穩(wěn)定性高, 多線程易崩潰
- 計算密集型任務適合單任務, 代碼運行效率高, c語言; IO密集型適合多任務, CPU等待IO操作
- 異步IO:考慮到CPU和IO之間巨大的速度差異,一個任務在執(zhí)行的過程中大部分時間都在等待IO操作丰包,單進程單線程模型會導致別的任務無法并行執(zhí)行禁熏,因此,我們才需要多進程模型或者多線程模型來支持多任務并發(fā)執(zhí)行√躺常現(xiàn)代操作系統(tǒng)對IO操作已經(jīng)做了巨大的改進匹层,最大的特點就是支持異步IO隙笆。如果充分利用操作系統(tǒng)提供的異步IO支持锌蓄,就可以用單進程單線程模型來執(zhí)行多任務升筏,這種全新的模型稱為事件驅(qū)動模型,Nginx就是支持異步IO的Web服務器瘸爽,它在單核CPU上采用單進程模型就可以高效地支持多任務您访。在多核CPU上,可以運行多個進程(數(shù)量與CPU核心數(shù)相同)剪决,充分利用多核CPU灵汪。由于系統(tǒng)總的進程數(shù)量十分有限,因此操作系統(tǒng)調(diào)度非常高效柑潦。用異步IO編程模型來實現(xiàn)多任務是一個主要的趨勢享言。對應到Python語言,單線程的異步編程模型稱為協(xié)程渗鬼,有了協(xié)程的支持览露,就可以基于事件驅(qū)動編寫高效的多任務程序。我們會在后面討論如何編寫協(xié)程譬胎。
-
正則模塊
- 使用
import re re.match(r'^\d{3}\-\d{3,8}$', '010-12345') # 匹配成功返回Match對象, 否則返回None test = '用戶輸入的字符串' if re.match(r'正則表達式', test): print('ok') else: print('failed')
- 可用來切割字符串
re.split(r'\s+', 'a b c') # ['a','b','c'] re.split(r'[\s\,]+', 'a,b, c, d') #['a','b','c','d'], 可以用正則表達式來把不貴但的輸入轉(zhuǎn)化為正確的數(shù)組.
- 分組: 除了簡單地判斷是否匹配之外差牛,正則表達式還有提取子串的強大功能。用()表示的就是要提取的分組(Group)
m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345') # <_sre.SRE_Match object; span=(0, 9), match='010-12345'> # 如果正則表達式定義了組, 就可以再Match對象上用group()方法提取出字串來. m.group(0) # '010-12345' m.group(1) # '010' m.group(2) # '12345'
- 當時在Python中使用正則表達式, re模塊內(nèi)部會干兩件事:
- 編譯正則表達式, 如果正則表達式字符串本身不合法, 后報錯;
- 用編譯后的正則表達式去匹配字符串.
- 如果一個正則要重復使用幾千次, 出于效率考慮, 我們可以預編譯該正則表達式, 接下來就不需要編譯這個步驟了, 直接匹配:
import re re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$') re_telephone.match('010-12345').groups() # ('010','12345') re_telephone.match('010-8086').groups() # ('010','8086')
- 使用
-
datetime
- 當前時間的獲取: now= datetime.now()
- 獲取指定日期和時間, 我們可以直接用參數(shù)構(gòu)建一個datetime: dt = datetime(2017,11,29,17,27)
- timestamp對象
- 在計算機中堰乔,時間實際上是用數(shù)字表示的偏化。我們把1970年1月1日 00:00:00 UTC+00:00時區(qū)的時刻稱為epoch time,記為0(1970年以前的時間timestamp為負數(shù))镐侯,當前時間就是相對于epoch time的秒數(shù)侦讨,稱為timestamp。
- timestamp = 0 = 1970-1-1 00:00:00 UTC+0:00
- 北京 timestamp = 0 = 1970-1-1 08:00:00 UTC+8:00
- 可見timestamp的值與時區(qū)毫無關系苟翻,因為timestamp一旦確定搭伤,其UTC時間就確定了,轉(zhuǎn)換到任意時區(qū)的時間也是完全確定的袜瞬,這就是為什么計算機存儲的當前時間是以timestamp表示的怜俐,因為全球各地的計算機在任意時刻的timestamp都是完全相同的(假定時間已校準).
- 轉(zhuǎn)換
from datetime import datetime dt = datetime(2017,11,29,20) dt.timestamp()
- 在計算機中堰乔,時間實際上是用數(shù)字表示的偏化。我們把1970年1月1日 00:00:00 UTC+00:00時區(qū)的時刻稱為epoch time,記為0(1970年以前的時間timestamp為負數(shù))镐侯,當前時間就是相對于epoch time的秒數(shù)侦讨,稱為timestamp。
-
collections: collections是Python內(nèi)建的一個集合模塊膨蛮,提供了許多有用的集合類
- namedtuple: namedtuple是一個函數(shù)耀销,它用來創(chuàng)建一個自定義的tuple對象,并且規(guī)定了tuple元素的個數(shù)憋沿,并可以用屬性而不是索引來引用tuple的某個元素
- deque:deque 是為了高效實現(xiàn)插入和刪除操作的雙向列表汞扎,適合用于隊列和棧季稳。可以再首或者尾進行刪除和插入
- defaultdict: 使用dict時澈魄,如果引用的Key不存在景鼠,就會拋出KeyError。如果希望key不存在時,返回一個默認值铛漓,就可以用defaultdict
- OrderedDict: 可以保證key的順序
-
base64: Base64是一種任意二進制到文本字符串的編碼方法溯香,常用于在URL、Cookie浓恶、網(wǎng)頁中傳輸少量二進制數(shù)據(jù)玫坛。
import base64 print(base64.b64encode(b'binary\x00string')) # 如果要編碼的二進制數(shù)據(jù)不是3的倍數(shù),最后會剩下1個或2個字節(jié)怎么辦包晰?Base64用\x00字節(jié)在末尾補足后湿镀,再在編碼的末尾加上1個或2個=號,表示補了多少字節(jié)伐憾,解碼的時候勉痴,會自動去掉。 print(base64.b64decode(b'YmluYXJ5AHN0cmluZw==')) # 由于標準的Base64編碼后可能出現(xiàn)字符+和/树肃,在URL中就不能直接作為參數(shù)蚀腿,所以又有一種"url safe"的base64編碼,其實就是把字符+和/分別變成-和_: print(base64.urlsafe_b64decode(b'i\xb7\x1d\xef\xff')) # 由于=字符也可能出現(xiàn)在Base64編碼中扫外,但=用在URL莉钙、Cookie里面會造成歧義,所以筛谚,很多Base64編碼后會把=去掉 # 去掉=后怎么解碼呢磁玉?因為Base64是把3個字節(jié)變?yōu)?個字節(jié),所以驾讲,Base64編碼的長度永遠是4的倍數(shù)蚊伞,因此,需要加上=把Base64字符串的長度變?yōu)?的倍數(shù)吮铭,就可以正常解碼了时迫。
-
hashlib: 摘要算法在很多地方都有廣泛的應用。要注意摘要算法不是加密算法谓晌,不能用于加密(因為無法通過摘要反推明文)掠拳,只能用于防篡改,但是它的單向計算特性決定了可以在不存儲明文口令的情況下驗證用戶口令纸肉。
import hashlib md5 = hashlib.md5() md5.update('how to use md5 in python hashlib?'.encode('utf-8')) print(md5.hexdigest()) # 如果數(shù)據(jù)量很大, 可以分塊多次調(diào)用update(), 最后計算結(jié)果一樣 md5_2 = hashlib.md5() md5_2.update('how to use md5 in '.encode('utf-8')) md5_2.update('python hashlib?'.encode('utf-8')) print(md5_2.hexdigest())
hmac:Python內(nèi)置的hmac模塊實現(xiàn)了標準的Hmac算法溺欧,它利用一個key對message計算“雜湊”后的hash,使用hmac算法比標準hash算法更安全柏肪,因為針對相同的message姐刁,不同的key會產(chǎn)生不同的hash。(加鹽)
itertools: itertools模塊提供的全部是處理迭代功能的函數(shù)烦味,它們的返回值不是list聂使,而是Iterator,只有用for循環(huán)迭代的時候才真正計算。