字節型編譯
如果Python在系統中有寫的權限,當程序運行時Python會把源碼編譯成字節碼(與系統環境無關)存在一個.pyc擴展名文件中,如果沒有修改源碼而重新運行程序時,不會進行編譯的步驟而使用字節碼,可以優化運行速度。
變量、對象和引用
變量與對象是分開的,它們通過引用來建立連接,變量名是對象的引用。變量是一個系統表的元素,擁有指向對象連接的空間;對象是分配的一塊內存,有倆個頭部信息,一個類型標志符去標識這個對象的類型,一個是引用的計數器(回收機制);引用是自動形成的從變量到對象的指針。
通常x,y指向不同的對象x is y應該是FALSE,但是Python內部會自動緩存小的數字和字符串,所以x,y都指向了緩存中的42的對象。
1 x = 42 2 y = 42 3 x == y 4 Out[15]: True 5 x is y 6 Out[16]: True
常用的內置類型
數字,字符串,列表,字典,元組,文件,集合,編程單元類型(函數、模塊、類),與實現相關的類型(編譯的代碼堆跟蹤),其他類型(類型、None、bool值)
不可變類型:數字,字符串,元組,bool(對於不可變類型和可變的類型進行修改時盡量使用 +=,節約一個對象的空間)
可變類型:字典,列表,集合(需要修改變量內容的方法則是直接在原來對象上修改)
函數是可變對象。類則是初始化時創建的一個命名空間,是獨立的存儲空間,實例化時會單獨創建一存儲空間給實例而函數不會。
函數幫助
dir()方法,列出可可調用的方法,包括雙下划線的方法
help()方法,傳達方法名,返回說明文檔
列表解析
[]、{}是列表解析,()是生成生成器。
1 M = [[1,2,3], 2 [4,5,6], 3 [7,8,9]] 4 col = [row[1] for row in M]
{}也可以是列表解析。列表解析比直接用for效率高。
map:map(function, iterable, ...),對iterable逐一進行function,返回的是迭代器
1 >>> map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10]) 2 <map at 0x3ebed04208>
filter:filter(function, iterable),filter() 函數用於過濾序列,過濾掉不符合條件的元素,返回由符合條件元素組成的迭代器。
1 def is_odd(n): 2 return n % 2 == 1 3 4 newlist = filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) 5 print(newlist) 6 <filter object at 0x0000003EBED044E0>
zip:取得一個或多個序列為參數,然后返回元組的列表,將這些序列中的並排的元素配對。長度不同則以最短的為准。
1 list(zip([1,2,3],[4,5,6,7])) 2 Out[38]: [(1, 4), (2, 5), (3, 6)]
字典&集合
都是哈希映射。
字典:是一系列的鍵值對,key:value,鍵必須是不可變對象(數字、字符串等)。dict是一個可迭代對象,next返回的是他的鍵。key可以是任意的不可比對象。
dict.get(key,default)、dict.setdefault(key,default)、dict.updata():合並操作(會覆蓋相同的元素)、dict.pop(key):刪除指定元素、dict.(zip([1,2,3],[4,5,6])):把倆個列表組合成鍵值對
集合:&:交集 |:並集 a ^ b:a不在b和b不在a的元素 a - b:b不在a的元素 <、>:包含。set()只能接受一個參數,如果有多個參數則只能變成list等類型。集合只能包含不可變對象,因為集合的實現是哈希結構,是根據只來映射,所以集合不能是list等可變的對象。
1 x = set('123456') 2 x 3 Out[3]: {'1', '2', '3', '4', '5', '6'} 4 type({}) # x = {}初始化是一個字典,如果想初始化一個集合只能用set()函數 5 Out[6]: dict
列表
嵌套的數組結構。(插入、刪除元素效率慢(盡量用append、extend、pop),查找效率快)
分片賦值是先刪除所選擇的分片的元素再進行插入操作。所以也會有效率的問題,所以盡量少用。如果是一個元素的替換則不屬於分片賦值
1 ['xsa','sd'] * 3 # [0] * 100 進行初始化
2 Out[20]: ['xsa', 'sd', 'xsa', 'sd', 'xsa', 'sd'] 3 'xsa' * 3 4 Out[21]: 'xsaxsaxsa'
文件
1 with open() as f: 2 f.readlines() # 讀取全部行,按行返回list 3 f.read() # 讀取全部內容,返回字符串 4 f.readline() #返回一行的內容,字符串 5 for line in open(): # open是一個可迭代對象,一行一行的讀取數據 6 pass
賦值
1 spam = 'Spam' 2 spam,ham = 'yum','YUM' # 自動轉成元組,存儲變量的值 3 [spam,ham] = ['yum','YUM'] 4 a,b,c,d = 'spam' 5 a,*b = 'spam' 6 spam = ham = 'lunch' 7 spam += 42
序列解包:帶星號的名稱可能只匹配單個或空的項,但是返回一個列表。只能有一個帶星號的名稱。
1 a,b,c,d,*e = 'spam' 2 a,b,c,d,e 3 Out[8]: ('s', 'p', 'a', 'm', []) 4 5 *a = 'spam' 6 SyntaxError: starred assignment target must be in a list or tuple 7 *a, = 'spam' 8 a 9 Out[11]: ['s', 'p', 'a', 'm']
print([object,...][,sep = ' '][,end = '\n'][,file = sys.stdout])
sep,end,file如果給出的話,必須作為關鍵字參數給定。file指定文本將要發送的文件,標准流或者類似文件的對象。帶有一個類似文件的write(string)方法的任何對象都可以傳遞。
1 print(x,y) 2 等價於 3 import sys 4 sys.stdout.write(str(x) + str(y) + '\n') 5 6 # 重定向到指定文件(也可以直接指定file參數 -) 7 import sys 8 sys.stdout = open('file_path','w/a/..',encodeing = 'utf-8/....') 9 ... 10 print(x,y)
迭代器
單個迭代器:zip、map、filter不支持多個迭代器,生成器是單迭代器。
1 x = zip([1,2,3],[4,5,6]) 2 I1 = x 3 I2 = x 4 next(I1) 5 Out[52]: (1, 4) 6 next(I2) 7 Out[53]: (2, 5) 8 next(x) 9 Out[54]: (3, 6)
函數
函數與類相似也是一個命名空間,可以用fun.args來對函數里面的屬性進行賦值(與原來函數內部的屬性分開,存在__dict__變量里),但是不能查看或修改函數里面的屬性。
作用域:當你在一個程序中使用變量名時,python創建、改變或查找變量名都是在所謂的命名空間(一個保存變量名的地方)中進行的。在哪里賦值決定了他的命名空間。
LEGB原則:本地作用域(L)、上層結構中def或lambda的本地作用域(E)、全局作用域(G)、內置作用域(B)。
工廠函數:能記住嵌套作用域的變量值的函數,盡管那個作用域已經不在了。(類更適合記住狀態)
嵌套作用域中的變量在嵌套的函數被調用時才進行查找,所以實際記住的是相同的一個值(在最后一次循環迭代中循環變量的值)。可以使用默認參數來記住當前的狀態,默認參數是記錄在函數對象中的。
1 t = [lambda i:i * x for x in range(5)] # t = [lambda i,x = x:i * x for x in range(5)] 2 t[0](1) 3 Out[3]: 4 4 t[0](2) 5 Out[4]: 8
nonlocal:nonlocal應用於一個嵌套的函數的作用域中的一個名稱,而不是所有def之外的全局模塊作用域,聲明時該變量必須已經定義,否則將產生錯誤。對變量名的查找從嵌套的def的作用域中開始,而不是從本地作用域開始,只限定在嵌套的def中。不會在全局作用域中查找。(global查找也是跳過本地作用域)
參數
傳遞參數:1.參數的傳遞時通過自動將對象賦值給本地變量名來實現的。2.在函數內部的參數名的賦值不會影響調用者。3.改變函數的可變對象參數的值也許會對調用者有影響。4.不可變參數通過值傳遞的,可變參數通過“指針”進行傳遞的。
在函數調用中,參數必須以此順序出現:任何位置參數(value),后面跟着任何關鍵字參數(name = value)和*sequence形式的組合,后面跟着**dict形式。
在函數的頭部,參數必須以此順序出現:任意一般的參數(name),緊跟着任何的默認參數(name = value),如果有的話后面是*name或*的形式,后面跟着任何name或name = value keyword-only參數,后面跟着**name形式。
參數匹配:
1.通過位置分配非關鍵字參數。
2.通過匹配變量名分配關鍵字參數
3.其他額外的非關鍵字分配到*name元組中
4.其他額外的關鍵字參數分配到**name字典中。
5.用默認值分配給在頭部未得到分配的參數。
1 def func(a,b,c,d): 2 print(a,b,c,d) 3 func(1,c = 3,*(2,),**{'d':4}) 4 1 2 3 4
1 def f(a,*b,c = 6,**d): 2 print(a,b,c,d) 3 f(1,*(2,3),**dict(x = 4,y = 5)) 4 1 (2, 3) 6 {'x': 4, 'y': 5}
import
流程:在第一次導入文件時執行以下三個步驟,如果導入相同的模塊則只是提取內存中已加載的模塊對象。Python把已加載的模塊存儲到sys,modules表中。如果需要再次導入要調用reload。
1.找到模塊文件(sys.path)
2.編譯成位碼(需要時)
3.執行模塊的代碼來創建其所定義的對象
搜索路徑:1.程序的主程序 2.PYTHONPATH目錄 3.標准鏈接庫目錄 4.任何的.pth文件的內容(如果存在的話 )
模塊文件選擇:.py .pyc 目錄 編譯擴展模塊(通常用C/C++)等不限於Python文件
from:除了會讀取整個模塊(運行),並把變量名賦值到另一個作用域的同名變量(可變對象是引用)
1 from module import name1,name2 2 # 等價於 3 import module 4 name1 = module.name1 5 name2 = module.name2 6 del module
包導入:以'.'相隔路徑名。包導入語句的路徑中的每一個目錄內都必須有一個__init__.py(導入包會先運行此文件的代碼但可以為空,用來標識此目錄是包)文件,否則這個包導入失敗。
包的相對導入(包內文件的導入原則):from語句現在可以使用前面的點號 "." 來指定,它們需要位於同一包中的模塊(包的相對導入),而不是位於模塊導入搜索路徑的模塊(絕對導入)。不會在sys.path中查找,只在包內查找。否則在sys.path中查找。(只適用於from語句,import默認絕對導入)
類
類:實例工廠。類的屬性提供了行為(數據以及函數),所有從類產生的實例都繼承該類的屬性
實例:代表程序領域中具體的元素。實例屬性記錄數據,而每個特定對象的數據都不同。初始化一個實例都會分配單獨的存儲空間。不可以修改父類的數據,只能引用,如果進行賦值的操作會在實例空間初始化或引用賦值(father類有屬性x,而children實例運行children.x += 1不會修改father的值,會在自己的命名空間上先引用父類的x在+1,然后存放在自己的實例空間上,children有x屬性)
屬性繼承搜索:先搜索本地實例,然后是該對象之上的所有類,由下至上,由左到右。(廣度優先)
self:self指向調用自己的實例對象。
__class__:顯示實例鏈接到父類名稱
__bases__:一個元組,顯示繼承的父類
__bese__:顯示第一個父類名稱
__dict__:顯示模塊的屬性(dict形式)
__doc__:文檔字符串(函數也有此屬性),常放在類的開頭,用'''.......'''
__slots__:這個屬性一般是在class語句頂層內將字符串名順序賦值給變量__slots__,只有__slots__列表內的這些變量名可賦值給實例屬性(__slots__內的變量類不能再定義)。如果實例想賦值實例屬性,則實例會搜索父類查看實例屬性是否存在父類的__slots__中,如果任意一父類存在則可以賦值,否則失敗。(如果父類的__slots__內有__dict__變量則都可以賦值,因為實例屬性賦值是通過調用__dict__方法,把變量名存__dict__字典中)
運算符重載:當類的實例出現在內置操作中,Python會自動調用你的方法。
運算符重載讓類攔截常規的Python運算 類可重載所有Python表達式運算符 重載使類實例的行為像內置類型 重載使通過提供特殊名稱的類方法來實現是
__call__:當調用實例時使用__call__方法。把實例當做一個方法來調用。
__init__(構造函數):初始化實例時自動調用此方法。
__sub__:捕獲減法表達式
__add__、__radd__:加法運算符,右側+(只有當+右側的對象是實例對象時才會調用__radd__,其他所有情況調用__add__)
__iadd__:X += Y
1 class Number: 2 def __init__(self,start): 3 self.data = start 4 def __sub__(self,other): 5 return Number(self.data - other)
__str__:打印、轉換(__str__比__repr__的優先級高)
1 class Str(object): 2 def __str__(self): 3 return "__str__ called" 4 def __repr__(self): 5 return "__repr__ called"
__getitem__:索引運算
__setitem__:索引賦值語句
__delitem__:索引和分片的刪除
1 class Indexer: 2 data = [5,6,7,8,9] 3 def __getitem__(self,index): 4 print('getitem:',index) 5 return self.data[index] 6 def __setitem__(self,index,value): 7 self.data[index] = value 8 x = Indexer() 9 x[2:4] 10 slice(2, 4, None) 11 Out[38]: [7, 8]
12 for item in x: # for語句的作用是從0到更大的索引值,每次循環都會調用類的__getitem__,可以支持所有的迭代環境(in、列表解析、map、iter)
13 print(item)
__iter__、__next__:迭代環境(Python中所有的迭代環境都會先嘗試__iter__,在嘗試__getitem__)
__contains__:in 方法,for i in range(5)調用__iter__
1 class Squares: 2 def __init__(self,start,stop): 3 self.value = start - 1 4 self.stop = stop 5 def __iter__(self): 6 return self 7 def __next__(self): 8 if self.value == self.stop: 9 raise StopIteration 10 self.value += 1 11 return self.value ** 2
__getattr__:屬性引用(如果不能再類中找到屬性,則調用此方法)
__setattr__:屬性賦值(攔截所有屬性賦值操作)
__delattr__:屬性刪除
__getattribute__:攔截所有屬性,而不只那些未定義的屬性。
1 class Empty: 2 def __getattr__(self,attrname): 3 if attrname == 'age': 4 return 40 5 else: 6 raise AttributeError 7 def __setattr__(self,attrname,value): 8 if attrname == 'age': 9 self.__dict__[attrname] = value 10 else: 11 raise AttributeError
__or__:or運算符
__lt__、 __gt__、 __le__、 __ge__、 __eq__、 __ne__:<、>、<=、>=、==、!=
__bool__、__len__:bool()、len()。進行布爾值的判斷,如果沒有定義__bool__則會找__len__(一個非0的長度為True)
__del__:析構函數,回收實例空間是自動運行
偽私有屬性:用一個 _ 來編寫內部名稱(_X),但這只是讓你知道此變量是一個不應該修改的名字,不是一個真正的私有變量。
變量名壓縮:class語句內開頭有倆個下划線但是結尾沒有倆個下划線的變量名會自動擴張,從而包含了所在類的名稱。例如:Spam類內__X的變量名會自動變成_Spam__X,原始變量名會在頭部加入一個下划線,然后是所在類名稱。(防止變量名的沖突)
裝飾器
1 class D: 2 @staticmethod(A,B) 3 def meth(self): 4 pass 5 # 等價於 6 class D: 7 def meth(self): 8 pass 9 meth = staticmethod(A,B)(meth)
1 def decorator(F): 2 def wrapper(*args,**kargs): 3 ... 4 F(*args,**kargs) 5 ... 6 return wrapper