Python3 我的學習總結
python 3
a = 5
b = 10
a,b = b,a
print(a,b)
10 5
Python 只需要一句a,b = b,a 交換兩個變數的值,如果其他語言,可能需要 temp = a , a = b, b = temp 至少這三步吧,哪種語言更優雅
基礎部分
9/3 = 2.0 除法結果是浮點數,即使兩個數都是整數,結果也是浮點數
10 // 3 = 3 結果是整數 即使除不盡
10 %3 = 1 取餘運算 結果為整數
關於編碼
ord(‘A’) = 65
chr(66) = ‘B’
b’ABC’ 把 str 變成 bytes,用於儲存或者傳輸
len(’str’) 計算及字元數
list 和tuple
List 相當於陣列,可變,可以是任何資料型別 L= [ ] 或 L = list()即建立一個空的 list,list 可以包含 list,tuple也可以包含 list, 所以即使 tuple 是不可變的,其實仍然可變,不可變的只是引用.所以你想幹壞事, Python 是阻止不了你的,全靠自覺.
a = [1,2,3] b = [4,5,6] c= a+b c = [1,2,3,4,5,6]
可以複製 list 中的元素[0]4 [0,0,0,0]
list 切片 可以使用指定上下限的方式來獲得一個 list 的切片 list = [1,2,3,4,5,6];
list[1:3] [2,3] 包括後面的不包括前面的
list.append()會自動新增到末尾
list.insert(index,object) 新增到指定的位置
list.pop() 出棧的方式 刪除末尾的元素
list. pop(index) 刪除指定位置的元素
list 中的元素可以是各種型別混合
tuple 元組 ,不可變的 但是可以包含 list 作為其中的一個元素 T= () 如果只含有一個元素的 tuple,需要在這一個元素後面加”,” 消除歧義,如 tuple = (‘Bob’,)必須在單個元素後面加’,’
其實 tuple也並不是完全不可變的, tuple裡面可以包含 list, list 可變,只是 tuple 每個元素的指向不能變
dict 相當於字典,鍵值對, d = {name: ‘bob’, } d.keys() 獲取所有的 key, d.values 獲取所有的 value
d.copy 獲取一個拷貝
d.pop(key)刪除一個 key 值, value 值也會被刪除
d.get(key) 查詢對應 key 值得 value 如果不存在 返回 None 另外還可以d.get(key,-1) 自己指定一個值如果 key 不存在 直接返回這個值
凡是可作用於 for 迴圈的物件都是 Iterable (可迭代物件)型別
凡是可作用於 next() 函式的物件都是 Iterator(迭代器)型別,他表示一個惰性計算的序列
集合資料型別如 list dict str 等是 itterable 但不是 Iterator,不過可以通過 iter() 函式獲得一個 Iterator 物件, Python 的 for 迴圈本質上就是通過不斷呼叫 next() 函式實現的
可以使用 iter() 函式 傳入一個序列物件,即可變成可迭代物件,比如 iter = iter(list) 這個 iter 就變成了可迭代物件,如果列印它的型別可以看到
可變引數
函式如果傳入的引數是可變的 可以在引數前面加 *表示引數是可變的
關鍵字引數
def person (name,age, **kw):
print(name,age,kw)
函式 person 除了必選引數 name 和 age 外,還接受關鍵字引數 kw,這些關鍵字引數在函式內部自動組裝為一個 dict ,在呼叫該函式時可以只傳入必選引數,也可以傳入任意個數的關鍵字引數
person (‘bob’,20,city = ‘beijing’),它的作用就是可以擴充套件函式的功能
生成器 generator 他是在 for 迴圈的過程中不斷的計算出下一個元素,並在適當的條件下結束 for 迴圈,對於函式改成的 generator 來說遇到 return 語句或者執行到函式體的最後一句,就是結束 generator 的指令
高階函式
變數可以指向函式
函式名也是變數
一個函式可以接收另一個函式作為引數
map() 函式 接收兩個引數,一個是函式,一個是可迭代物件list 這種, map將傳入的函式依次作用於每一個 list 物件,並返回一個 list
reduce() 函式 傳入也是一個函式,一個 list, 將 list 的1,2 個元素執行 function, 然後將結果與第3個元素執行 function
filter() 接受一個函式和一個序列,函式會依次作用於每一個元素,然後根據返回值是 true 或者 false 保留和丟棄元素,返回一個生成器,需要要 list()函式接收
sorted() 排序高階函式 接受一個序列和函式
返回函式 一個函式可以返回一個計算結果 也可以返回一個函式,返回一個函式時,牢記該函式並未執行,返回函式中不要引用可能會變化的變數
如: def count():
deff(j):
def g():
return j *j
return g
fs = [ ]
for i in range(1,4):
fs.append(f(i))
return fs
匿名函式關鍵字 lambda表示,匿名函式有個限制,就是隻能有一個表示式,不用寫 return, 返回值就是該表示式的結果,匿名函式也是一個函式物件,可以把匿名函式賦值給一個變數,再利用變數來呼叫該函式,也可以把匿名函式作為返回值返回
偏函式 functors.partial 的作用就是,把一個函式的某些引數給固定住(也就是設定預設值),返回一個新的函式,呼叫這個函式會更簡單.當函式的引數個數太多,需要簡化時,可以使用 functools.partial 建立一個新函式,這個函式可以固定住原函式的部分引數,從而在呼叫時更簡單
decorator 裝飾器
不改變函式的定義在程式碼執行期間動態的增加功能的方式稱之為裝飾器
def log(func):
def wrapper(*args,**kw):
print(fund.name)
return func(*args,**kw)
return wrapper
把@ log 放在函式的定義處 相當於執行log(function)
多重繼承MixIn python 支援多重繼承機制
class MyTCPServer (TCPServer,ForkingMixIn):
pass
1>類是建立例項的模板,而例項是一個一個具體的物件,各個例項擁有的資料都相互獨立,互不影響;
2>方法就是與例項繫結的函式,和普通函式不同,方法可以直接訪問例項的資料
3>通過在例項上呼叫方法,我們就直接操作了物件內部的資料,但無需知道方法內部的實現細節
4> 和靜態語言不同, Python 允許對例項變數繫結任何資料,也就是說,對於兩個例項變數,雖然他們都是同一個類的不同例項,但擁有的變數名稱都可能不同.
初始化類和例項,例項屬性,類屬性
class Person(object):
def init(self,name,gender,birth):
self.name = name
self.gender = gender
self.birth = birth
注意:_ init_方法第一個引數必須是 self
xiaoming = Person(‘xiaoming’,’Male’,’1990-1-1’)
python 是動態語言 ,可以直接給他們的屬性賦值如:xiaoming.age = 20,就添加了一個age 屬性 ,但是這個屬性只屬於 xiaoming 這個例項物件,如 jack = Person(‘jack’,’Male’,’1992-0-0’) jack 訪問 age 屬性就會報錯
__name 表示私有變數 外部不可以直接訪問
類屬性是直接繫結在類上的,訪問類屬性時不需要建立例項 例項也可以訪問類屬性 不能在例項上修改類屬性 他並沒有修改類屬性 只是給例項繫結一個例項屬性 例項屬性的優先順序比類屬性高
Python 並不是絕對安全的語言,name 變數你也可以訪問到, instance._class. name 即可訪問到
class Student(object):
name = ‘Bob’
def init(self,age):
self.age = ages =Student(20)
s.name
‘Bob’
s.age
20
s.score = 100
s.score
100
Student.number = 1
Student.number
1
s.number
1
s.number = 10
s.number
10
Student.number
1
細心的同學可以看到,類屬性,例項屬性都是可以隨時新增的,例項屬性也能訪問類屬性並且賦值,但是你會發現賦值之後例項去訪問類屬性,值好像被修改了,然後再用類去訪問類屬性發現值其實沒有被修改,其實你用例項去訪問類屬性可以訪問到(如果你對例項沒有該屬性,否則會被覆蓋,並且例項的優先順序更高),但是你用例項去給類屬性賦值的時候,就相當於給例項重新添加了一個新的例項屬性,所以你怎麼改,類屬性都是不會變的,因為你改的根本不是它.
例項方法
例項方法就是在類中定義的函式,他的第一個引數永遠是 self, 指向呼叫該方法的例項本身
class Person(object):
def init(self,name):
self.__name = name
def get_name(self):
return self.__name
在 class 中定義的例項方法其實也是一種屬性,他實際上是一個函式物件 可以用 p1.get_grade = types.MethodType(fn_get_grade,p1,Person)把例項方法單獨繫結到某一個例項上,給一個例項動態新增方法並不常見,直接在 class 中定義要更加直觀
類方法
class Person(object):
count = 0
@classmethod
def how_many(cls):
return cls.count
def init(self,name):
self.name = name
Person.conut = Person.count +1
print Person.how_many()
p1 = Person(‘Bob’)
print Person.how_many()
通過標記一個@ classmethod,該方法將繫結到一個類上,類方法的第一個引數將傳入類本身,因為是在類上呼叫,而非例項上呼叫,因此類方法無法獲得任何例項變數,只能獲得類的引用
動態語言呼叫例項方法,不檢查型別,只要方法存在,引數正確,就可以呼叫,所以 Python 的多型,傳遞給一個方法的不一定是子類,任何資料型別的例項都可以
@property 裝飾器 把方法裝飾成屬性來呼叫
class Student(object):
def init(self, name, score):
self.name = name
self.__score = score
@property #修飾 getter 方法
def score(self):
return self.__score
@score.setter# 修飾 setter 方法
def score(self, score):
if score < 0 or score > 100:
raise ValueError(‘invalid score’)
self.__score = score
s = Student(‘Bob’, 59)
s.score = 60
print s.score
60
s.score = 1000
Traceback (most recent call last):
…
ValueError: invalid score
說明對 score 賦值實際呼叫的是 set方法
slots
由於 Python 是動態語言,任何例項在執行期間都可以動態的新增屬性,如果要限制新增的屬性,比如 Student 類只許新增 name,gender,score 這3個屬性,就可以用_ slots_來實現,就是指一個類允許的屬性列表,其實的目的就是節約記憶體
class Student (object):
slots = (‘name’,’gender’,’score’)
def__init__(self,name,gender,score):
self.name = name
self.gender = gender
self.score = score
s = Student(‘Bob’, ‘male’, 59)
s.name = ‘Tim’ # OK
s.score = 99 # OK
s.grade = ‘A’
Traceback (most recent call last):
…
AttributeError: ‘Student’ object has no attribute ‘grade’
call()
在 Python 中任何資料型別都可以看做物件,函式也是可呼叫物件,一個類例項也可以變成一個可呼叫物件,只需實現一個_ call_()方法
class Person(object):
def__init__(self,name,gender):
self.name = name
self.gender = gender
def call(self,friend):
print’my name is %s’%self.name
print’y friend is % s’%friend
p = Person (‘Bob’,’male’)
p(‘Tim’)
my name is Bob
my friend is Tim
type()函式 建立類
要建立一個 class,type()函式依次傳入3 個引數:1> class 的名稱
2> 繼承的父類集合, Python 是多重繼承,如果只有一個父類,別忘了 tuple的單元素寫法;
3>class 的方法名稱和函式繫結,這裡把函式 fn 繫結到方法名 hello 上
如:>>> def fn(self,name=‘world’):#先定義函式
…print (‘Hello,%s.’%name)
…
Hello = type(‘Hello’,(object,),dict(hello = fn)) #建立一個 Hello類
h = Hello()
h.hello()
Hello,world.
print(type(Hello))
attrs 就是將要創建出來的類的屬性
class ListMetaclass(type):
… def new(cls,name,bases,attrs):
… attrs[‘add’]= lambda self,value:self.append(value)
… return type.new(cls,name,bases,attrs)
…
class MyList(list,metaclass=ListMetaclass):
… pass
…
L = MyList()
L.add(1)
L
[1]
L2 = list()
L2.add(1)
Traceback (most recent call last):
File “”, line 1, in
AttributeError: ‘list’ object has no attribute ‘add’new() 方法接受的引數依次是:
1>當前準備建立的類的物件
2>類的名稱
3>類繼承的父類集合
4>類的方法集合
IO 程式設計
f=open(‘/Users/BobZhou/Desktop/hello.py’) #r 表示讀檔案 這樣就成功的打開了一個檔案
f.read() #把檔案讀進記憶體中用一個 str 物件表示
f.close() 檔案讀取完必須關閉 因為 IO 操作並不是立刻進行的,只是先寫進記憶體中,等作業系統空閒時再讀寫進磁碟,如果沒有關閉操作,將出現檔案丟失等問題
try:
f = open(‘ ’)
print(f.read())
finally:
if f:
f.close()
這樣寫比較麻煩 ,可用 with … as 的寫法來代替
with open (‘’,’r’)as f:
print(f.read())
StringIO 和 BytesIO
很多時候資料讀寫並不一定是檔案,也可以在記憶體中讀寫, StringIO 就是在記憶體中讀寫 str
from io import StringIO
f = StringIO()
f.write(‘hello’)
5
f.write(’ ‘)
1
f.write(‘world!’)
6
print(f.getvalue())
hello world!
getvalue() 用於獲取寫入後的 str
StringIO 操作的只能是 str, 如果要操作二進位制資料,就需要使用 BytesIO ,然後寫入一些 bytes:
from io import BytesIO
f = BytesIO()
f.write(‘中文’.encode(‘utf-8’))
6
print(f.getvalue())
b’\xe4\xb8\xad\xe6\x96\x87’
操作檔案和目錄
os.path.abspath(‘.’)# 檢視當前目錄的絕對路徑
os.path.join(‘users/bobzhou/desktop’,’testdir’)#在某個目錄下建立一個新目錄,首先把新目錄的完整路徑表示出來
os.mkdir(‘users/bobzhou/desktop/testdir’)#然後建立一個目錄
os.rmdir(‘/users/bobzhou/desktop/testdir’)#刪除一個目錄
把兩個路徑合成一個時,不要直接拼接字串,而是通過os.path.join()函式,這樣可以正確處理不同作業系統的路徑分隔符
要拆分路勁時,也不要直接去拆字串,通過 os.path.split()函式,後一部分總是最後級別的目錄或檔名
os.path.splitext() 可以直接獲得副檔名,這些合併拆分路徑的函式並不要求目錄和檔案真實存在,他們只對字串進行操作. os.rename(‘test.text’,’test.py’)# 重新命名
os.remove(‘test.py’)# 刪除檔案
os.listdir(path) 可以獲得路徑下的檔案列表 返回的是一個 list
序列化與反序列化
import pickle
d = dict(name = ‘bob’,age = 20,score = 90)
pickle.dumps(d)
b’\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x03\x00\x00\x00bobq\x02X\x03\x00\x00\x00ageq\x03K\x14X\x05\x00\x00\x00scoreq\x04KZu.’
也可以直接序列化寫入一個檔案 ,然後通過 pickle.load() 函式反序列化出物件
f= open(‘dump.text’,’wb’)
pickle.dump(d,f)
f.close()
f= open(‘dump.text’,’rb’)
d = pickle.load(f)
f.close()
d
{‘name’: ‘bob’, ‘age’: 20, ‘score’: 90}
變數的內容又回來了,但是這個變數和原來的變數是完全不相干的物件,他們只是內容相同而已
JSON 的序列化與反序列化
Python 內建的 json 模組提供了 Python 物件到 json 格式的轉換
import json
d = dict(name = ‘bob’,age = 20,score = 90)
data = json.dumps(d) # 把字典物件序列化成一個 json
reborn = json.loads(data)#反序列化
當然只操作字典物件沒什麼意義,有時候需要把一個類序列化為 json表示,如果直接寫肯定會報錯,原因就是不知道怎麼把一個類物件轉化為 json, 這裡可以把 class 的例項變為 dict,因為通常 class 例項都有一個_ dict_的屬性,他就是一個 dict,用來儲存例項變數,也有少數例外,比如定義了_ slots_ 的 class, 同樣的道理,如果我們要把 json 反序列化我一個物件,loads() 函式首先轉化為一個 dict物件,然後我們傳入 object_hook 函式負責把 dict轉換為 student 例項
import json
class Student (object):
def init(self,name,age,score):
self.name = name
self.age = age
self.score = score
def str(self):
return ‘Student object(%s %s %s)’%(self.name,self.age,self.score)s = Student(‘bob’,20,90)
std_data = json.dumps(s,default = lambda obj:obj.dict)
print (‘dump Stundent:’,std_data)
dump Stundent: {“age”: 20, “name”: “bob”, “score”: 90}
rebuild = json.loads(std_data,objct_hook = lambda d :Student(d[‘name’],d[‘age’],d[‘score’]))
Traceback (most recent call last):
File “
新執行緒執行的程式碼:
def loop():
print(‘thread %s is running…’ % threading.current_thread().name)
n = 0
while n < 5:
n = n + 1
print(‘thread %s >>> %s’ % (threading.current_thread().name, n))
time.sleep(1)
print(‘thread %s ended.’ % threading.current_thread().name)
print(‘thread %s is running…’ % threading.current_thread().name)
t = threading.Thread(target=loop, name=’LoopThread’)
t.start()
t.join()
print(‘thread %s ended.’ % threading.current_thread().name)
多執行緒同時訪問一個變數肯定會出現資料混亂的問題,因此 Python 也有執行緒鎖
balance = 0
lock = threading.Lock()
def run_thread(n):
for i in range(10000):
lock.acquire() # 先獲取鎖
try:
change_it(n)#放心改吧
finally:
lock.release()#改完了一定要釋放鎖,不然別的執行緒無法繼續訪問
多核 CPU
要想把 N 核 CPU 的核心全部跑滿,就必須啟東 N 個死迴圈執行緒
import threading,multiprocessing
def loop():
x = 0
while True:
x = x^1
for i in range (multiprocessing.cpu_count()):
t = threading.Thread(target = loop)
t.start()
在4 核 CPU 上可以監控到 CPU 佔用率僅有102%,也就是僅僅用了一核
因為 Python 的執行緒雖然是真正的執行緒,但直譯器執行程式碼時,會有一個 GIL 鎖,任何執行緒執行前,必須獲取鎖,然後每執行100條程式碼,直譯器就會自動釋放 GIL 鎖,讓別的執行緒有機會執行,所以多執行緒在 Python 中只能交替執行,即使100個執行緒跑在100核 CPU 上,也只能用到一個核,要想真正利用多核,除非重寫一個不帶 GIL 的 Python 直譯器.
ThreadLocal
在多執行緒環境下,每個執行緒都有自己的資料,一個執行緒使用自己的區域性變數比使用全域性變數好,因為區域性變數不會影響其他執行緒,而全域性變數的修改必須加鎖,但是區域性變數也有問題就是在函式呼叫時,傳遞起來麻煩
import threading
local_school = threading.local()
def process_student():
std = local_school.student
print(‘hello,%s (in %s)’ % (std,threading.current_thread().name))
def process_thread(name):
local_school.student = name
process_student()
t1 = threading.Thread(target=process_thread,args = (‘Alice’,),name = ‘Thread-A’)
t2 = threading.Thread(target=process_thread,args=(‘Bob’,),name = ‘Thread-B’)
t1.start()
t2.start()
t1.join()
t2.join()
collections
namedtuple()
這是一個函式,它用來建立一個自定義的tuple物件,並且規定了tuple的元素個數,並可以用屬性而不是索引來引用tuple的某個元素
from collections import named tuple
point = namedtuple(‘point’.[‘x’,’y’])
p = point(1.2)
p.x p.y
hash lib(MD5 SHA1)
import hashlib
md5 = hashlib.md5()
md5.update(‘hello’.encode(‘utf-8’))
print(md5.hexdigest())
5d41402abc4b2a76b9719d911017c592 # 32位16進位制字元
sha1 = hashlib.sha1()
sha1.update(‘hello’.encode(‘utf-8’))
print(sha1.hexdigest())
aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
網路程式設計
TCP 程式設計 和 UDP 程式設計
電子郵件 SMTP 傳送郵件 POP3 收取郵件
訪問資料庫
web 開發 HTML 協議 WSGI 介面 web框架
非同步 IO
這裡主要說下 WSGI 介面,其他的比如 HTTP 協議一本書都寫不完
一個 web 應用的本質其實就是:
1 瀏覽器傳送一個HTTP請求;
2 伺服器收到請求,生成一個HTML文件;
3 伺服器把HTML文件作為HTTP響應的Body傳送給瀏覽器;
4 瀏覽器收到HTTP響應,從HTTP Body取出HTML文件並顯示;
所以最簡單的 web 應用就是先把 HTML 用檔案儲存好,用一個現成的 HTTP 伺服器軟體,接收使用者請求,從檔案中讀取 HTML, 返回. Apache,Nginx,Lighttpd 等這些靜態伺服器就是幹這件事情的.
如果要動態的生成 HTML, 就需要把上面的步驟自己來實現, WSGI 介面就是做這個的,他只要求開發者實現一個函式,就可以響應 HTTP 請求,來個最簡單的 web版本Hello world
def application(environ,start_response):
start_response(‘200 OK ’,[(‘Content-Type’,’text/html’)])
return [b’
Hello,world
’]在 application() 中呼叫 start_response()函式就傳送了 HTTP 響應的 header, 然後函式的返回值將作為 HTTP 的 body 傳送到瀏覽器
不過這個函式怎麼呼叫呢?他必須由 WSGI 的伺服器來呼叫, Python 內建了一個,這個模組是 wsgiref.
再編寫一個 server.py
from wsgiref.simple_server import make_server
from hello import application # 從自己編寫的 hello.py 匯入 application() 函式
建立一個伺服器, IP 地址為空,埠8000,處理函式 application
http = make_server(‘’,8000,application)
print(‘Severing HTTP on port 8000…’)
http.serve_forever() # 開始監聽 HTTP 請求
啟動成功後,開啟瀏覽器,輸入 http://localhost:8000/ 就可以看到結果了