原文轉載自「劉悅的技術博客」https://v3u.cn/a_id_151
最近春招如火如荼,我接觸到的幾位同學也紛紛去市場裏試了試水,不得不說由於疫情的影響,導致目前中等水平的開發者擇業有了些許困難,畢竟不是所有人都能去一二線的大廠,有的時候海浪過大,我們不得不收起風帆,臥薪嚐膽,入港蟄伏,所以我們可以把目光投向一些相對要求不是特別高的崗位,比如外包崗,當然了業內其實對外包公司有一些偏見,評價不高。客觀的說,外包公司確實有一些不盡如人意的地方,但是其實也有一些好處不能忽略:比如接觸的項目多,接觸的人多,積累的經驗快,適合想積累經驗的人,同時有進入甲方的機會,如果業務做得不錯,有一定機會能進入甲方公司,一般甲方如果是大廠的話直接進不去但通過外包進去後有一定概率能轉甲方,不失爲一條曲線救國的路線。文思海輝作爲國內外包公司的執牛耳者,它的面試題相對有代表性,這裏我們針對文思海輝的外包崗面試題進行分析和總結,技術棧以Python爲主,輔以一些簡單的前端知識點。
1.自我介紹
關於自我介紹這個其實是老生常談的問題,很多人的介紹都很模式化
,比如我叫某某,今年多大,几几年在某家公司都做過什麼,其實這些基本信息都在簡歷上已經明確註明了,所以自我介紹完全可以說一點簡歷上沒寫的東西,比如可以說說自己最近關注的新技術,對於一些最近的業內新聞有哪些自己的見解,也可以藉助自我介紹環節主動和麪試官搭訕,問問老家在哪兒之類的,一開始和麪試官簡單的交流可以判斷這位面試官關注點在哪裏,比如是注重細節還是着眼大局,爲下面的面試題打好基礎。
2.range和xrange的區別
這是一個相當古老的問題,古老到可以追溯到Python2時代,由此可見文思海輝的Python面試題更新頻率並不高,首先一定要明確python3已經沒有xrange方法,之前的xrange已經重命名爲range,而在python2時代,range返回的是一個包含所有元素的列表,xrange返回的是一個生成器,生成器是一個可迭代對象,在對生成器進行迭代時,元素是逐個被創建的。而列表需要根據列表長度而開闢出相應的內存空間用來遍歷,一般來看,在對大序列進行迭代的時候,因爲xrange的特性,所以它會比較節約內存。
3.請談談Python的深淺拷貝
這也是一個python面試經常會被問到的問題,一般人的簡單理解就是淺拷貝會影響原對象而深拷貝不會,其實這道題是有坑的,深拷貝之後對原對象不產生影響基本問題不大,但是淺拷貝一定要分三種情況來討論
1.拷貝不可變對象:只是增加一個指向原對象的引用,改變會互相影響。
>>> a = (1, 2, [3, 4])
>>> b = copy.copy(a)
>>> b
... (1, 2, [3, 4])
# 改變一方,另一方也改變
>>> b[2].append(5)
>>> a
... (1, 2, [3, 4, 5])
2.拷貝可變對象(一層結構):產生新的對象,開闢新的內存空間,改變互不影響。
>>> import copy
>>> a = [1, 2, 3]
>>> b = copy.copy(a)
>>> b
... [1, 2, 3]
# 查看兩者的內存地址,不同,開闢了新的內存空間
>>> id(b)
... 1833997595272
>>> id(a)
... 1833997595080
>>> a is b
... False
# 改變了一方,另一方關我卵事
a = [1, 2, 3] b = [1, 2, 3]
>>> b.append(4)
>>> a
... [1, 2, 3]
>>> a.append(5)
>>> b
... [1, 2, 3, 4]
3.拷貝可變對象(多層結構):產生新的對象,開闢新的內存空間,不改變包含的子對象則互不影響、改變包含的子對象則互相影響。
>>> import copy
>>> a = [1, 2, [3, 4]]
>>> b = copy.copy(a)
>>> b
... [1, 2, [3, 4]]
# 查看兩者的內存地址,不同,開闢了新的內存空間
>>> id(b)
1833997596488
>>> id(a)
1833997596424
>>> a is b
... False
# 1.沒有對包含的子對象進行修改,另一方關我卵事
a = [1, 2, [3, 4]] b = [1, 2, [3, 4]]
>>> b.append(5)
>>> a
... [1, 2, [3, 4]]
>>> a.append(6)
>>> b
... [1, 2, [3, 4], 5]
# 2.對包含的子對象進行修改,另一方也隨之改變
a = [1, 2, [3, 4]] b = [1, 2, [3, 4]]
>>> b[2].append(5)
>>> a
... [1, 2, [3, 4, 5]]
>>> a[2].append(6)
>>> b
... [1, 2, [3, 4, 5, 6]]
4.請談談Python中對內存是怎麼管理的
內存管理也是一道經典的高頻面試題,使用一門語言,如果不瞭解它的內存管理機制,顯然有點說不過去,後期涉及代碼性能優化方面可能會力不從心。
Python有一個私有堆空間來保存所有的對象和數據結構。作爲開發者,我們無法訪問它,是解釋器在管理它。但是有了核心API後,我們可以訪問一些工具。Python內存管理器控制內存分配。
另外,內置垃圾回收器會回收使用所有的未使用內存,所以使其適用於堆空間。
一、垃圾回收:python不像C++,Java等語言一樣,他們可以不用事先聲明變量類型而直接對變量進行賦值。對Python語言來講,對象的類型和內存都是在運行時確定的。這也是爲什麼我們稱Python語言爲動態類型的原因(這裏我們把動態類型可以簡單的歸結爲對變量內存地址的分配是在運行時自動判斷變量類型並對變量進行賦值)。
二、引用計數:Python採用了類似Windows內核對象一樣的方式來對內存進行管理。每一個對象,都維護這一個對指向該對對象的引用的計數。當變量被綁定在一個對象上的時候,該變量的引用計數就是1,(還有另外一些情況也會導致變量引用計數的增加),系統會自動維護這些標籤,並定時掃描,當某標籤的引用計數變爲0的時候,該對就會被回收。
一個對象, 會記錄着自身被引用的個數 每增加一個引用, 這個對象的引用計數會自動+1 每減少一個引用, 這個對象的引用計數會自動-1
引用計數+1場景
1、對象被創建
p1 = Person()
2、對象被引用
p2 = p1
3、對象被作爲參數,傳入到一個函數中
log(p1)
這裏注意會+2, 因爲內部有兩個屬性引用着這個參數
4、對象作爲一個元素,存儲在容器中
l = [p1]
引用計數-1場景
1、對象的別名被顯式銷燬
del p1
2、對象的別名被賦予新的對象
p1 = 123
3、一個對象離開它的作用域
一個函數執行完畢時
內部的局部變量關聯的對象, 它的引用計數就會-1
4、對象所在的容器被銷燬,或從容器中刪除對象
查看引用計數
import sys
class Person:
pass
p1 = Person() # 1
print(sys.getrefcount(p1)) # 2
p2 = p1 # 2
print(sys.getrefcount(p1)) # 3
del p2 # 1
print(sys.getrefcount(p1)) # 2
del p1
# print(sys.getrefcount(p1)) #error,因爲上一行代碼執行類p1對象已經銷燬
>>>> 打印結果
2
3
2
關於對象間互相引用,導致對象不能通過引用計數器進行銷燬手動觸發垃圾回收,揮手循環引用問題
import objgraph
import gc
class Person:
pass
class Dog:
pass
p = Person()
d = Dog()
p.pet = d
d.master = p
del p
del d
gc.collect() #手動觸發垃圾回收
print(objgraph.count("Person"))
print(objgraph.count("Dog"))
>>>> 打印結果
0
0
5.請談談web框架Django和Flask的區別
類似這種開放性問題,可以根據自己的認知簡要回答:
1.Django走的是大而全的方向,開發效率高。它的MTV框架,自帶的ORM,admin後臺管理,自帶的sqlite數據庫和開發測試用的服務器 給開發者提高了超高的開發效率
2.Flask是輕量級的框架,自由,靈活,可擴展性很強,核心基於Werkzeug WSGI工具和jinja2模板引擎
但是這樣的回答還遠遠不夠,需要一個生動形象的例子來彰顯理解的深度,可以說Flask就像是小轎車,可以自由馳騁,不受限制,也可以隨時更換輪胎,進行改裝這就和可擴展性強對應了起來。
而Django則更像是一列火車,火車雖然只能依託鐵軌而前行,行進路線相對死板,但是火車內有各種自帶服務,比如衛生間、餐飲、臥鋪等等,這就對應了orm,admin等內置模塊,不需要自己造輪子,但是必須按照框架規定的方式來進行,多多少少失去了自由性。
總體來看,文思海輝的面試相對還是偏基礎,並沒有算法結合場景的複雜問題,同時也沒有市面上很火的leetcode原題,所以拿offer的機率肯定要比一線大廠要高,但是話說回來,如果拿到了外包公司的offer也不要只着眼於眼前,還是要繼續努力和精進,爭取一個去大廠的機會,最後附上一個Python全棧的知識體系結構圖
原文轉載自「劉悅的技術博客」 https://v3u.cn/a_id_151