元類
-
Python 中類方法、類實例方法、靜態方法有何區別?
類方法:是類對象的方法,在定義時需要在上方使用“@classmethod”進行裝飾,形參為cls, 表示類對象,類對象和實例對象都可調用; 類實例方法:是類實例化對象的方法,只有實例對象可以調用,形參為self,指代對象本身; 靜態方法:是一個任意函數,在其上方使用“@staticmethod”進行裝飾,可以用對象直接調用, 靜態方法實際上跟該類沒有太大關系。
- Python 中如何動態獲取和設置對象的屬性?
1. if hasattr(Parent,'x'): 2. print(getattr(Parent,'x')) 3. setattr(Parent,'x',3) 4. print(getattr(Parent,'x'))
內存管理與垃圾回收機制
- Python 的內存管理機制及調優手段?
內存管理機制:引用計數、垃圾回收、內存池。 引用計數: 引用計數是一種非常高效的內存管理手段, 當一個Python 對象被引用時其引用計數增加1, 當 其不再被一個變量引用時則計數減1. 當引用計數等於0 時對象被刪除。 垃圾回收: 1. 引用計數 引用計數也是一種垃圾收集機制,而且也是一種最直觀,最簡單的垃圾收集技術。當Python 的某 個對象的引用計數降為0 時,說明沒有任何引用指向該對象,該對象就成為要被回收的垃圾了。比如 某個新建對象,它被分配給某個引用,對象的引用計數變為1。如果引用被刪除,對象的引用計數為0, 那么該對象就可以被垃圾回收。不過如果出現循環引用的話,引用計數機制就不再起有效的作用了 2. 標記清除 如果兩個對象的引用計數都為1,但是僅僅存在他們之間的循環引用,那么這兩個對象都是需要被 回收的,也就是說,它們的引用計數雖然表現為非0,但實際上有效的引用計數為0。所以先將循環引 用摘掉,就會得出這兩個對象的有效計數。 3. 分代回收 從前面“標記-清除”這樣的垃圾收集機制來看,這種垃圾收集機制所帶來的額外操作實際上與系統 中總的內存塊的數量是相關的,當需要回收的內存塊越多時,垃圾檢測帶來的額外操作就越多,而垃圾 回收帶來的額外操作就越少;反之,當需回收的內存塊越少時,垃圾檢測就將比垃圾回收帶來更少的額 外操作。 舉個例子: 當某些內存塊M 經過了3 次垃圾收集的清洗之后還存活時,我們就將內存塊M 划到一個集合 A 中去,而新分配的內存都划分到集合B 中去。當垃圾收集開始工作時,大多數情況都只對集合B 進 行垃圾回收,而對集合A 進行垃圾回收要隔相當長一段時間后才進行,這就使得垃圾收集機制需要處 理的內存少了,效率自然就提高了。在這個過程中,集合B 中的某些內存塊由於存活時間長而會被轉 移到集合A 中,當然,集合A 中實際上也存在一些垃圾,這些垃圾的回收會因為這種分代的機制而 被延遲。 內存池: 1. Python 的內存機制呈現金字塔形狀,-1,-2 層主要有操作系統進行操作; 2. 第0 層是C 中的malloc,free 等內存分配和釋放函數進行操作; 3. 第1 層和第2 層是內存池,有Python 的接口函數PyMem_Malloc 函數實現,當對象小於 256K 時有該層直接分配內存; 4. 第3 層是最上層,也就是我們對Python 對象的直接操作; Python 在運行期間會大量地執行malloc 和free 的操作,頻繁地在用戶態和核心態之間進行切 換,這將嚴重影響Python 的執行效率。為了加速Python 的執行效率,Python 引入了一個內存池 機制,用於管理對小塊內存的申請和釋放。 Python 內部默認的小塊內存與大塊內存的分界點定在256 個字節,當申請的內存小於256 字節 時,PyObject_Malloc 會在內存池中申請內存;當申請的內存大於256 字節時,PyObject_Malloc 的 行為將蛻化為malloc 的行為。當然,通過修改Python 源代碼,我們可以改變這個默認值,從而改 變Python 的默認內存管理行為。 調優手段(了解) 1.手動垃圾回收 2.調高垃圾回收閾值 3.避免循環引用(手動解循環引用和使用弱引用)
- 內存泄露是什么?如何避免?
指由於疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。內存泄漏並非指內存在物理上的 消失,而是應用程序分配某段內存后,由於設計錯誤,失去了對該段內存的控制,因而造成了內存的浪 費。導致程序運行速度減慢甚至系統崩潰等嚴重后果。 有__del__() 函數的對象間的循環引用是導致內存泄漏的主凶。 不使用一個對象時使用:del object 來刪除一個對象的引用計數就可以有效防止內存泄漏問題。 通過Python 擴展模塊gc 來查看不能回收的對象的詳細信息。 可以通過sys.getrefcount(obj) 來獲取對象的引用計數,並根據返回值是否為0 來判斷是否內存 泄漏。
函數
- 函數參數
Python 函數調用的時候參數的傳遞方式是值傳遞還是引用傳遞? Python 的參數傳遞有:位置參數、默認參數、可變參數、關鍵字參數。 函數的傳值到底是值傳遞還是引用傳遞,要分情況: 不可變參數用值傳遞: 像整數和字符串這樣的不可變對象,是通過拷貝進行傳遞的,因為你無論如何都不可能在原處改變 不可變對象 可變參數是引用傳遞的: 比如像列表,字典這樣的對象是通過引用傳遞、和C 語言里面的用指針傳遞數組很相似,可變對象 能在函數內部改變。
- 對缺省參數的理解?
缺省參數指在調用函數的時候沒有傳入參數的情況下,調用默認的參數,在調用函數的同時賦值時, 所傳入的參數會替代默認參數。 *args 是不定長參數,他可以表示輸入參數是不確定的,可以是任意多個。 **kwargs 是關鍵字參數,賦值的時候是以鍵= 值的方式,參數是可以任意多對在定義函數的時候 不確定會有多少參數會傳入時,就可以使用兩個參數。
- 為什么函數名字可以當做參數用?
Python 中一切皆對象,函數名是函數在內存中的空間,也是一個對象。 - Python 中pass 語句的作用是什么?
在編寫代碼時只寫框架思路,具體實現還未編寫就可以用pass 進行占位,使程序不報錯,不會進
行任何操作。 - 有這樣一段代碼,print c 會輸出什么,為什么?
1. a = 10 2. b = 20 3. c = [a] 4. a = 15 print c 答:10 對於字符串、數字,傳遞是相應的值。 數據聯動性和垃圾回收機制
內建函數
- map 函數和reduce 函數?
①從參數方面來講: map()包含兩個參數,第一個參數是一個函數,第二個是序列(列表或元組)。其中,函數(即map 的第一個參數位置的函數)可以接收一個或多個參數。 reduce()第一個參數是函數,第二個是序列(列表或元組)。但是,其函數必須接收兩個參數。 ②從對傳進去的數值作用來講: map()是將傳入的函數依次作用到序列的每個元素,每個元素都是獨自被函數“作用”一次。 reduce()是將傳人的函數作用在序列的第一個元素得到結果后,把這個結果繼續與下一個元素作用 (累積計算)。
- 遞歸函數停止的條件?
遞歸的終止條件一般定義在遞歸函數內部,在遞歸調用前要做一個條件判斷,根據判斷的結果選擇 是繼續調用自身,還是return;返回終止遞歸。 終止的條件: 1. 判斷遞歸的次數是否達到某一限定值 2. 判斷運算的結果是否達到某個范圍等,根據設計的目的來選擇
- 回調函數,如何通信的?
回調函數是把函數的指針(地址)作為參數傳遞給另一個函數,將整個函數當作一個對象,賦值給調
用的函數。 - Python 主要的內置數據類型都有哪些? print dir( ‘a ’) 的輸出?
內建類型:布爾類型、數字、字符串、列表、元組、字典、集合;
輸出字符串‘a’的內建方法; - print(list(map(lambda x: x * x, [y for y in range(3)])))?
[0, 1, 4] - hasattr() getattr() setattr() 函數使用詳解?
hasattr(object, name)函數: 判斷一個對象里面是否有name 屬性或者name 方法,返回bool 值,有name 屬性(方法)返回True, 否則返回False。 注意:name 要使用引號括起來。 1. class function_demo(object): 2. name = 'demo' 3. def run(self): 4. return "hello function" 5. functiondemo = function_demo() 6. res = hasattr(functiondemo, 'name') #判斷對象是否有name 屬性,True 7. res = hasattr(functiondemo, "run") #判斷對象是否有run 方法,True 8. res = hasattr(functiondemo, "age") #判斷對象是否有age 屬性,Falsw 9. print(res) getattr(object, name[,default]) 函數: 獲取對象object 的屬性或者方法,如果存在則打印出來,如果不存在,打印默認值,默認值可選。 注意:如果返回的是對象的方法,則打印結果是:方法的內存地址,如果需要運行這個方法,可以在后 面添加括號()。 1. functiondemo = function_demo() 2. getattr(functiondemo, 'name') #獲取name 屬性,存在就打印出來--- demo 3. getattr(functiondemo, "run") #獲取run 方法,存在打印出方法的內存地址---<bound method function_demo.run of <__main__.function_demo object at 0x10244f320>> 4. getattr(functiondemo, "age") #獲取不存在的屬性,報錯如下: 5. Traceback (most recent call last): 6. File "/Users/liuhuiling/Desktop/MT_code/OpAPIDemo/conf/OPCommUtil.py", line 39, in <module> 7. res = getattr(functiondemo, "age") 8. AttributeError: 'function_demo' object has no attribute 'age' 9. getattr(functiondemo, "age", 18) #獲取不存在的屬性,返回一個默認值 setattr(object,name,values)函數: 給對象的屬性賦值,若屬性不存在,先創建再賦值 1.class function_demo(object): 2. name = 'demo' 3. def run(self): 4. return "hello function" 5.functiondemo = function_demo() 6.res = hasattr(functiondemo, 'age') # 判斷age 屬性是否存在,False 7.print(res) 8.setattr(functiondemo, 'age', 18 ) #對age 屬性進行賦值,無返回值 9.res1 = hasattr(functiondemo, 'age') #再次判斷屬性是否存在,True 綜合使用: 1.class function_demo(object): 2. name = 'demo' 3. def run(self): 4. return "hello function" 5.functiondemo = function_demo() 6.res = hasattr(functiondemo, 'addr') # 先判斷是否存在if res: 7. addr = getattr(functiondemo, 'addr') 8. print(addr)else: 9. addr = getattr(functiondemo, 'addr', setattr(functiondemo, 'addr', '北京首都')) 10. #addr = getattr(functiondemo, 'addr', '美國紐約') 11. print(addr)
- 一句話解決階乘函數?
在Python2 中:
1.reduce(lambda x,y: x*y, range(1,n+1))
注意:Python3 中取消了該函數。
lambda
- 什么是lambda 函數? 有什么好處?
lambda 函數是一個可以接收任意多個參數(包括可選參數)並且返回單個表達式值的函數
1、lambda 函數比較輕便,即用即仍,很適合需要完成一項功能,但是此功能只在此一處使用,
連名字都很隨意的情況下;
2、匿名函數,一般用來給filter, map 這樣的函數式編程服務;
3、作為回調函數,傳遞給某些應用,比如消息處理 - 下面這段代碼的輸出結果將是什么?請解釋
1. def multipliers(): return [lambda x : i * x for i in range(4)] print [m(2) for m in multipliers()] 上面代碼輸出的結果是[6, 6, 6, 6] (不是我們想的[0, 2, 4, 6])。 你如何修改上面的multipliers 的定義產生想要的結果? 上述問題產生的原因是Python 閉包的延遲綁定。這意味着內部函數被調用時,參數的值在閉包內 進行查找。因此,當任何由multipliers()返回的函數被調用時,i 的值將在附近的范圍進行查找。那時, 不管返回的函數是否被調用,for 循環已經完成,i 被賦予了最終的值3。
下面是解決這一問題的一些方法。
一種解決方法就是用Python 生成器。
1. def multipliers():
2. for i in range(4): yield lambda x : i * x
另外一個解決方案就是創造一個閉包,利用默認函數立即綁定。
1. def multipliers():
2. return [lambda x, i=i : i * x for i in range(4)] - 什么是lambda 函數?它有什么好處?寫一個匿名函數求兩個數的和?
1. f = lambda x,y:x+y
2. print(f(2017,2018))
裝飾器
- 對裝飾器的理解
裝飾器本質上是一個Python 函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外
功能,裝飾器的返回值也是一個函數對象。1. import time 2. def timeit(func): 3. def wrapper(): 4. start = time.clock() 5. func() end =time.clock() 6. print 'used:', end - start 7. return wrapper 8. @timeit 9. def foo(): 10. print 'in foo()'foo()
- 什么是閉包?
在函數內部再定義一個函數,並且這個函數用到了外邊函數的變量,那么將這個函數以及用到的一些變量稱之為 閉包。
- 函數裝飾器有什么作用?
裝飾器本質上是一個Python 函數,它可以在讓其他函數在不需要做任何代碼的變動的前提下增加額外的功能。裝 飾器的返回值也是一個函數的對象,它經常用於有切面需求的場景。比如:插入日志、性能測試、事務處理、緩存、 權限的校驗等場景有了裝飾器就可以抽離出大量的與函數功能本身無關的雷同代碼並發並繼續使用。
- 生成器
生成器、迭代器的區別? 迭代器是一個更抽象的概念,任何對象,如果它的類有next 方法和iter 方法返回自己本身,對於string、list、 dict、tuple 等這類容器對象,使用for 循環遍歷是很方便的。在后台for 語句對容器對象調用iter()函數,iter() 是python 的內置函數。iter()會返回一個定義了next()方法的迭代器對象,它在容器中逐個訪問容器內元素,next() 也是python 的內置函數。在沒有后續元素時,next()會拋出一個StopIteration 異常。 生成器(Generator)是創建迭代器的簡單而強大的工具。它們寫起來就像是正規的函數,只是在需要返回數 據的時候使用yield 語句。每次next()被調用時,生成器會返回它脫離的位置(它記憶語句最后一次執行的位置 和所有的數據值) 區別:生成器能做到迭代器能做的所有事,而且因為自動創建了iter()和next()方法,生成器顯得特別簡潔,而且 生成器也是高效的,使用生成器表達式取代列表解析可以同時節省內存。除了創建和保存程序狀態的自動方法,當 發生器終結時,還會自動拋出StopIteration 異常。
-
X 是什么類型? X = (for i in ramg(10)) 答:X 是generator 類型。 請嘗試用“一行代碼”實現將1-N 的整數列表以3 為單位分組,比如1-100 分組后為? print([[x for x in range(1,100)][i:i+3] for i in range(0,len(list_a),3)])
- python中yield的用法?
yield 就是保存當前程序執行狀態。你用for 循環的時候,每次取一個元素的時候就會計算一次。用yield 的函數 叫generator,和iterator 一樣,它的好處是不用一次計算所有元素,而是用一次算一次,可以節省很多空間。generator 每次計算需要上一次計算結果,所以用yield,否則一return,上次計算結果就沒了。 1. >>> def createGenerator(): 2. ... mylist = range(3) 3. ... for i in mylist: 4. ... yield i*i 5. ... 6. >>> mygenerator = createGenerator() # create a generator 7. >>> print(mygenerator) # mygenerator is an object! 8. <generator object createGenerator at 0xb7555c34> 9. >>> for i in mygenerator: 10. ... print(i) 11. 0 12. 1 13. 4