python近期遇到的一些面試問題(二)


1. 解釋什么是棧溢出,在什么情況下可能出現。

棧溢出是由於C語言系列沒有內置檢查機制來確保復制到緩沖區的數據不得大於緩沖區的大小,因此當這個數據足夠大的時候,將會溢出緩沖區的范圍。
在Python中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出。

以上內容來自百度百科。https://baike.baidu.com/item/%E6%A0%88%E6%BA%A2%E5%87%BA/8538051?fr=aladdin

棧溢出的幾種情況:

局部數組過大,當函數內部的數組過大時,有可能導致堆棧溢出。
遞歸調用層次太多。遞歸函數在運行時會執行壓棧操作,當壓棧次數太多時,也會導致堆棧溢出。
指針或數組越界。這種情況最常見,例如進行字符串拷貝,或處理用戶輸入等等。

以上內容來自,https://blog.csdn.net/u010590166/article/details/22294291/

2.簡述Cpython的內存管理機制

Python和其他高級編程語言,如Java、Ruby或JavaScript等一樣,有自動內存管理機制。所以許多程序開發人員沒有過多地關注內存管理,但是這可能會導致更多的內存開銷和內存泄漏。

引用計數

每一個Python對象都有一個引用計數器----用於記錄有多少其他對象指向(引用)這個對象。它存儲在變量 refcnt 中,並通過調用C宏Py_INCREF實現引用計數增加和Py_DECREF實現引用計數減少的操作。 Py_DECREF更復雜點,當引用計數器到零時,它會運行該對象的釋放函數,回收該類型的對象。

通常以下兩種情況你需要考慮這個宏定義:實現自己創建數據結構,或者修改已經存在的Python C API。如果你使用Python內置的數據結構,那么不需要任何操作。

如果想不增加引用計數,可以使用弱引用或 weakrefs 引用對象。 Weakrefs對於實現緩存和代理非常有用。

垃圾回收(GC)

引用計數是在Python 2.0之前管理對象生命周期的唯一方法。它有一個弱點,它不能刪除循環引用的對象。 循環引用的最簡單的例子是對象引用自身。

通常情況下,可以避免使用循環引用對象,但是有時是不可避免的(例如:長時間運行的程序)。

為了解決這個問題,Python 2.0引入了新的垃圾回收機制。 新GC與其他語言運行時(如JVM和CLR)的GC的主要區別在於,它僅用於尋找存在引用計數的循環引用。

循環引用只能由容器對象創建,因此Python GC不會跟蹤整數,字符串等類型。

GC將對象分為3代,每一代對象都有一個計數器和一個閾值。當對象被創建時,閾值會被自動地指派為0,也就是第0代對象。當計數器大於某個閥值,GC就會運行在當前對象代上,回收該對象。沒被回收的對象會被移至下一代,並且將相應的計數器復位。下一代的對象保留在下一代。

在Python 3.4之前,GC有一個致命缺點----每個對象重載了del__()方法,因為每個對象都可以相互引用,所以GC不知道該調用那個對象的__del()方法,這會導致GC直接跳過這些對象。具體詳細信息可以參考 gc.garbage並且循環引用需要編程人員手動打破。

Python3.4介紹了一種最終的解決方法finalization approach ,現在的GC可以打破對象的循環引用,而不在使用gc.garbage介紹的方法去回收對象。

此外,值得一提的是,如果你確定你的代碼沒有創建循環引用(或者你不關心內存管理),那么你可以只依賴引用計數器自動管理內存,而不使用GC去管理內存。

以上內容來自:,https://python.freelycode.com/contribution/detail/511
英文原文:https://medium.com/@nvdv/cpython-memory-management-479e6cd86c9#.sbvb0py87

3.請列舉你知道的Python的魔法方法及用途。

__init__

構造器,當一個實例被創建的時候調用的初始化方法

__new__

__new__ 是在一個對象實例化的時候所調用的第一個方法

它的第一個參數是這個類,其他的參數是用來直接傳遞給 ####__init__ 方法

__new__ 決定是否要使用該 ####__init__ 方法,因為 ####__new__ 可以調用其他類的構造方法或者直接返回別的實例對象來作為本類的實例,如果 ####__new__ 沒有返回實例對象,則 ####__init__ 不會被調用

__new__ 主要是用於繼承一個不可變的類型比如一個 tuple 或者 string

__call__

允許一個類的實例像函數一樣被調用

__del__

析構器,當一個實例被銷毀的時候調用的方法

__len__(self): 定義當被 len() 調用時的行為

__repr__(self): 定義當被 repr() 調用時的行為

__str__(self): 定義當被 str() 調用時的行為

__bytes__(self):定義當被 bytes() 調用時的行為

__hash__(self): 定義當被 hash() 調用時的行為

__bool__(self): 定義當被 bool() 調用時的行為,應該返回 True 或 False

4. 已知以下list:

list1 = [
{
"mm": 2,
},{
"mm": 1,
},{
"mm": 4,
},{
"mm": 3,
},{
"mm": 3,
}
]

4.1 把list1中的元素按mm的值排序。

首先說函數sorted的具體用法:

(1).Python2.x:sorted(iterable, cmp=None, key=None, reverse=False)
,Python3.x:sorted(iterable, /, *, key=None, reverse=False),Python3.x和Python2.x的sorted函數有點不太一樣,少了cmp參數。
key接受一個函數,這個函數只接受一個元素,默認為None
reverse是一個布爾值。如果設置為True,列表元素將被倒序排列,默認為False
着重介紹key的作用原理:
key指定一個接收一個參數的函數,這個函數用於從每個元素中提取一個用於比較的關鍵字。默認值為None 。

(2).sorted() 函數對所有可迭代的對象進行排序操作。
(3).對於 元組類型的列表比如

list2=[('b',4),('a',0),('c',2),('d',3)]

排序的方法是使用lambda然后獲取需要排的元素的下標即可。

print(sorted(list2,key=lambda x:x[0]))

而本題有點麻煩 里面是字典類型所以我們需要通過以下方式獲取

答案 :

sorted(list1,key=lambda x:x['mm']))

或者用 operator 函數來加快速度, 上面排序等價於

from operator import itemgetter
print(sorted(list1,key=itemgetter('mm')))

4.2 獲取list1中第一個mm值等於x的元素。

x=1
for index,item in enumerate(list1):
    if x in item.values():
        print(list1[index])
        break

4.3 刪除list1中所有mm等於x的元素,且不對list重新賦值。

x=3
for item in list1[:]:
    if x in item.values():
        list1.remove(item)
print(list1)

4.4 取出list1中mm最大的元素,不能排序。

max_num=0
for item in list1[:]:
  if item['mm']>max_num:
    max_num=item['mm']
print(max_num)

5. 以下操作的時間復雜度是多少?

5.1 list.index 時間復雜度:
5.2 dict.get 時間復雜度:
5.3 x in set(…..) 時間復雜度:

首先了解什么是時間復雜度:
時間復雜度是指執行算法所需要的計算工作量
如果一個算法的執行次數是 T(n),那么只保留最高次項,同時忽略最高項的系數后得到函數 f(n),此時算法的時間復雜度就是 O(f(n))。
大概是這個意思,取n的最大次冪,去除低級次冪和常數以及系數項


簡單的理解的話比如for循環,對於一個循環,假設循環體的時間復雜度為 O(n)

 

for i in range(1,1000):
    print(i)

2個循環就是 O(n^2)

for i in range(1,1000):
   for j in rang(1,1000):
       print(i*j)

依次類推
再舉一個例子

n = 64
while n > 1:
    print(n)
    n = n // 2

這個 n = n // 2,每次循環之后都會減半,時間復雜度為O(log2n),而常數項往往可以省略所以這個時間復雜度是O(logn)。
常見python內置方法的時間復雜度參考:https://www.cnblogs.com/harvey888/p/6659061.html

5.1 list.index 時間復雜度:

列表是以數組(Array)實現的。最大的開銷發生在超過當前分配大小的增長,這種情況下所有元素都需要移動;或者是在起始位置附近插入或者刪除元素,這種情況下所有在該位置后面的元素都需要移動。
點擊這里查看列表的內部實現原理http://python.jobbole.com/82549/

答案:

O(1)

5.2 dict.get 時間復雜度:

首先先看dict的內部實現原理:
http://python.jobbole.com/85040/

答案:

平均復雜度:O(1)    最壞復雜度:O(n)

5.3 x in set(…..) 時間復雜度:

答案:

平均復雜度:O(1)    最壞復雜度:O(n)

6. 解釋以下輸出的原因:

In [1]: '{:0.2f}'.format(0.135)
Out[1]: '0.14'

In [2]: '{:0.2f}'.format(0.145)
Out[2]: '0.14'

答案:

官方文檔:6.1. string - Common string operations - Python 3.4.8 documentation
https://docs.python.org/3.4/library/string.html
涉及知識點:Fixed Point 定點數表示法然后看看這兩個數的定點數表示法
3.145:3.1450000000000000177635683940
3.135:3.1349999999999997868371792719699442386627197265625
所以就是一個簡單的四舍五入:
3.1349顯然舍了4
3.1450顯然入了5

參考鏈接:https://www.zhihu.com/question/270543447/answer/355068323

7 簡述代碼跑出以下異常的原因是什么:

IndexError 序列中沒有此索引(index)
AttributeError 對象沒有這個屬性
AssertionError 斷言語句失敗
NotImplementedError 尚未實現的方法
StopIteration 迭代器沒有更多的值
TypeError 對類型無效的操作
IndentationError 縮進錯誤

8. 簡述你對GIL的理解

GIL的全稱是Global Interpreter Lock(全局解釋器鎖),來源是python設計之初的考慮,為了數據安全所做的決定。
每個CPU在同一時間只能執行一個線程
在單核CPU下的多線程其實都只是並發,不是並行,並發和並行從宏觀上來講都是同時處理多路請求的概念。
但並發和並行又有區別,並行是指兩個或者多個事件在同一時刻發生;而並發是指兩個或多個事件在同一時間間隔內發生。

在Python多線程下,每個線程的執行方式:
1、獲取GIL
2、執行代碼直到sleep或者是python虛擬機將其掛起。
3、釋放GIL
可見,某個線程想要執行,必須先拿到GIL,我們可以把GIL看作是“通行證”,並且在一個python進程中,GIL只有一個。
拿不到通行證的線程,就不允許進入CPU執行。
在Python2.x里,GIL的釋放邏輯是當前線程遇見IO操作或者ticks計數達到100(ticks可以看作是Python自身的一個計數器,
專門做用於GIL,每次釋放后歸零,這個計數可以通過 sys.setcheckinterval 來調整),進行釋放。
而每次釋放GIL鎖,線程進行鎖競爭、切換線程,會消耗資源。並且由於GIL鎖存在,
python里一個進程永遠只能同時執行一個線程(拿到GIL的線程才能執行)。

IO密集型代碼(文件處理、網絡爬蟲等),多線程能夠有效提升效率(單線程下有IO操作會進行IO等待,
造成不必要的時間浪費,而開啟多線程能在線程A等待時,自動切換到線程B,
可以不浪費CPU的資源,從而能提升程序執行效率),所以多線程對IO密集型代碼比較友好。
更多參考資料:
http://cenalulu.github.io/python/gil-in-python/

9.簡述以下內置函數的用法

reduce
map
all
any

答案:

reduce:

在Python 3里,reduce()函數已經被從全局名字空間里移除了,它現在被放置在fucntools模塊里
用的話要 先引入
from functools import reduce
reduce的用法
reduce(function, sequence[, initial]) -> value

from functools import reduce 
reduce(lambda x,y: x+y, [123]) 
輸出 6 
reduce(lambda x, y: x+y, [1,2,3], 9
輸出 15 
reduce(lambda x,y: x+y, [123], 7
輸出 13 

map:

map()是 Python 內置的高階函數,它接收一個函數 f 和一個 list,並通過把函數 f 依次作用在 list 的每個元素上,得到一個新的 list 並返回。

例如,對於list [123456789]

如果希望把list的每個元素都作平方,就可以用map()函數:
因此,我們只需要傳入函數f(x)=x*x,就可以利用map()函數完成這個計算:
def f(x):
    return x*x
print map(f, [123456789])
輸出結果:
[149102536496481]
注意:map()函數不改變原有的 list,而是返回一個新的 list

all:

all(x)如果all(x)參數x對象的所有元素不為0、''、False或者x為空對象,則返回True,否則返回False
all([0, 1,2, 3]) #列表list,存在一個為0的元素 輸出Flase
all(('a', 'b', '', 'd')) #元組tuple,存在一個為空的元素 輸出Flase
all(('', '', '', '')) #元組tuple,全部為空的元素 輸出Flase
all([]) # 空列表 輸出True
all(()) # 空元組 輸出True
all('') # 空字串 輸出True

注意:空元組、空列表,空字串返回值為True,這里要特別注意

any:

any(x)判斷x對象是否為空對象,如果都為空、0、false,則返回false,如果不都為空、0、false,則返回true
any('123')
輸出True
any([0,1])
輸出True
any([0,'0',''])
輸出True
any([0,''])
輸出False
any([0,'','false'])
輸出True
any([0,'',bool('false')])
輸出True
any([0,'',False])
輸出False
any(('a','b','c'))
輸出True
any(('a','b',''))
輸出True
any((0,False,''))
輸出False
any([])
輸出False
any(())
輸出False

10.copy和deepcopy的區別是什么?

—–我們尋常意義的復制就是深復制,即將被復制對象完全再復制一遍作為獨立的新個體單獨存在。所以改變原有被復制對象不會對已經復制出來的新對象產生影響。
—–而淺復制並不會產生一個獨立的對象單獨存在,他只是將原有的數據塊打上一個新標簽,所以當其中一個標簽被改變的時候,數據塊就會發生變化,另一個標簽也會隨之改變。這就和我們尋常意義上的復制有所不同了。
對於簡單的 object,用 shallow copy 和 deep copy 沒區別
復雜的 object, 如 list 中套着 list 的情況,shallow copy 中的 子list,並未從原 object 真的「獨立」出來。也就是說,如果你改變原 object 的子 list 中的一個元素,你的 copy 就會跟着一起變。這跟我們直覺上對「復制」的理解不同。
代碼解釋

>>> import copy
>>> origin = [12, [34]]
#origin 里邊有三個元素:1, 2,[3, 4]
>>> cop1 = copy.copy(origin)
>>> cop2 = copy.deepcopy(origin)
>>> cop1 == cop2
True
>>> cop1 is cop2
False 
#cop1 和 cop2 看上去相同,但已不再是同一個object
>>> origin[2][0] = "hey!" 
>>> origin
[12, ['hey!'4]]
>>> cop1
[12, ['hey!'4]]
>>> cop2
[12, [34]]
#把origin內的子list [3, 4] 改掉了一個元素,觀察 cop1 和 cop2

該部分內容來自:https://blog.csdn.net/qq_32907349/article/details/52190796

11. 簡述多線程、多進程、協程之間的區別與聯系。

概念:

進程:

進程是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,
進程是系統進行資源分配和調度的一個獨立單位。每個進程都有自己的獨立內存空間,
不同進程通過進程間通信來通信。由於進程比較重量,占據獨立的內存,
所以上下文進程間的切換開銷(棧、寄存器、虛擬內存、文件句柄等)比較大,但相對比較穩定安全。

線程:

線程是進程的一個實體,是CPU調度和分派的基本單位,
它是比進程更小的能獨立運行的基本單位.
線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),
但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。
線程間通信主要通過共享內存,上下文切換很快,資源開銷較少,但相比進程不夠穩定容易丟失數據。

協程:

協程是一種用戶態的輕量級線程,協程的調度完全由用戶控制。
協程擁有自己的寄存器上下文和棧。
協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧,
直接操作棧則基本沒有內核切換的開銷,可以不加鎖的訪問全局變量,所以上下文的切換非常快。

區別:

進程與線程比較:

線程是指進程內的一個執行單元,也是進程內的可調度實體。線程與進程的區別:
1) 地址空間:線程是進程內的一個執行單元,進程內至少有一個線程,它們共享進程的地址空間,
而進程有自己獨立的地址空間

2) 資源擁有:進程是資源分配和擁有的單位,同一個進程內的線程共享進程的資源

3) 線程是處理器調度的基本單位,但進程不是

4) 二者均可並發執行

5) 每個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口,
但是線程不能夠獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制

協程與線程進行比較:

1) 一個線程可以多個協程,一個進程也可以單獨擁有多個協程,這樣python中則能使用多核CPU。

2) 線程進程都是同步機制,而協程則是異步

3) 協程能保留上一次調用時的狀態,每次過程重入時,就相當於進入上一次調用的狀態

12. 代碼中經常遇到的*args, **kwargs含義及用法。

在函數定義中使用args和*kwargs傳遞可變長參數
*args用來將參數打包成tuple給函數體調用
**kwargs 打包關鍵字參數成dict給函數體調用

13. 列舉一些你知道的HTTP Header 及其功能。

Accept

作用: 瀏覽器端可以接受的媒體類型,

例如: Accept: text/html 代表瀏覽器可以接受服務器回發的類型為 text/html 也就是我們常說的html文檔,

如果服務器無法返回text/html類型的數據,服務器應該返回一個406錯誤(non acceptable)

通配符 * 代表任意類型

例如 Accept: / 代表瀏覽器可以處理所有類型,(一般瀏覽器發給服務器都是發這個)

Accept-Encoding:

作用: 瀏覽器申明自己接收的編碼方法,通常指定壓縮方法,是否支持壓縮,支持什么壓縮方法(gzip,deflate),(注意:這不是只字符編碼);

例如: Accept-Encoding: zh-CN,zh;q=0.8

Accept-Language

作用: 瀏覽器申明自己接收的語言。

語言跟字符集的區別:中文是語言,中文有多種字符集,比如big5,gb2312,gbk等等;

例如: Accept-Language: en-us

Connection

例如: Connection: keep-alive 當一個網頁打開完成后,客戶端和服務器之間用於傳輸HTTP數據的TCP連接不會關閉,如果客戶端再次訪問這個服務器上的網頁,會繼續使用這一條已經建立的連接

例如: Connection: close 代表一個Request完成后,客戶端和服務器之間用於傳輸HTTP數據的TCP連接會關閉, 當客戶端再次發送Request,需要重新建立TCP連接。

Host(發送請求時,該報頭域是必需的)

作用: 請求報頭域主要用於指定被請求資源的Internet主機和端口號,它通常從HTTP URL中提取出來的

例如: 我們在瀏覽器中輸入:http://www.hzau.edu.cn

瀏覽器發送的請求消息中,就會包含Host請求報頭域,如下:

Host:www.hzau.edu.cn

此處使用缺省端口號80,若指定了端口號,則變成:Host:指定端口號

Referer

當瀏覽器向web服務器發送請求的時候,一般會帶上Referer,告訴服務器我是從哪個頁面鏈接過來的,服務器籍此可以獲得一些信息用於處理。比如從我主頁上鏈接到一個朋友那里,他的服務器就能夠從HTTP Referer中統計出每天有多少用戶點擊我主頁上的鏈接訪問他的網站。可用於防盜鏈

User-Agent

作用:告訴HTTP服務器, 客戶端使用的操作系統和瀏覽器的名稱和版本.

我們上網登陸論壇的時候,往往會看到一些歡迎信息,其中列出了你的操作系統的名稱和版本,你所使用的瀏覽器的名稱和版本,這往往讓很多人感到很神奇,實際上,服務器應用程序就是從User-Agent這個請求報頭域中獲取到這些信息User-Agent請求報頭域允許客戶端將它的操作系統、瀏覽器和其它屬性告訴服務器。

例如: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; InfoPath.2; .NET4.0E)

更多參考資料:https://blog.csdn.net/u014175572/article/details/54861813

14 簡述Cookie和Session的區別與聯系。

cookie數據存放在客戶的瀏覽器上,session數據放在服務器上
cookie不是很安全,別人可以分析存放在本地的COOKIE並進行COOKIE欺騙考慮到安全應當使用session。
session會在一定時間內保存在服務器上。當訪問增多,會比較占用服務器的性能考慮到減輕服務器性能方面,應當使用COOKIE。
單個cookie保存的數據不能超過4K,很多瀏覽器都限制一個站點最多保存20個cookie。
建議:
將登陸信息等重要信息存放為SESSION
其他信息如果需要保留,可以放在COOKIE中

15. 簡述什么是瀏覽器的同源策略。

同源策略是瀏覽器上為安全性考慮實施的非常重要的安全策略。
何謂同源:
URL由協議、域名、端口和路徑組成,如果兩個URL的協議、域名和端口相同,則表示他們同源。
同源策略:
瀏覽器的同源策略,限制了來自不同源的"document"或腳本,對當前"document"讀取或設置某些屬性。
從一個域上加載的腳本不允許訪問另外一個域的文檔屬性。
舉個例子:
比如一個惡意網站的頁面通過iframe嵌入了銀行的登錄頁面(二者不同源),如果沒有同源限制,惡意網頁上的javascript腳本就可以在用戶登錄銀行的時候獲取用戶名和密碼。
在瀏覽器中<script>、<img>、<iframe>、<link>等標簽都可以加載跨域資源,
而不受同源限制,但瀏覽器限制了JavaScript的權限使其不能讀、寫加載的內容。
另外同源策略只對網頁的HTML文檔做了限制,對加載的其他靜態資源如javascript、css、圖片等仍然認為屬於同源。

16. git commit --amend 有和用處

git commit --amend命令是修復最新提交的便捷方式。它允許你將緩存的修改和之前的提交合並到一起,而不是提交一個全新的快照。它還可以用來簡單地編輯上一次提交的信息而不改變快照。

但是,amend不只是修改了最新的提交——它進行了一次替換。對於Git來說,這看上去像一個全新的提交,即上圖中用星號表示的那一個。在公共倉庫工作時一定要牢記這一點。

17. git如何查看某次提交修改的內容

首先我們可以使用git log 顯示歷史的提交列表
git show 便可以顯示某次提交的修改內容
git show filename 可以顯示某次提交的某個內容的修改信息。
git log -p 查看某個文件的修改歷史
git log -p -2 查看最近2次的更新內容

18. git如何比較兩個commit的區別?

git diff commit-id-1 commit-id-2

19. git如何把分支A上某個commit應用到分支B上。

場景A分支上部分文件需要合並到B分支,然而這些文件又是多次commit,並不能直接使用cherry-pick。
然而需要合並的文件並不是太多,所以果斷的選擇了merge的部分文件合並。
1 首先切換到B分支 , git checkout branchB
2 整理好需要合並的文件列表, git checkout branchA file1 file2 ……

20. 如何查看Linux系統的啟動時間,磁盤使用量,內存使用量。

查看啟動時間: uptime
查看磁盤使用情況:df -lh
查看內存使用量:top
本次題目內容較多,難度也偏大,大部分內容來自網絡的整理,如有意見和疑問大家可以在下面留言,我們一起探討。


免責聲明!

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



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