Python面試題_中級版


Python 面試題

1.Python是如何進行內存管理的

對象引用機制、垃圾回收機制、內存池機制
1.1對象引用機制
Python內部使用引用計數,來保持追蹤內存中的對象,所有對象都有引用計數。
引用計數增加的情況:
1,一個對象分配一個新名稱
2,將其放入一個容器中(如列表、元組或字典)
引用計數減少的情況:
1,使用del語句對對象別名顯示的銷毀
2,引用超出作用域或被重新賦值
sys.getrefcount( )函數可以獲得對象的當前引用計數
多數情況下,引用計數比你猜測得要大得多。對於不可變數據(如數字和字符串),解釋器會在程序的不同部分共享內存,以便節約內存。
二、垃圾回收
1,當一個對象的引用計數歸零時,它將被垃圾收集機制處理掉。
2,當兩個對象a和b相互引用時,del語句可以減少a和b的引用計數,並銷毀用於引用底層對象的名稱。然而由於每個對象都包含一個對其他對象的應用,因此引用計數不會歸零,對象也不會銷毀。(從而導致內存泄露)。為解決這一問題,解釋器會定期執行一個循環檢測器,搜索不可訪問對象的循環並刪除它們。
三、內存池機制
Python提供了對內存的垃圾收集機制,但是它將不用的內存放到內存池而不是返回給操作系統。
1,Pymalloc機制。為了加速Python的執行效率,Python引入了一個內存池機制,用於管理對小塊內存的申請和釋放。
2,Python中所有小於256個字節的對象都使用pymalloc實現的分配器,而大的對象則使用系統的malloc。
3,對於Python對象,如整數,浮點數和List,都有其獨立的私有內存池,對象間不共享他們的內存池。也就是說如果你分配又釋放了大量的整數,用於緩存這些整數的內存就不能再分配給浮點數。

<!-- Python垃圾回收機制 -->
Python GC主要使用引用計數(reference counting)來跟蹤和回收垃圾。在引用計數的基礎上,通過“標記-清除”(mark and sweep)解決容器對象可能產生的循環引用問題,通過“分代回收”(generation collection)以空間換時間的方法提高垃圾回收效率。
1 引用計數
PyObject是每個對象必有的內容,其中ob_refcnt就是做為引用計數。當一個對象有新的引用時,它的ob_refcnt就會增加,當引用它的對象被刪除,它的ob_refcnt就會減少.引用計數為0時,該對象生命就結束了。
優點:
1.簡單
2.實時性
缺點:
1.維護引用計數消耗資源
2.循環引用
2 標記-清除機制
基本思路是先按需分配,等到沒有空閑內存的時候從寄存器和程序棧上的引用出發,遍歷以對象為節點、以引用為邊構成的圖,把所有可以訪問到的對象打上標記,然后清掃一遍內存空間,把所有沒標記的對象釋放。
3 分代技術
分代回收的整體思想是:將系統中的所有內存塊根據其存活時間划分為不同的集合,每個集合就成為一個“代”,垃圾收集頻率隨着“代”的存活時間的增大而減小,存活時間通常利用經過幾次垃圾回收來度量。
Python默認定義了三代對象集合,索引數越大,對象存活時間越長。
舉例:
當某些內存塊M經過了3次垃圾收集的清洗之后還存活時,我們就將內存塊M划到一個集合A中去,而新分配的內存都划分到集合B中去。當垃圾收集開始工作時,大多數情況都只對集合B進行垃圾回收,而對集合A進行垃圾回收要隔相當長一段時間后才進行,這就使得垃圾收集機制需要處理的內存少了,效率自然就提高了。在這個過程中,集合B中的某些內存塊由於存活時間長而會被轉移到集合A中,當然,集合A中實際上也存在一些垃圾,這些垃圾的回收會因為這種分代的機制而被延遲。

2.什么是lambda函數?它有什么好處?

lambda 表達式,通常是在需要一個函數,但是又不想費神去命名一個函數的場合下使用,也就是指匿名函數
lambda函數:首要用途是指點短小的回調函數
lambda [arguments]:expression
a=lambda x,y:x+y
print(a(2,5))

3.Python里面如何拷貝一個對象?(賦值,淺拷貝,深拷貝的區別)

一般來說可以使用copy.copy()方法或者copy.deepcopy()方法,幾乎所有的對象都可以被拷貝;Python2可以直接用,3需要引入copy模塊

賦值(=),就是創建了對象的一個新的引用,修改其中任意一個變量都會影響到另一個。
淺拷貝:創建一個新的對象,但它包含的是對原始對象中包含項的引用(如果用引用的方式修改其中一個對象,另外一個也會修改改變){1,完全切片方法;2,工廠函數,如list();3,copy模塊的copy()函數}
深拷貝:創建一個新的對象,並且遞歸的復制它所包含的對象(修改其中一個,另外一個不會改變){copy模塊的deep.deepcopy()函數}

4.Python里面match()和search()的區別?

match()函數只檢測RE是不是在string的開始位置匹配,只有在0位置匹配成功的話才有返回,如果不是開始位置匹配成功的話,match()就返回none
search()會掃描整個string查找匹配

>>> import re
>>> re.match('super','superrestion').span() #match必須從頭開始匹配
(0, 5)
>>> re.search('super','dsasuperds').span()  #search不必從頭開始
(3, 8)

5.Python如何實現單例模式?

# # 一般的類可以通過實例化創建多個實例,如下
class Earth:
    pass

a = Earth()
print(id(a))  # 2700727126840
b = Earth()  
print(id(b))  # 2700729163168

# 單例模式只能實例化一個對象
class Earth(object):  
    __instance=None #定義一個類屬性做判斷
    def __new__(cls):
        if cls.__instance==None:
            #如果__instance為空證明是第一次創建實例
            #通過父類的__new__(cls)創建實例
            cls.__instance==object.__new__(cls)
            return  cls.__instance
        else:
            #返回上一個對象的引用
            return cls.__instance

a = Earth()
print(id(a))  #1479054384
b = Earth()
print(id(b))  #1479054384

#-------------------------------------------------------------
1 使用__new__方法
class Singleton(object):
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            orig = super(Singleton, cls)
            cls._instance = orig.__new__(cls, *args, **kw)
        return cls._instance
class MyClass(Singleton):
    a = 1
2 共享屬性
創建實例時把所有實例的__dict__指向同一個字典,這樣它們具有相同的屬性和方法.
class Borg(object):
    _state = {}
    def __new__(cls, *args, **kw):
        ob = super(Borg, cls).__new__(cls, *args, **kw)
        ob.__dict__ = cls._state
        return ob
class MyClass2(Borg):
    a = 1
3 裝飾器版本
def singleton(cls, *args, **kw):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]
    return getinstance
@singleton
class MyClass:
  ...
4 import方法
作為python的模塊是天然的單例模式

# mysingleton.py
class My_Singleton(object):
    def foo(self):
        pass

my_singleton = My_Singleton()
# to use
from mysingleton import my_singleton
my_singleton.foo()

6.如何反序的迭代一個序列?

注:就是翻轉一個列表的元素
如果是一個list, 最快的解決方案是:
list.reverse()
try:
for x in list:
“do something with x”
finally:
list.reverse()
如果不是list, 最通用但是稍慢的解決方案是:
for i in range(len(sequence)-1, -1, -1):
x = sequence[i]

7.什么是Python的自省

面向對象的語言能通過方法/函數獲取對象類型
type() dir() getatte() hasattr() isinstance()

8.Python的函數參數傳遞

9.如何用Python來發送郵件?

10.Python字典推導式

d = {key: value for (key, value) in iterable}

11.Python中單下划線和雙下划線

>>> class MyClass():
...     def __init__(self):
...             self.__superprivate = "Hello"
...             self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}
__foo__:一種約定,Python內部的名字,用來區別其他用戶自定義的命名,以防沖突.
_foo:一種約定,用來指定變量私有.程序員用來指定私有變量的一種方式.
__foo:這個有真正的意義:解析器用_classname__foo來代替這個名字,以區別和其他類相同的命名.

12. 字符串格式化:%和.format

"hi there %s" % name    #一個%s 可以傳入一個變量
"hi there %s" % (name,) #一個%s 可以傳入多個變量

.format是通過{}和:來代替%。format函數可以接受不限個參數,位置可以不按順序,例:a = '{}{}'.format('hello','world')
    print(a)  # helloworld
    a = '{1}{0}{1}'.format('hello','world')
    print(a)    # worldhelloworld

13.舉例說明迭代器與生成器

http://taizilongxu.gitbooks.io/stackoverflow-about-python/content/1/README.html

14.面向切面編程AOP和裝飾器

裝飾器是一個很著名的設計模式,經常被用於有切面需求的場景,較為經典的有插入日志、性能測試、事務處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量函數中與函數功能本身無關的雷同代碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能。

15.描述下鴨子類型

“當看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子。”
我們並不關心對象是什么類型,到底是不是鴨子,只關心行為。
比如在python中,有很多file-like的東西,比如StringIO,GzipFile,socket。它們有很多相同的方法,我們把它們當作文件使用。
又比如list.extend()方法中,我們並不關心它的參數是不是list,只要它是可迭代的,所以它的參數可以是list/tuple/dict/字符串/生成器等.
鴨子類型在動態語言中經常使用,非常靈活,使得python不想java那樣專門去弄一大堆的設計模式。

16.Python中重載

函數重載主要是為了解決兩個問題。
1.可變參數類型。
2.可變參數個數。
另外,一個基本的設計原則是,僅僅當兩個函數除了參數類型和參數個數不同以外,其功能是完全相同的,此時才使用函數重載,如果兩個函數的功能其實不同,那么不應當使用重載,而應當使用一個名字不同的函數。
好吧,那么對於情況 1 ,函數功能相同,但是參數類型不同,python 如何處理?答案是根本不需要處理,因為 python 可以接受任何類型的參數,如果函數的功能相同,那么不同的參數類型在 python 中很可能是相同的代碼,沒有必要做成兩個不同函數。
那么對於情況 2 ,函數功能相同,但參數個數不同,python 如何處理?大家知道,答案就是缺省參數。對那些缺少的參數設定為缺省參數即可解決問題。因為你假設函數功能相同,那么那些缺少的參數終歸是需要用的。
好了,鑒於情況 1 跟 情況 2 都有了解決方案,python 自然就不需要函數重載了。
http://www.zhihu.com/question/20053359

17.新式類和舊式類

http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html

18.__new__和__init__的區別

1.__new__是一個靜態方法,而__init__是一個實例方法.
2.__new__方法會返回一個創建的實例,而__init__什么都不返回.
3.只有在__new__返回一個cls的實例時后面的__init__才能被調用.
4.當創建一個新實例時調用__new__,初始化一個實例時用__init__.
__metaclass__是創建類時起作用.所以我們可以分別使用__metaclass__,__new__和__init__來分別在類創建,實例創建和實例初始化的時候做一些小手腳.

19.Python中的作用域

Python 中,一個變量的作用域總是由在代碼中被賦值的地方所決定的。
當 Python 遇到一個變量的話他會按照這樣的順序進行搜索:
本地作用域(Local)→當前作用域被嵌入的本地作用域(Enclosing locals)→全局/模塊作用域(Global)→內置作用域(Built-in)

20.Python的協程是什么

簡單點說協程是進程和線程的升級版,進程和線程都面臨着內核態和用戶態的切換問題而耗費許多切換時間,而協程就是用戶自己控制切換的時機,不再需要陷入系統的內核態.
Python里最常見的yield就是協程的思想.

21.閉包

閉包(closure)是函數式編程的重要的語法結構。閉包也是一種組織代碼的結構,它同樣提高了代碼的可重復使用性。
當一個內嵌函數引用其外部作作用域的變量,我們就會得到一個閉包. 總結一下,創建一個閉包必須滿足以下幾點:
1.必須有一個內嵌函數
2.內嵌函數必須引用外部函數中的變量
3.外部函數的返回值必須是內嵌函數
感覺閉包還是有難度的,幾句話是說不明白的,還是查查相關資料.
重點是函數運行后並不會被撤銷,就像16題的instance字典一樣,當函數運行完后,instance並不被銷毀,而是繼續留在內存空間里.這個功能類似類里的類變量,只不過遷移到了函數上.
閉包就像個空心球一樣,你知道外面和里面,但你不知道中間是什么樣.

22.Python函數式編程

python中函數式編程支持:
filter 函數的功能相當於過濾器。調用一個布爾函數bool_func來迭代遍歷每個seq中的元素;返回一個使bool_seq返回值為true的元素的序列。

>>>a = [1,2,3,4,5,6,7]
>>>b = filter(lambda x: x > 5, a)
>>>print b
>>>[6,7]
map函數是對一個序列的每個項依次執行函數,下面是對一個序列每個項都乘以2:
>>> a = map(lambda x:x*2,[1,2,3])
>>> list(a)
[2, 4, 6]
reduce函數是對一個序列的每個項迭代調用函數,下面是求3的階乘:
>>> reduce(lambda x,y:x*y,range(1,4))
26

23.Python面向對象

代碼的本質是控制計算機完成特定任務的指令集,即只要符合語法規則,指令集寫在一個文件里或是通過一定方式組合都能順利執行,簡單的程序也就是少量的代碼片段的組合,但是隨着工程復雜度的提    
高,需要將有類似功能的代碼歸類封裝成為一個對象,每個對象有獨立的輸入輸出和完成特定操作的能力,開發者通過編寫、組合這些對象來完成特定任務的思維是面向對象思想;這樣能提高代碼的可讀 
性、減少冗余,提高可維護性;典型的面向對象的語言有Python、java、ruby、go語言


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM