1. 程式人生 > 程式設計 >python-面試通關寶典-有面Python開發方向的,看這一個repo就夠啦?

python-面試通關寶典-有面Python開發方向的,看這一個repo就夠啦?


之前看過一些面試題,有些有答案有些不完整,零零散散的,這回整理了一下,把答案都重新寫了,能測試通過那種(有錯的話還請大家指出,一起完善這個repo)

github.com/hantmac/Pyt…

最後還剩幾十道題沒寫完,週末補上?

python-面試通關寶典

秋招的小夥伴,有面Python開發方向的,看這一個repo就夠啦?

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 &emsp;&emsp;&emsp;&emsp;range py3:range 統一使用range,Python3中range的機制也進行修改並提高了大資料集生成效率 7:在包的知識點裡 包:一群模組檔案的集合 + __init__ 區別:py2 : 必須有__init__ &emsp;&emsp;&emsp;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,3" 變成 ["1","2","3"]

s = "1,3"
s.split(',')
複製程式碼

32.給定兩個 list,A 和 B,找出相同元素和不同元素

# 最直接的方法
list_a = [1,4,5,6]
list_b = [2,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,6]
set1 = set(list_a)
set2 = set(list_b)
# 相同元素
print(set1&set2)
# 不同元素
print(set1^set2)
複製程式碼

33.[[1,[3,4],[5,6]]一行程式碼展開該列表,得出[1,4,5,6]

mm = [[1,2],[3,4],[5,6]]
[j for a in mm for j in a]
複製程式碼

34.合併列表[1,7,9]和[2,6,8]

a = [1,7,9]
b = [2,6,8]
# 方法1
a.extend(b)
# 方法2
a[0:0] = b
複製程式碼

35.如何打亂一個列表的元素?

import random
a = [1,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,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,[4,7],8)
a[2] = 2
報錯,元組是不可變物件,不支援修改
複製程式碼

48.下面的程式碼輸出的結果是什麼?

a = (1,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,30
    the_date = datetime.datetime(y,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): 刪除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',''))
True
>>> any((0,False,''))
False
>>> any([])
False
>>> any(())
False
>>> all(['a','c','d'])  #列表list,
True
>>> all(['a','d'])  #列表list,元素都不為空或0
True
>>> all(['a','d'])  #列表list,存在一個為空的元素
False
>>> all([0,3])  #列表list,存在一個為0的元素
False
>>> all(('a','d'))  #元組tuple,元素都不為空或0
True
>>> all(('a','d'))  #元組tuple,存在一個為空的元素
False
>>> all((0,3))  #元組tuple,存在一個為0的元素
False
>>> all([]) # 空列表
True
>>> all(()) # 空元組
True
>>> #注意:空元組、空列表返回值為True,這裡要特別注意
複製程式碼

84.filter 方法求出列表所有奇數並構造新列表,a = [1,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','c']
for index,item in a:
    print(index,item)
複製程式碼

97.列舉 5 個 Python 中的標準模組

json,re,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,3]
ys = xs # 淺拷貝
zs = deepcopy(xs) # 深拷貝
xs[2][0] = 5
print(ys)
[1,3]
print(xs)
[1,3]

print(zs)
[1,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,3]
BSet = {1,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,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,**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,4]
    print(find_duplicate(array))
複製程式碼

127.找出列表中的單個數字

a = ['a','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,6]
    arr1 = [1,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,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','c': {'d'},'d': {'e','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,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)
        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,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則可以檢視最近一次修改的內容
複製程式碼