python-面試通關寶典
有面Python開發方向的,看這一個repo就夠啦?
語言特性
1.談談對 Python 和其他語言的區別
Python屬於解釋型語言,當程序運行時,是一行一行的解釋,並運行,所以調式代碼很方便,開發效率高,
還有龜叔給Python定位是任其自由發展、優雅、明確、簡單,所以在每個領域都有建樹,所有它有着非常強大的第三方庫,
特點:
語法簡潔優美,功能強大,標准庫與第三方庫都非常強大,而且應用領域也非常廣
可移植性,可擴展性,可嵌入性
缺點:
運行速度慢,
- 解釋型
- python/php
- 編譯型
- c/java/c#
- Python弱類型
2.簡述解釋型和編譯型編程語言
解釋型:就是邊解釋邊執行(Python,php)
編譯型:編譯后再執行(c、java、c#)
3.Python 的解釋器種類以及相關特點?
CPython
是官方版本的解釋器:CPython。是使用C語言開發的,所以叫CPython。在命令行下運行python就是啟動CPython解釋器。
CPython是使用最廣的Python解釋器。教程的所有代碼也都在CPython下執行。
IPython
IPython是基於CPython之上的一個交互式解釋器,也就是說,IPython只是在交互方式上有所增強,但是執行Python代碼的功能和CPython是完全一樣的。CPython用>>>作為提示符,而IPython用In [序號]:作為提示符。
PyPy
由Python寫的解釋器,它的執行速度是最快。PyPy采用JIT技術,對Python代碼進行動態編譯(注意不是解釋),
絕大部分Python代碼都可以在PyPy下運行,但是PyPy和CPython有一些是不同的,這就導致相同的Python代碼在兩種解釋器下執行可能會有不同的結果。
Jython
Jython是運行在Java平台上的Python解釋器,可以直接把Python代碼編譯成Java字節碼執行。
IronPython
IronPython和Jython類似,只不過IronPython是運行在.Net平台上的Python解釋器,可以直接把Python代碼編譯成.Net的字節碼。
小結:
Python的解釋器很多,但使用最廣泛的還是CPython。如果要和Java或.Net平台交互,最好的辦法不是用Jython或IronPython,而是通過網絡調用來交互,確保各程序之間的獨立性。
4.說說你知道的Python3 和 Python2 之間的區別?
1:打印時,py2需要可以不需要加括號,py3 需要
python 2 :print ('lili') , print 'lili'
python 3 : print ('lili')
python3 必須加括號
exec語句被python3廢棄,統一使用exec函數
2:內涵
Python2:1,臃腫,源碼的重復量很多。
2,語法不清晰,摻雜着C,php,Java,的一些陋習。
Python3:幾乎是重構后的源碼,規范,清晰,優美。
3、輸出中文的區別
python2:要輸出中文 需加 # -*- encoding:utf-8 -*-
Python3 : 直接搞
4:input不同
python2 :raw_input
python3 :input 統一使用input函數
5:指定字節
python2在編譯安裝時,可以通過參數-----enable-unicode=ucs2 或-----enable-unicode=ucs4分別用於指定使用2個字節、4個字節表示一個unicode;
python3無法進行選擇,默認使用 ucs4
查看當前python中表示unicode字符串時占用的空間:
impor sys
print(sys.maxunicode)
#如果值是65535,則表示使用usc2標准,即:2個字節表示
#如果值是1114111,則表示使用usc4標准,即:4個字節表示
6:
py2:xrange
range
py3:range 統一使用range,Python3中range的機制也進行修改並提高了大數據集生成效率
7:在包的知識點里
包:一群模塊文件的集合 + __init__
區別:py2 : 必須有__init__
py3:不是必須的了
8:不相等操作符"<>"被Python3廢棄,統一使用"!="
9:long整數類型被Python3廢棄,統一使用int
10:迭代器iterator的next()函數被Python3廢棄,統一使用next(iterator)
11:異常StandardError 被Python3廢棄,統一使用Exception
12:字典變量的has_key函數被Python廢棄,統一使用in關鍵詞
13:file函數被Python3廢棄,統一使用open來處理文件,可以通過io.IOBase檢查文件類型
5.Python3 和 Python2 中 int 和 long 區別?
在python3里,只有一種整數類型int,大多數情況下,和python2中的長整型類似。
6.xrange 和 range 的區別?
xrange用法與range完全相同,所不同的是生成的不是一個數組,而是一個生成器。
要生成很大的數字序列的時候,用xrange會比range性能優很多,因為不需要一上來就開辟一塊很大的內存空間,這兩個基本上都是在循環的時候用。
在 Python 3 中,range() 是像 xrange() 那樣實現,xrange()被拋棄。
編碼規范
7.什么是 PEP8?
PEP是 Python Enhancement Proposal 的縮寫,翻譯過來就是 Python增強建議書
簡單說就是一種編碼規范,是為了讓代碼“更好看”,更容易被閱讀
具體可參考:
https://www.python.org/dev/peps/pep-0008/
8.了解 Python 之禪么?
import this
9.了解 dosctring 么?
Python有一個很奇妙的特性,稱為 文檔字符串 ,它通常被簡稱為 docstrings 。DocStrings是一個重要的工具,由於它幫助你的程序文檔更加簡單易懂,你應該盡量使用它。你甚至可以在程序運行的時候,從函數恢復文檔字符串。
使用魔法方法'__doc__'可以打印docstring的內容
10.了解類型注解么?
def add(x:int, y:int) -> int:
return x + y
用 : 類型 的形式指定函數的參數類型,用 -> 類型 的形式指定函數的返回值類型
11.例舉你知道 Python 對象的命名規范,例如方法或者類等
類名都使用首字母大寫開頭(Pascal命名風格)的規范;
全局變量全用大寫字母,單詞之間用 _分割;
普通變量用小寫字母,單詞之間用 _分割;
普通函數和普通變量一樣;
私有函數以 __ 開頭(2個下划線),其他和普通函數一樣;
12.Python 中的注釋有幾種?
單行注釋,多行注釋,docstring注釋
13.如何優雅的給一個函數加注釋?
在函數邏輯行的首行使用""" xxx """給函數添加注釋,注釋中可包含函數參數的說明,返回值說明等
def foo(bar):
"""
This is an example.
:param bar: explain param bar
"""
return bar
14.如何給變量加注釋?
參數注釋:以冒號(:)標記
返回值注釋:以 -> 標記
示例:
def add(x:int, y:int) -> int:
return x + y
15.Python 代碼縮進中是否支持 Tab 鍵和空格混用。
支持,Python 並沒有強制要求你用Tab縮進或者用空格縮進,但在 PEP8中,建議使用4個空格來縮進
16.是否可以在一句 import 中導入多個庫?
可以的
import json,random,requests
17.在給 Py 文件命名的時候需要注意什么?
全小寫,單詞之間使用下划線分隔
18.例舉幾個規范 Python 代碼風格的工具
pylint,black,pycharm也帶有pep8的代碼規范工具
數據類型
字符串
19.列舉 Python 中的基本數據類型?
Python3 中有六個標准的數據類型:
Number(數字)
String(字符串)
List(列表)
Tuple(元組)
Set(集合)
Dictionary(字典)
Python3 的六個標准數據類型中:
不可變數據(3 個):Number(數字)、String(字符串)、Tuple(元組);
可變數據(3 個):List(列表)、Dictionary(字典)、Set(集合)。
20.如何區別可變數據類型和不可變數據類型
Python3 的六個標准數據類型中:
不可變數據(3 個):Number(數字)、String(字符串)、Tuple(元組);
可變數據(3 個):List(列表)、Dictionary(字典)、Set(集合)。
21.將"hello world"轉換為首字母大寫"Hello World"
z = 'hello world'
[s.capitalize() for s in z.split(' ')]
22.如何檢測字符串中只含有數字?
# 分為兩種情況
# 1.不包含正負號 +-
a = '32323'
a.isdigit()
# 2.含有正負號
import re
re.match(r'[+-]?\d+$',a)
23.將字符串"ilovechina"進行反轉
s = 'ilovechina'
x = list(s)
x.reverse()
''.join(x)
24.Python 中的字符串格式化方式你知道哪些?
# Python3.6之后的版本提供了三種字符串格式化的方式
# 1. %s占位符
def foo(name):
return 'hello %s' % name
# 2. format()
def foo(name):
return 'hello {}'.format(name)
# f-string
def foo(name):
return f'hello {name}'
25.有一個字符串開頭和末尾都有空格,比如“ adabdw ”,要求寫一個函數把這個字符串的前后空格都去掉。
s = " adabdw "
s.strip()
26.獲取字符串”123456“最后的兩個字符。
s = '123456'
print(s[-2:])
27.一個編碼為 GBK 的字符串 S,要將其轉成 UTF-8 編碼的字符串,應如何操作?
s.encode('utf-8')
28.s=“info:xiaoZhang 33 shandong”,用正則切分字符串輸出[‘info’, ‘xiaoZhang’, ‘33’, ‘shandong’]
import re
s="info:xiaoZhang 33 shandong"
re.split(r'[:\s]',s)
27.怎樣將字符串轉換為小寫?
b = 'HHH'
b.lower()
28.單引號、雙引號、三引號的區別?
s = 'hello'
s= "hello"
單引號與雙引號沒有區別,
三引號可以用來加注釋,所加注釋可以使用__doc__查看
29.a = "你好 中國 ",去除多余空格只留一個空格。
a = "你好 中國 "
s = ' '.join(a.strip().split())
列表
30.已知 AList = [1,2,3,1,2],對 AList 列表元素去重,寫出具體過程。
a_list = [1,2,3,1,2]
ss = set(a_list)
31.如何實現 “1,2,3” 變成 [“1”,“2”,“3”]
s = "1,2,3"
s.split(',')
32.給定兩個 list,A 和 B,找出相同元素和不同元素
# 最直接的方法
list_a = [1,2,3,4,5,6]
list_b = [2,3,6]
same_l = []
not_same = []
for i in list_a:
if i not in list_b:
not_same.append(i)
for j in list_b:
if j not in list_a:
not_same.append(j)
for x in list_a:
if x in list_b:
same_l.append(x)
# 奇技淫巧
list_a = [1,2,3,4,5,6]
list_b = [2,3,6]
set1 = set(list_a)
set2 = set(list_b)
# 相同元素
print(set1&set2)
# 不同元素
print(set1^set2)
33.[[1,2],[3,4],[5,6]]一行代碼展開該列表,得出[1,2,3,4,5,6]
mm = [[1,2],[3,4],[5,6]]
[j for a in mm for j in a]
34.合並列表[1,5,7,9]和[2,2,6,8]
a = [1,5,7,9]
b = [2,2,6,8]
# 方法1
a.extend(b)
# 方法2
a[0:0] = b
35.如何打亂一個列表的元素?
import random
a = [1,5,7,9]
random.shuffle(a)
字典
36.字典操作中 del 和 pop 有什么區別
del 操作刪除鍵值對,不返回值;
pop 操作刪除鍵值對的同時,返回鍵所對應的值。
37.按照字典的內的年齡排序
d1 = [
{'name':'alice', 'age':38},
{'name':'bob', 'age':18},
{'name':'Carl', 'age':28},
]
sorted(d1,key=lambda x:x['age'])
38.請合並下面兩個字典 a = {“A”:1,“B”:2},b = {“C”:3,“D”:4}
# python3合並字典有三種方式
# 1.
a = {'a':1,'b':2}
b = {'c':3,'d':4}
c = {}
c.update(a)
c.update(b)
# 2.
c = dict(a,**b)
# 3.
c = {**a,**b} # 官方推薦這種方式
39.如何使用生成式的方式生成一個字典,寫一段功能代碼。
{x:x*x for x in range(6)}
40.如何把元組(“a”,“b”)和元組(1,2),變為字典{“a”:1,“b”:2}
a = ('a','b')
b = (1,2)
z=zip(a,b)
c = dict(z)
綜合
41.Python 常用的數據結構的類型及其特性?
List,tuple,dict,set是比較常用的數據結構,queue,heap,deque,ProrityQueue,multiprocessing.Queue等進階的數據結構類型。特性就去查查吧,寫在這里太長了。
42.如何將元組(“A”,“B”)和元組(1,2),合並成字典{“A”:1,“B”:2}
a = ('A','B')
b = (1,2)
z=zip(a,b)
c = dict(z)
43.Python 里面如何實現 tuple 和 list 的轉換?
tuple(list) # tuple轉list
list(tuple) # list 轉tuple
44.我們知道對於列表可以使用切片操作進行部分元素的選擇,那么如何對生成器類型的對象實現相同的功能呢?
使用自帶的itertools庫進行實現,具體實現方式 itertools.islice(生成器對象,起始位置,結束位置),即可實現切片功能。
45.請將[i for i in range(3)]改成生成器
(i for i in range(3))
46.a="hello"和 b="你好"編碼成 bytes 類型
a.encode()
b.encode()
47.下面的代碼輸出結果是什么?
a = (1,2,3,[4,5,6,7],8)
a[2] = 2
報錯,元組是不可變對象,不支持修改
48.下面的代碼輸出的結果是什么?
a = (1,2,3,[4,5,6,7],8)
a[5] = 2
報錯,元組是不可變對象,下標越界
操作類題目
49.Python 交換兩個變量的值
a,b = b,a
50.在讀文件操作的時候會使用 read、readline 或者 readlines,簡述它們各自的作用
read將整個文本都讀取為一個字符串,占用內存大,readline讀取為一個生成器,支持遍歷和迭代,占用空間小。readlines將文本讀取為列表,占用空間大
51.json 序列化時,可以處理的數據類型有哪些?如何定制支持 datetime 類型?
字符串、數字(整數和浮點數)、字典、列表、布爾值、None。使用strftime將datetime格式化為標准字符串類型即可。
52.json 序列化時,默認遇到中文會轉換成 unicode,如果想要保留中文怎么辦?
使用json.dumps函數時,添加參數ensure_ascii=False,如果想顯示的更美觀,可以添加indent=2參數,會在每個key值前添加兩個空格。
53.有兩個磁盤文件 A 和 B,各存放一行字母,要求把這兩個文件中的信息合並(按字母順序排列),輸出到一個新文件 C 中。
with open('A.txt','r') as f:
a = f.readlines()[0]
with open('B.txt','r') as f:
b = f.readlines()[0]
a.extend(b).sort()
with open('C.txt','w') as f:
for i in a:
f.write(i)
54.如果當前的日期為 20190530,要求寫一個函數輸出 N 天后的日期,(比如 N 為 2,則輸出 20190601)。
import datetime
def getday(n):
y,m,d = 2019,5,30
the_date = datetime.datetime(y,m,d)
result_date = the_date + datetime.timedelta(days=n)
target_date = result_date.strftime('%Y%m%d')
return target_date
55.寫一個函數,接收整數參數 n,返回一個函數,函數的功能是把函數的參數和 n 相乘並把結果返回。
def mul(n):
def wrapper(m):
return n*m
return wrapper
# 閉包的基本操作
56.下面代碼會存在什么問題,如何改進?
def strappend(num):
str='first'
for i in range(num):
str+=str(i)
return str
# 將str(i)改為str[i]
57.一行代碼輸出 1-100 之間的所有偶數。
[x for x in range(101) if x %2 ==0]
58.with 語句的作用,寫一段代碼?
# with語句用來管理資源,及時關閉文件等操作,避免資源的泄漏
with open('a.txt','r') as f:
f.read()
59.python 字典和 json 字符串相互轉化方法
json.dumps() 將Python中的對象轉換為JSON中的字符串對象
json.loads() 將JSON中的字符串對象轉換為Python中的對象
60.請寫一個 Python 邏輯,計算一個文件中的大寫字母數量
import re
with open('a.txt','r') as f:
f_content = f.read()
len_cap = len(re.compile(r'[A-Z]').findall(f_content))
高級特效
70.函數裝飾器有什么作用?請列舉說明?
函數裝飾器可以在不修改原函數的條件下,為原函數添加額外的功能,例如記錄日志,運行性能,緩存等
以記錄函數運行時間為例,實現一個裝飾器
import time
def time_it(func):
def wrapper(func):
start_time = time.time()
res = func()
end_time = time.time()
return start_time - end_time
return wrapper
71.Python 垃圾回收機制?
引用計數機制:
python里每一個東西都是對象,它們的核心就是一個結構體:PyObject
typedef struct_object {
int ob_refcnt;
struct_typeobject *ob_type;
} PyObject;
PyObject是每個對象必有的內容,其中ob_refcnt就是做為引用計數。當一個對象有新的引用時,它的ob_refcnt就會增加,當引用它的對象被刪除,它的ob_refcnt就會減少
#define Py_INCREF(op) ((op)->ob_refcnt++) //增加計數
#define Py_DECREF(op) \ //減少計數
if (--(op)->ob_refcnt != 0) \
; \
else \
__Py_Dealloc((PyObject *)(op))
當引用計數為0時,該對象生命就結束了。
引用計數機制的優點:
簡單
實時性:一旦沒有引用,內存就直接釋放了。不用像其他機制等到特定時機。實時性還帶來一個好處:處理回收內存的時間分攤到了平時
72.魔法函數 __call__怎么使用?
class Bar:
def __call__(self, *args, **kwargs):
print('i am instance method')
b = Bar() # 實例化
b() # 實例對象b 可以作為函數調用 等同於b.__call__ 使用
# OUT: i am instance method
# 帶參數的類裝飾器
class Bar:
def __init__(self, p1):
self.p1 = p1
def __call__(self, func):
def wrapper():
print("Starting", func.__name__)
print("p1=", self.p1)
func()
print("Ending", func.__name__)
return wrapper
@Bar("foo bar")
def hello():
print("Hello")
73.如何判斷一個對象是函數還是方法?
判斷對象是函數或方法應該使用type(obj)
74.@classmethod 和@staticmethod 用法和區別
一般來說,要使用某個類的方法,需要先實例化一個對象再調用方法。
而使用@staticmethod或@classmethod,就可以不需要實例化,直接類名.方法名()來調用。
這有利於組織代碼,把某些應該屬於某個類的函數給放到那個類里去,同時有利於命名空間的整潔。
既然@staticmethod和@classmethod都可以直接類名.方法名()來調用,那他們有什么區別呢
從它們的使用上來看,
@staticmethod不需要表示自身對象的self和自身類的cls參數,就跟使用函數一樣。
@classmethod也不需要self參數,但第一個參數需要是表示自身類的cls參數。
如果在@staticmethod中要調用到這個類的一些屬性方法,只能直接類名.屬性名或類名.方法名。
而@classmethod因為持有cls參數,可以來調用類的屬性,類的方法,實例化對象等,避免硬編碼
class A(object):
bar = 1
def foo(self):
print 'foo'
@staticmethod
def static_foo():
print 'static_foo'
print A.bar
@classmethod
def class_foo(cls):
print 'class_foo'
print cls.bar
cls().foo()
A.static_foo()
A.class_foo()
75.Python 中的接口如何實現?
#抽象類加抽象方法就等於面向對象編程中的接口
from abc import ABCMeta,abstractmethod
class interface(object):
__metaclass__ = ABCMeta #指定這是一個抽象類
@abstractmethod #抽象方法
def Lee(self):
pass
def Marlon(self):
pass
class RelalizeInterfaceLee(interface):#必須實現interface中的所有函數,否則會編譯錯誤
def __init__(self):
print '這是接口interface的實現'
def Lee(self):
print '實現Lee功能'
def Marlon(self):
pass
76.Python 中的反射了解么?
通過字符串映射object對象的方法或者屬性
hasattr(obj,name_str): 判斷objec是否有name_str這個方法或者屬性
getattr(obj,name_str): 獲取object對象中與name_str同名的方法或者函數
setattr(obj,name_str,value): 為object對象設置一個以name_str為名的value方法或者屬性
delattr(obj,name_str): 刪除object對象中的name_str方法或者屬性
舉個栗子
import requests
class Http(object):
def get(self,url):
res = requests.get(url)
response = res.text
return response
def post(self,url):
res = requests.post(url)
response = res.text
return response
# 使用反射后
url = "https://www.jianshu.com/u/14140bf8f6c7"
method = input("請求方法>>>:")
h = Http()
if hasattr(h,method):
func = getattr(h,method)
res = func(url)
print(res)
else:
print("你的請求方式有誤...")
77.metaclass 作用?以及應用場景?
metaclass我沒怎么用過,不能亂說誤人子弟,可以看下這篇博文https://www.cnblogs.com/xybaby/p/7927407.html
78.hasattr() getattr() setattr()的用法
hasattr(obj,name_str): 判斷objec是否有name_str這個方法或者屬性
getattr(obj,name_str): 獲取object對象中與name_str同名的方法或者函數
setattr(obj,name_str,value): 為object對象設置一個以name_str為名的value方法或者屬性
delattr(obj,name_str): 刪除object對象中的name_str方法或者屬性
79.請列舉你知道的 Python 的魔法方法及用途。
1. __call__:允許一個類的實例像函數一樣被調用。實質上說,這意味着 x() 與 x._call_() 是相同的
2.__init__:顯示初始化屬性
3.__str__,__repr__,定義類的時候,重寫這兩個方法可以讓類更清晰
再就是__setattr__,__getattr__,__delattr__等等
80.如何知道一個 Python 對象的類型?
type(obj)
81.Python 的傳參是傳值還是傳址?
說傳值或者傳引用都不准確。非要安一個確切的叫法的話,叫傳對象(call by object)
具體可以參考這篇文章:https://foofish.net/python-function-args.html
82.Python 中的元類(metaclass)使用舉例
參考77.
83.簡述 any()和 all()方法
#any(x)判斷x對象是否為空對象,如果都為空、0、false,則返回false,如果不都為空、0、false,則返回true
#all(x)如果all(x)參數x對象的所有元素不為0、''、False或者x為空對象,則返回True,否則返回False
>>> 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
>>> all(['a', 'b', 'c', 'd']) #列表list,
True
>>> all(['a', 'b', 'c', 'd']) #列表list,元素都不為空或0
True
>>> all(['a', 'b', '', 'd']) #列表list,存在一個為空的元素
False
>>> all([0, 1,2, 3]) #列表list,存在一個為0的元素
False
>>> all(('a', 'b', 'c', 'd')) #元組tuple,元素都不為空或0
True
>>> all(('a', 'b', '', 'd')) #元組tuple,存在一個為空的元素
False
>>> all((0, 1,2, 3)) #元組tuple,存在一個為0的元素
False
>>> all([]) # 空列表
True
>>> all(()) # 空元組
True
>>> #注意:空元組、空列表返回值為True,這里要特別注意
84.filter 方法求出列表所有奇數並構造新列表,a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list(filter(lambda x: x%2==1,a))
85.什么是猴子補丁?
猴子補丁是一個概念,不是python中發明的,其他動態語言也有這么個概念。 《松本行弘的程序世界》這本書,里面專門有一章講了猴子補丁的設計,所謂的猴子補丁的含義是指在動態語言中,不去改變源碼而對功能進行追加和變更
86.在 Python 中是如何管理內存的?
參考71.
87.當退出 Python 時是否釋放所有內存分配?
答案是否定的。那些具有對象循環引用或者全局命名空間引用的變量,在 Python 退出是往往不會被釋放
另外不會釋放 C 庫保留的部分內容。
正則表達式
88.使用正則表達式匹配<html><h1>www.baidu.com</html>
中的地址
import re
s = '<html><h1>www.baidu.com</html>'
p = re.compile(r'<html><h1>(.*?)</html>')
result = re.findall(p,s)[0]
a=“張明 98 分”,用 re.sub,將 98 替換為 100
import re
a="張明 98 分"
pa = re.compile(r'\d+')
re.sub(pa,'100',a)
89.正則表達式匹配中(.*)和(.?)匹配區別?**
加?會將貪婪模式改成懶惰模式,如果有問號的話,則表示匹配0個或1個問號前面的表達式
90.寫一段匹配郵箱的正則表達式
r'[0-9a-zA-Z_]*@.+\.(com|cn|net)$'
其他內容
91.解釋一下 python 中 pass 語句的作用?
Python pass 是空語句,是為了保持程序結構的完整性。
pass 不做任何事情,一般用做占位語句。
92.簡述你對 input()函數的理解
在python3中,input()獲取用戶輸入,不論用戶輸入的是什么,獲取到的都是字符串類型的。
93.python 中的 is 和==
is比較的是兩個對象的id值是否相等,也就是比較兩個對象是否為同一個實例對象,是否指向同一個內存地址。
==比較的是兩個對象的內容是否相等,默認會調用對象的__eq__()方法。
94.Python 中的作用域
L (Local) 局部作用域
E (Enclosing) 閉包函數外的函數中
G (Global) 全局作用域
B (Built-in) 內建作用域
95.三元運算寫法和應用場景?
條件語句比較簡單時可以使用三元運算符,最常見的就是
res = 'test True' if expression is True else 'test False'
96.了解 enumerate 么?
# 遍歷列表時候,攜帶索引index
a = ['a','b','c']
for index,item in a:
print(index,item)
97.列舉 5 個 Python 中的標准模塊
json,re,random,datetime,codecs
98.如何在函數中設置一個全局變量
使用global關鍵字
99.pathlib 的用法舉例
from pathlib import Path
data_folder = Path("source_data/text_files/")
file_to_open = data_folder / "raw_data.txt"
f = open(file_to_open)
print(f.read())
100.Python 中的異常處理,寫一個簡單的應用場景
class NameTooShortError(ValueError):
pass
def validate(name):
if len(name) < 10:
raise NameTooShortError(name)
# 使用自定義異常類,使得發生異常時的錯誤更容易排查
101.Python 中遞歸的最大次數,那如何突破呢?
python解釋器限制最大的遞歸深度是999,可以通過
import sys
sys.setrecursionlimit(10000) # set the maximum depth as 10000
重新設置最大遞歸深度
102.什么是面向對象的 mro
對於支持繼承的編程語言來說,其方法(屬性)可能定義在當前類,也可能來自於基類,所以在方法調用時就需要對當前類和基類進行搜索以確定方法所在的位置。而搜索的順序就是所謂的「方法解析順序」(Method Resolution Order,或MRO)。對於只支持單繼承的語言來說,MRO 一般比較簡單;而對於 Python 這種支持多繼承的語言來說,MRO 就復雜很多。
103.isinstance 作用以及應用場景?
來判斷一個對象是否是一個已知的類型
舉個栗子:
p= 'sfa'
isinstance(p,str)
True
104.什么是斷言?應用場景?
Python 的斷言語句是一種調試工具,用來測試某個斷言條件。如果斷言條件 為真,則程序將繼續正常執行;但如果條件為假,則會引發 AssertionError 異常並顯示相關 的錯誤消息。
105.lambda 表達式格式以及應用場景?
d = {'a':2,'b':1,'c':3,'d':'4'}
sorted(d,key=lambda x :x[1])
# 將字典d按照值排序
106.新式類和舊式類的區別
Python 有兩種類:經典類(classic class)和新式類(new-style class)。兩者的不同之處在於新式類繼承自 object。在 Python 2.1 以前,經典類是唯一可用的形式;Python 2.2 引入了新式類,使得類和內置類型更加統一;在 Python 3 中,新式類是唯一支持的類。
107.dir()是干什么用的?
dir() 函數不帶參數時,返回當前范圍內的變量、方法和定義的類型列表;帶參數時,返回參數的屬性、方法列表。如果參數包含方法__dir__(),該方法將被調用。如果參數不包含__dir__(),該方法將最大限度地收集參數信息。
108.*一個包里有三個模塊,demo1.py, demo2.py, demo3.py,但使用 from tools import 導入模塊時,如何保證只有 demo1、demo3 被導入了。
但若想使用from pacakge_1 import *這種形式的寫法,需在 init.py中加上: all = [‘file_a’, ‘file_b’] #package_1下有file_a.py和file_b.py,在導入時init.py文件將被執行。
但不建議在 init.py中寫模塊,以保證該文件簡單。不過可在init.py導入我們需要的模塊,以便避免一個個導入、方便使用。
109.列舉 5 個 Python 中的異常類型以及其含義
1. ArithmeticError 此基類用於派生針對各種算術類錯誤而引發的內置異常: OverflowError, ZeroDivisionError, FloatingPointError
2. BufferError 當與 緩沖區 相關的操作無法執行時將被引發。
3. LookupError 此基類用於派生當映射或序列所使用的鍵或索引無效時引發的異常: IndexError, KeyError。 這可以通過 codecs.lookup() 來直接引發
4. ImportError 當 import 語句嘗試加載模塊遇到麻煩時將被引發。 並且當 from ... import 中的 "from list" 存在無法找到的名稱時也會被引發
5. IndexError 當序列抽取超出范圍時將被引發。 (切片索引會被靜默截短到允許的范圍;如果指定索引不是整數則 TypeError 會被引發
110.copy 和 deepcopy 的區別是什么?
copy 即所謂的淺拷貝,賦值的時候非遞歸地復制子對象的引用;
deepcopy 即所謂的深拷貝,賦值的時候遞歸復制子對象。
舉個栗子,
xs = [1,2,[2,3,4],3]
ys = xs # 淺拷貝
zs = deepcopy(xs) # 深拷貝
xs[2][0] = 5
print(ys)
[1,2,[2,3,4],3]
print(xs)
[1,2,[5,3,4],3]
print(zs)
[1,2,[5,3,4],3] # 由於深拷貝已經遞歸復制了子對象,所以內部的List也發生改變
111.**代碼中經常遇到的*args, kwargs 含義及用法。
這兩個是python中的可變參數。*args表示任何多個位置參數,它是一個tuple;**kwargs表示關鍵字參數,它是一個dict。並且同時使用*args和**kwargs時,必須*args參數列要在**kwargs前
112.Python 中會有函數或成員變量包含單下划線前綴和結尾,和雙下划線前綴結尾,區別是什么?
前置單下划線_var:命名約定,用來表示該名稱僅在內部使用。一般對 Python 解釋器沒 有特殊含義(通配符導入除外),只能作為對程序員的提示。
后置單下划線 var_:命名約定,用於避免與 Python 關鍵字發生命名沖突。
前置雙下划線__var:在類環境中使用時會觸發名稱改寫,對 Python 解釋器有特殊含義。
前后雙下划線__var__:表示由 Python 語言定義的特殊方法。在自定義的屬性中要避免
使用這種命名方式。
113.w、a+、wb 文件寫入模式的區別
w:寫入時會覆蓋上一次的寫入
a+:追加寫入
wb:以二進制文件形式寫入
114.舉例 sort 和 sorted 的區別
sort()與sorted()的不同在於,sort是在原位重新排列列表,而sorted()是產生一個新的列表
115.什么是負索引?
負索引和正索引不同,它是從右邊開始檢索。例如:使用負索引取出列表的最后一個數
lis[-1] # 取出列表的最后一個元素
lis[-2] # 取出列表的倒數第二個元素
116.pprint 模塊是干什么的?
print()和pprint()都是python的打印模塊,功能基本一樣,唯一的區別就是pprint()模塊打印出來的數據結構更加完整,每行為一個數據結構,更加方便閱讀打印輸出結果。特別是對於特別長的數據打印,print()輸出結果都在一行,不方便查看,而pprint()采用分行打印輸出,所以對於數據結構比較復雜、數據長度較長的數據,適合采用pprint()打印方式
117.解釋一下 Python 中的賦值運算符
在python中,使用 = 可以給變量賦值。
在算術運算時,為了簡化代碼的編寫,Python還提供了一系列與算術運算符對應的賦值運算符
例如,c += a 等效於 c=c+a
118.解釋一下 Python 中的邏輯運算符
and, or, not
119.講講 Python 中的位運算符
& 按位與運算符:參與運算的兩個值,如果兩個相應位都為1,則該位的結果為1,否則為0 (a & b) 輸出結果 12 ,二進制解釋: 0000 1100
| 按位或運算符:只要對應的二個二進位有一個為1時,結果位就為1 (a | b) 輸出結果 61 ,二進制解釋: 0011 1101
^ 按位異或運算符:當兩對應的二進位相異時,結果為1 (a ^ b) 輸出結果 49 ,二進制解釋: 0011 0001
~ 按位取反運算符:對數據的每個二進制位取反,即把1變為0,把0變為1 (~a ) 輸出結果 -61 ,二進制解釋: 1100 0011, 在一個有符號二進制數的補碼形式。
<< 左移動運算符:運算數的各二進位全部左移若干位,由”<<”右邊的數指定移動的位數,高位丟棄,低位補0 a << 2 輸出結果 240 ,二進制解釋: 1111 0000
>> 右移動運算符:把”>>”左邊的運算數的各二進位全部右移若干位,”>>”右邊的數指定移動的位數 a >> 2 輸出結果 15 ,二進制解釋: 0000 1111
120.在 Python 中如何使用多進制數字?
1、二進制數字由0和1組成,我們使用0b或0B前綴表示二進制數
print(int(0b1010))#10
2、使用bin()函數將一個數字轉換為它的二進制形式
print(bin(0xf))#0b1111
3、八進制數由數字0-7組成,用前綴0o或0O表示8進制數
print(oct(8))#0o10
4、十六進數由數字0-15組成,用前綴0x或者0X表示16進制數
print(hex(16))#0x10
print(hex(15))#0xf
121.怎樣聲明多個變量並賦值?
a,b = 1,2
算法和數據結構
122.已知:
AList = [1,2,3]
BSet = {1,2,3}
(1) 從 AList 和 BSet 中 查找 4,最壞時間復雜度那個大?
查找列表最壞時間復雜度是O(n),查找字典是O(1),因為字典的數據結構是散列表
(2) 從 AList 和 BSet 中 插入 4,最壞時間復雜度那個大?
插入的操作都是O(1)
123.用 Python 實現一個二分查找的函數
def binary_search(item,arr):
start = 0
end = len(arr) - 1
while start <= end:
mid = (start + end) // 2
if item == arr[mid]:
return True
elif item < arr[mid]:
end = mid - 1
else:
start = mid + 1
array = [1,2,3,4,5,6]
print(binary_search(2,array))
124.python 單例模式的實現方法
單例模式(Singleton Pattern)是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你希望在整個系統中,某個類只能出現一個實例時,單例對象就能派上用場。
比如,某個服務器程序的配置信息存放在一個文件中,客戶端通過一個 AppConfig 的類來讀取配置文件的信息。如果在程序運行期間,有很多地方都需要使用配置文件的內容,也就是說,很多地方都需要創建 AppConfig 對象的實例,這就導致系統中存在多個 AppConfig 的實例對象,而這樣會嚴重浪費內存資源,尤其是在配置文件內容很多的情況下。事實上,類似 AppConfig 這樣的類,我們希望在程序運行期間只存在一個實例對象
1.使用裝飾器實現單例模式
def singleton(cls):
_instance = {}
def _singleton(*args,**kargs):
if cls not in _instance:
_instance[cls] = cls(*args,**args)
return _instance[cls]
return _singleton
2.使用類實現單例模式
import threading
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = object.__new__(cls)
return Singleton._instanc
125.使用 Python 實現一個斐波那契數列
# 很多人上來就寫下面這種解法
def fibnaqie(n):
if n < 2:
return n
return fibnaqie(n - 1) + fibnaqie(n - 2)
# 這種解法時間復雜度高,而且一般不是面試官最想要的答案
# 下面這種優化的版本
def fib2(n):
fib_n = 0
fib_one = 1
fib_two = 0
res = [0, 1]
if n < 2:
return res[n]
for i in range(2, n + 1):
fib_n = fib_one + fib_two
fib_two = fib_one
fib_one = fib_n
return fib_n
# 所以適當地選擇遞歸還是迭代,要看具體情況,處理樹,或者圖的遍歷的時候,遞歸還是比迭代優先考慮的
126.找出列表中的重復數字
def find_duplicate(arr):
not_dup = set()
dup = set()
for x in arr:
if x not in not_dup:
not_dup.add(x)
else:
dup.add(x)
return dup
if __name__ == '__main__':
array = [1, 2, 3, 4, 4, 4, 4]
print(find_duplicate(array))
127.找出列表中的單個數字
a = ['a', 1, 2, 'b']
for x in a:
if str(x).isdigit():
print(x)
128.寫一個冒泡排序
def bubble_sort(arr):
n = len(arr)
if len(arr) < 2:
return arr
for i in range(n - 1):
count = 0
for j in range(n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
count += 1
if count == 0:
return arr
return arr
if __name__ == '__main__':
arr = [2, 1, 5, 3, 6]
arr1 = [1, 2, 3, 4, 5]
print(bubble_sort(arr1))
129.寫一個快速排序
def quick_sort(arr):
if len(arr) < 2:
return arr
else:
pivot = arr[0]
less = [i for i in arr[1:] if i <= pivot]
more = [i for i in arr[1:] if i > pivot]
return quick_sort(less) + [pivot] + quick_sort(more)
if __name__ == '__main__':
array = [2, 1, 6, 3, 4, 5]
print(quick_sort(array))
130.寫一個拓撲排序
# 使用循環進行拓撲排序
def topoSort(G):
# 初始化計算被指向節點的字典
cnt = dict((u, 0) for u in G.keys())
import pdb;pdb.set_trace()
# 若某節點被其他節點指向,該節點計算量+1
for u in G:
for v in G[u]:
cnt[v] += 1
# 收集被指向數為0的節點,此時Q只有一個節點,即起始節點a
Q = [u for u in cnt.keys() if cnt[u] == 0]
# 記錄結果
seq = []
while Q:
s = Q.pop()
seq.append(s)
for u in G[s]:
cnt[u] -= 1
if cnt[u] == 0:
Q.append(u)
return seq
# 有向無環圖的鄰接字典
G = {
'a': {'b', 'f'},
'b': {'c', 'd', 'f'},
'c': {'d'},
'd': {'e', 'f'},
'e': {'f'},
'f': {}
}
res = topoSort(G)
print(res)
131.python 實現一個二進制計算
''' 以實現二進制加法為例 給定兩個二進制字符串,返回他們的和(用二進制表示)。 輸入為非空字符串且只包含數字 1 和 0。 輸入: a = “11”, b = “1” 輸出: “100” '''
def binary_plus(a, b):
a, b = int('0b' + a, 2), int('0b'+b, 2)
return bin(a + b)[2:]
if __name__ == '__main__':
a = '11'
b = '1'
print(binary_plus(a, b))
# 解釋一下,class int(x, base)
x–字符串或數字
base–進制數,默認十進制。
bin()函數返回一個整型int或者長整數long int的二進制表示,bin()運算返回的是二進制。所以前兩位是二進制的標志,需要[2:]去除
132.有一組“+”和“-”符號,要求將“+”排到左邊,“-”排到右邊,寫出具體的實現方法。
def some_sort(arr):
list_a = []
list_b = []
for x in arr:
if x == '+':
list_a.append(x)
else:
list_b.append(x)
list_a.extend(list_b)
print(list_a)
return list_a
133.單鏈表反轉
class ListNode:
def __init__(self, val):
self.val = val
self.next = None
def reverse_node(head):
p = head
q = head.next
head.next = None
while q:
r = q.next
q.next = p
p = q
q = r
head = p
return head
if __name__ == '__main__':
l1 = ListNode(3)
l1.next = ListNode(2)
l1.next.next = ListNode(1)
l = reverse_node(l1)
print(l.val, l.next.val, l.next.next.val)
# 1,2,3
134.交叉鏈表求交點
'''先遍歷兩個鏈表,獲知兩個鏈表各自的長度,往后調整較長鏈表的指針,使之與較短鏈表的起始指針對齊,然后兩個鏈表同時往后掃描,直至二者的節點相同,證明該節點是相交節點,否則返回 None,表明兩個鏈表不相交,時間復雜度o(n),空間復雜度0(1)'''
class ListNode:
def __init__(self, val):
self.val = val
self.next = None
def solution(head_a, head_b):
def get_list_length(head):
len = 0
while head:
len += 1
head = head.next
return head
def forward_long_list(long_len, short_len, head):
delta = long_len - short_len
while delta and head:
head = head.next
delta -= 1
return head
def main_func(head_a, head_b):
head_a_len = get_list_length(head_a)
head_b_len = get_list_length(head_b)
if head_a_len > head_b_len:
head_a = forward_long_list(head_a_len, head_b_len, head_a)
else:
head_b = forward_long_list(head_a_len, head_b_len, head_b)
while head_a and head_b:
if head_a == head_b:
return head_a
head_a = head_a.next
head_b = head_b.next
return None
return main_func(head_a, head_b)
135.用隊列實現棧
class Stack:
def __init__(self):
self.queue_a = []
self.queue_b = []
def push(self, val):
self.queue_a.append(val)
def pop(self):
if len(self.queue_a) == 0:
return None
# 核心思想就是留一個元素在隊列a中,最后交換隊列a,b,再從b中取棧頂元素
while len(self.queue_a) != 1:
self.queue_b.append(self.queue_a.pop(0))
self.queue_a, self.queue_b = self.queue_b, self.queue_a
return self.queue_b.pop()
test_stack = Stack()
for i in range(5):
test_stack.push(i)
for i in range(5):
print(test_stack.pop())
136.找出數據流的中位數
# 中位數是有序列表中間的數。如果列表長度是偶數,中位數則是中間兩個數的平均值
# 如果是奇數,則中位數就是中間的數字
def find_zhongwei(arr):
arr.sort()
if len(ar) == 0:
return
if len(ar) == 1:
return ar[0]
mid = len(arr) // 2
if len(arr) % 2 == 0:
return (arr[mid - 1] + arr[len(arr) / 2]) / 2
else:
return arr[mid]
if __name__ == '__main__':
ar = [2, 1, 5, 3, 4]
print(find_zhongwei(ar))
137.二叉搜索樹中第 K 小的元素
# 問題本質:對二叉樹進行中序遍歷,中序排序后,返回第K-1個值
class Solution(object):
def kthSmallest(self, root, k):
""" :type root: TreeNode :type k: int :rtype: int """
def inorderTraversal(root):
if root is None:
return []
res = []
res.extend(inorderTraversal(root.left))
res.append(root.val)
res.extend(inorderTraversal(root.right))
return res
return inorderTraversal(root)[k - 1]
爬蟲相關
138.在 requests 模塊中,requests.content 和 requests.text 什么區別
resp.text返回的是Unicode型的數據。
resp.content返回的是bytes型也就是二進制的數據。
也就是說,如果你想取文本,可以通過r.text。
如果想取圖片,文件,則可以通過r.content。
(resp.json()返回的是json格式數據)
139.簡要寫一下 lxml 模塊的使用方法框架
Python 標准庫中自帶了 xml 模塊,但是性能不夠好,而且缺乏一些人性化的 API,相比之下,第三方庫 lxml 是用 Cython 實現的,而且增加了很多實用的功能,可謂爬蟲處理網頁數據的一件利器。lxml 大部分功能都存在 lxml.etree中.
可以看一下官方文檔,https://lxml.de/index.html
140.說一說 scrapy 的工作流程
141.scrapy 的去重原理
1.需要將dont_filter設置為False開啟去重,默認是False,開啟去重;
2.對於每一個url的請求,調度器都會根據請求得相關信息加密得到一個指紋信息,並且將指紋信息和set()集合中的指紋信息進行比對,如果set()集合中已經存在這個數據,就不在將這個Request放入隊列中。如果set()集合中沒有存在這個加密后的數據,就將這個Request對象放入隊列中,等待被調度
142.scrapy 中間件有幾種類,你用過哪些中間件
scrapy自帶兩種中間件,下載器中間件(DownloaderMiddleware),爬蟲中間件(spiderMiddleware)
DownloaderMiddleware的作用是在request之前或者response之后,對spider進行配置處理,例如動態更換ip,更換user-agent,更換cookie等
Spider中間件是介入到Scrapy的spider處理機制的鈎子框架,您可以添加代碼來處理發送給 Spiders 的response及spider產生的item和request。
143.你寫爬蟲的時候都遇到過什么反爬蟲措施,你是怎么解決的?
可以看看這篇博文https://blog.csdn.net/weixin_33768481/article/details/87273454
144.為什么會用到代理?
單一ip頻繁重復請求同一個網站會被封掉
145.代理失效了怎么處理?
構建代理池,動態更換ip
146.列出你知道 header 的內容以及信息
user-agent
referer
content-type
content-length
.....
詳情可以看這篇https://kb.cnblogs.com/page/92320/
147.說一說打開瀏覽器訪問 www.baidu.com 獲取到結果,整個流程。
可參考這篇博文https://www.jianshu.com/p/d616d887953a
148.爬取速度過快出現了驗證碼怎么處理
比較簡單的驗證碼,可以用Python的PIL庫(from PIL import Image),tesserocr模塊;
比較復雜的話可以引入機器學習模型,但是成本會比較高,最好還是使用高質量的代理ip,避免觸發驗證碼。
149.scrapy 和 scrapy-redis 有什么區別?為什么選擇 redis 數據庫?
1) scrapy是一個Python爬蟲框架,爬取效率極高,具有高度定制性,但是不支持分布式。而scrapy-redis一套基於redis數據庫、運行在scrapy框架之上的組件,可以讓scrapy支持分布式策略,Slaver端共享Master端redis數據庫里的item隊列、請求隊列和請求指紋集合。
2) 為什么選擇redis數據庫,因為redis支持主從同步,而且數據都是緩存在內存中的,所以基於redis的分布式爬蟲,對請求和數據的高頻讀取效率非常高。
150.分布式爬蟲主要解決什么問題
1)ip
2)帶寬
3)cpu
4)io
151.寫爬蟲是用多進程好?還是多線程好? 為什么?
IO密集型代碼(文件處理、網絡爬蟲等),多線程能夠有效提升效率(單線程下有IO操作會進行IO等待,造成不必要的時間浪費,而開啟多線程能在線程A等待時,自動切換到線程B,可以不浪費CPU的資源,從而能提升程序執行效率)。在實際的數據采集過程中,既考慮網速和響應的問題,也需要考慮自身機器的硬件情況,來設置多進程或多線程
152.解析網頁的解析器使用最多的是哪幾個
xpath,css-selector,beautifulSoup
153.需要登錄的網頁,如何解決同時限制 ip,cookie,session(其中有一些是動態生成的)在不使用動態爬取的情況下?
解決限制IP可以使用代理IP地址池、服務器;
不適用動態爬取的情況下可以使用反編譯JS文件獲取相應的文件,或者換用其他平台(比如手機端)看看是否可以獲取相應的json文件
154.驗證碼的解決
圖形驗證碼:干擾、雜色不是特別多的圖片可以使用開源庫Tesseract進行識別,太過復雜的需要借助第三方打碼平台
點擊和拖動滑塊驗證碼可以借助selenium、無圖形界面瀏覽器(chromedirver或者phantomjs)和pillow包來模擬人的點擊和滑動操作,pillow可以根據色差識別需要滑動的位置
155.使用最多的數據庫(mysql,mongodb,redis 等),對他的理解?
這個自由發揮了,多看下MySQL和MongoDB的使用,了解Redis的基本數據結構
網絡編程
156.TCP 和 UDP 的區別?
TCP協議和UDP協議特性區別總結:
1. TCP協議在傳送數據段的時候要給段標號;UDP協議不
2. TCP協議可靠;UDP協議不可靠
3. TCP協議是面向連接;UDP協議采用無連接
4. TCP協議負載較高,采用虛電路;UDP采用無連接
5. TCP協議的發送方要確認接收方是否收到數據段(3次握手協議)
6. TCP協議采用窗口技術和流控制
157.簡要介紹三次握手和四次揮手
https://mp.weixin.qq.com/s/jLkhjM7wOpZuWgJdAXis1A
這篇博文講的不錯,可以參考一下
另外,time_wait狀態的作用在面試中也是常問的,可以參考下面這篇,https://www.iteblog.com/archives/169.html
158.什么是粘包? socket 中造成粘包的原因是什么? 哪些情況會發生粘包現象?
基於TCP的socket編程中,發送端為了將多個發往接收端的包,更有效的發到對方,使用了優化方法(Nagle算法),將多次間隔較小、數據量小的數據包,合並成一個大的數據包發送(把發送端的緩沖區填滿一次性發送)
造成粘包的原因:
1 發送端需要等緩沖區滿才發送出去,造成粘包
2 接收方不及時接收緩沖區的包,造成多個包接收
並發
159.舉例說明 conccurent.future 的中線程池的用法
concurrent.futures模塊的基礎是Exectuor,Executor是一個抽象類,它不能被直接使用。但是它提供的兩個子類ThreadPoolExecutor和ProcessPoolExecutor卻是非常有用,顧名思義兩者分別被用來創建線程池和進程池的代碼。我們可以將相應的tasks直接放入線程池/進程池,不需要維護Queue來操心死鎖的問題,線程池/進程池會自動幫我們調度。
example1.py
from
concurrent.futures
import
ThreadPoolExecutor
import
time
def
return_future_result(message):
time.sleep(2)
return
message
pool
=
ThreadPoolExecutor(max_workers=2) #
創建一個最大可容納2個task的線程池
future1
=
pool.submit(return_future_result,
("hello")) #
往線程池里面加入一個task
future2
=
pool.submit(return_future_result,
("world")) #
往線程池里面加入一個task
print(future1.done()) #
判斷task1是否結束
time.sleep(3)
print(future2.done()) #
判斷task2是否結束
print(future1.result()) #
查看task1返回的結果
print(future2.result()) #
查看task2返回的結果
160.說一說多線程,多進程和協程的區別。
這個問題雖然被問爛了,但是還是可能會問,不過網上都是答案,搜搜看下就行
161.簡述 GIL
在CPython解釋器中,全局解釋鎖GIL是在於執行Python字節碼時為了保護訪問Python對象而阻止多個線程執行的一把互斥鎖。這把鎖的存在在主要是因為CPython解釋器的內存管理不是線程安全的。然而直到今天GIL依舊存在,現在的很多功能已經習慣於依賴它作為執行的保證
由於GIL的存在,當線程被操作系統喚醒后,必須拿到GIL鎖后才能執行代碼,也就是說同一時刻永遠只有一個線程在執行,這就導致如果我們的程序是CPU密集運算型的任務,那么使用Python多線程是不能提高效率的
162.進程之間如何通信
共享內存,信號量,互斥鎖,消息隊列等
163.IO 多路復用的作用?
I/O多路復用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒后自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而異步I/O則無需自己負責進行讀寫,異步I/O的實現會負責把數據從內核拷貝到用戶空間
164.select、poll、epoll 模型的區別?
可參考這篇博文,https://www.cnblogs.com/Anker/p/3265058.html
165.什么是並發和並行?
並發的實質是一個物理CPU(也可以多個物理CPU) 在若干道程序之間多路復用,並發性是對有限物理資源強制行使多用戶共享以提高效率
並行”指兩個或兩個以上事件或活動在同一時刻發生。在多道程序環境下,並行性使多個程序同一時刻可在不同CPU上同時執行
167.解釋什么是異步非阻塞?
當一個異步過程調用發出后,調用者不會立刻得到結果。
實際處理這個調用的部件是在調用發出后,
通過狀態、通知來通知調用者,或通過回調函數處理這個調用
非阻塞的意思是,不能立刻得到結果之前,該函數不會阻塞當前線程,而會立刻返回
168.threading.local 的作用?
Python提供了 threading.local 類,將這個類實例化得到一個全局對象,但是不同的線程使用這個對象存儲的數據其它線程不可見(本質上就是不同的線程使用這個對象時為其創建一個獨立的字典)
Git 面試題
169.說說你知道的 git 命令
git clone;
git push
git status;
git commit;
...
170.git 如何查看某次提交修改的內容x
知道commit id的情況下:
1. 獲取commit id
git log
2. 查看commit內容
git show commit_id
查看最近n次提交的修改
git log -p -n
指定n為1則可以查看最近一次修改的內容