萬字長文Python面試題,建議先收藏

語言: CN / TW / HK

精心整理的 Python 相關的基礎知識,用於面試,或者平時複習,都是很好的!廢話不多說,直接開搞

由於文章過長,蘿蔔哥也貼心的把文章整理成了PDF文件,在文末檢視獲取方式

[TOC]

基礎篇

1. 為什麼學習 Python

Python 語言簡單易懂,上手容易,隨著 AI 風潮,越來越火

2. 解釋型和編譯型語言的區別

編譯型語言:把做好的源程式全部編譯成二進位制的可執行程式。然後,可直接執行這個程式。如:C,C++

解釋型語言:把做好的源程式翻譯一句,然後執行一句,直至結束!如:Python,

(Java 有些特殊,java程式也需要編譯,但是沒有直接編譯稱為機器語言,而是編譯稱為位元組碼,然後用解釋方式執行位元組碼。)

3. 簡述下 Python 中的字串、列表、元組和字典

字串(str):字串是用引號括起來的任意文字,是程式語言中最常用的資料型別

列表(list):列表是有序的集合,可以向其中新增或刪除元素

元組(tuple):元組也是有序集合,但是是無法修改的。即元組是不可變的

字典(dict):字典是無序的集合,是由 key-value 組成的

集合(set):是一組 key 的集合,每個元素都是唯一,不重複且無序的

4. 簡述上述資料型別的常用方法

字串:

  1. 切片

    mystr='luobodazahui'
    mystr[1:3]

    output

    'uo'
  2. format

    mystr2 = "welcome to luobodazahui, dear {name}"
    mystr2.format(name="baby")

    output

    'welcome to luobodazahui, dear baby'
  3. join

可以用來連線字串,將字串、元組、列表中的元素以指定的字元(分隔符)連線生成一個新的字串

mylist = ['luo', 'bo', 'da', 'za', 'hui']
mystr3 = '-'.join(mylist)
print(mystr3)

outout

'luo-bo-da-za-hui'
  1. replace

String.replace(old,new,count) 將字串中的 old 字元替換為 New 字元,count 為替換的個數

mystr4 = 'luobodazahui-haha'
print(mystr4.replace('haha', 'good'))

output

luobodazahui-good
  1. split

切割字串,得到一個列表

mystr5 = 'luobo,dazahui good'
# 以空格分割
print(mystr5.split())
# 以h分割
print(mystr5.split('h'))
# 以逗號分割
print(mystr5.split(','))

output

['luobo,dazahui', 'good']
['luobo,daza', 'ui good']
['luobo', 'dazahui good']

列表:

  1. 切片
    同字串
  2. append 和 extend

    向列表中國新增元素

    mylist1 = [1, 2]
    mylist2 = [3, 4]
    mylist3 = [1, 2]
    mylist1.append(mylist2)
    print(mylist1)
    mylist3.extend(mylist2)
    print(mylist3)

    outout

    [1, 2, [3, 4]]
    [1, 2, 3, 4]
  3. 刪除元素

    del:根據下標進行刪除

    pop:刪除最後一個元素

    remove:根據元素的值進行刪除

    mylist4 = ['a', 'b', 'c', 'd']
    del mylist4[0]
    print(mylist4)
    mylist4.pop()
    print(mylist4)
    mylist4.remove('c')
    print(mylist4)

    output

    ['b', 'c', 'd']
    ['b', 'c']
    ['b']
  4. 元素排序

    sort:是將list按特定順序重新排列,預設為由小到大,引數 reverse=True 可改為倒序,由大到小。

    reverse:是將list逆置

    mylist5 = [1, 5, 2, 3, 4]
    mylist5.sort()
    print(mylist5)
    mylist5.reverse()
    print(mylist5)

    output

    [1, 2, 3, 4, 5]
    [5, 4, 3, 2, 1]

    字典:

  5. 清空字典

    dict.clear()

    dict1 = {'key1':1, 'key2':2}
    dict1.clear()
    print(dict1)

    output

    {}
  6. 指定刪除

    使用 pop 方法來指定刪除字典中的某一項

    dict1 = {'key1':1, 'key2':2}
    d1 = dict1.pop('key1')
    print(d1)
    print(dict1)

    output

    1
    {'key2': 2}
  7. 遍歷字典

    dict2 = {'key1':1, 'key2':2}
    mykey = [key for key in dict2]
    print(mykey)
    myvalue = [value for value in dict2.values()]
    print(myvalue)
    key_value = [(k, v) for k, v in dict2.items() ]
    print(key_value)

    output

    ['key1', 'key2']
    [1, 2]
    [('key1', 1), ('key2', 2)]
  8. fromkeys

    用於建立一個新字典,以序列中元素做字典的鍵,value 為字典所有鍵對應的初始值

    keys = ['zhangfei', 'guanyu', 'liubei', 'zhaoyun']
    dict.fromkeys(keys, 0)

    output

    {'zhangfei': 0, 'guanyu': 0, 'liubei': 0, 'zhaoyun': 0}

    5. 簡述 Python 中的字串編碼

    計算機在最初的設計中,採用了8個位元(bit)作為一個位元組(byte)的方式。一個位元組能表示的最大的整數就是255(二進位制11111111=十進位制255),如果要表示更大的整數,就必須用更多的位元組

最早,計算機只有 ASCII 編碼,即只包含大小寫英文字母、數字和一些符號,這些對於其他語言,如中文,日文顯然是不夠用的。後來又發明了Unicode,Unicode把所有語言都統一到一套編碼裡,這樣就不會再有亂碼問題了。當需要儲存到硬碟或者需要傳輸的時候,就轉換為UTF-8編碼。UTF-8 是隸屬於 Unicode 的可變長的編碼方式

在 Python 中,以 Unicode 方式編碼的字串,可以使用 encode() 方法來編碼成指定的 bytes,也可以通過 decode() 方法來把 bytes 編碼成字串 encode

"中文".encode('utf-8')

output

b'\xe4\xb8\xad\xe6\x96\x87'

decode

b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')

output

'中文'

6.一行程式碼實現數值交換

1a = 1
2b = 2
3a, b = b, a
4print(a, b)

output

7. is 和 == 的區別

先來看個例子

c = d = [1,2]
e = [1,2]
print(c is d)
print(c == d)
print(c is e)
print(c == e)

output

True
True
False
True

== 是比較操作符,只是判斷物件的值(value)是否一致,而 is 則判斷的是物件之間的身份(記憶體地址)是否一致。物件的身份,可以通過 id() 方法來檢視

id(c)
id(d)
id(e)

output

可以看出,只有 id 一致時,is 比較才會返回 True,而當 value 一致時,== 比較就會返回 True

8.Python 函式中的引數型別

位置引數,預設引數,可變引數,關鍵字引數

9. *arg**kwarg 作用

允許我們在呼叫函式的時候傳入多個實參

def test(*arg, **kwarg):
    if arg:
        print("arg:", arg)
    if kwarg:
        print("kearg:", kwarg)
test('ni', 'hao', key='world')

output

arg: ('ni', 'hao')
kearg: {'key': 'world'}

可以看出,

*arg 會把位置引數轉化為 tuple

**kwarg 會把關鍵字引數轉化為 dict

10.一行程式碼實現1-100之和

sum(range(1, 101))

11.獲取當前時間

import time
import datetime
print(datetime.datetime.now())
print(time.strftime('%Y-%m-%d %H:%M:%S'))

output

2019-06-07 18:12:11.165330
2019-06-07 18:12:11

12.PEP8 規範

簡單列舉10條:

_

13.Python 的深淺拷貝

淺拷貝

import copy
list1 = [1, 2, 3, [1, 2]]
list2 = copy.copy(list1)
list2.append('a')
list2[3].append('a')
print(list1, list2)

output

[1, 2, 3, [1, 2, 'a']] [1, 2, 3, [1, 2, 'a'], 'a']

能夠看出,淺拷貝只成功”獨立“拷貝了列表的外層,而列表的內層列表,還是共享的

深拷貝

import copy
list1 = [1, 2, 3, [1, 2]]
list3 = copy.deepcopy(list1)
list3.append('a')
list3[3].append('a')
print(list1, list3)

output

[1, 2, 3, [1, 2]] [1, 2, 3, [1, 2, 'a'], 'a']

深拷貝使得兩個列表完全獨立開來,每一個列表的操作,都不會影響到另一個

14.檢視下面程式碼的輸出

def num():
    return [lambda x:i*x for i in range(4)]
print([m(1) for m in num()])

output

[3, 3, 3, 3]

通過執行結果,可以看出 i 的取值為3,很神奇

15.可變型別與不可變型別

可變資料型別:list、dict、set

不可變資料型別:int/float、str、tuple

16.列印九九乘法表

for i in range(1, 10):
    for j in range(1, i+1):
        print("%s*%s=%s " %(i, j, i*j), end="")
    print()

output

1*1=1 
2*1=2 2*2=4 
3*1=3 3*2=6 3*3=9 
4*1=4 4*2=8 4*3=12 4*4=16 
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25 
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36 
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49 
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64 
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81

print 函式,預設是會換行的,其有一個預設引數 end,如果像例子中,我們把 end 引數顯示的置為"",那麼 print 函式執行完後,就不會換行了,這樣就達到了九九乘法表的效果了

17.filter、map、reduce 的作用

filter 函式用於過濾序列,它接收一個函式和一個序列,把函式作用在序列的每個元素上,然後根據返回值是True還是False決定保留還是丟棄該元素

mylist = [1, 2, 3, 4, 5, 6, 7, 8, 9]
list(filter(lambda x: x%2 == 1, mylist))

output

[1, 3, 5, 7, 9]

保留奇數列表

map 函式傳入一個函式和一個序列,並把函式作用到序列的每個元素上,返回一個可迭代物件

mylist = [1, 2, 3, 4, 5, 6, 7, 8, 9]
list(map(lambda x: x*2, mylist))

output

[2, 4, 6, 8, 10, 12, 14, 16, 18]

reduce 函式用於遞迴計算,同樣需要傳入一個函式和一個序列,並把函式和序列元素的計算結果與下一個元素進行計算

from functools import reduce
reduce(lambda x, y: x+y, range(101))

output

可以看出,上面的三個函式與匿名函式相結合使用,可以寫出強大簡潔的程式碼

18.re 的 match 和 search 區別

match()函式只檢測要匹配的字元是不是在 string 的開始位置匹配,search()會掃描整個 string 查詢匹配

19.面向物件中 __new____init__ 區別

__new__ 是在例項建立之前被呼叫的,因為它的任務就是建立例項然後返回該例項物件,是個靜態方法

__init__ 是當例項物件建立完成後被呼叫的,然後設定物件屬性的一些初始值,通常用在初始化一個類例項的時候,是一個例項方法

1、 __new__ 至少要有一個引數 cls,代表當前類,此引數在例項化時由 Python 直譯器自動識別

2、 __new__ 必須要有返回值,返回例項化出來的例項,這點在自己實現 __new__ 時要特別注意,可以 return 父類(通過 super(當前類名, cls)) __new__ 出來的例項,或者直接是 object 的 __new__ 出來的例項

3、 __init__ 有一個引數 self,就是這個 __new__ 返回的例項, __init____new__ 的基礎上可以完成一些其它初始化的動作, __init__ 不需要返回值

4、如果 __new__ 建立的是當前類的例項,會自動呼叫 __init__ 函式,通過 return 語句裡面呼叫的 __new__ 函式的第一個引數是 cls 來保證是當前類例項,如果是其他類的類名,那麼實際建立返回的就是其他類的例項,其實就不會呼叫當前類的 __init__ 函式,也不會呼叫其他類的 __init__ 函式

20.三元運算規則

a, b = 1, 2
# 若果 a>b 成立  就輸出  a-b  否則 a+b
h = a-b if a>b else a+b

output

21.生成隨機數

print(random.random())
print(random.randint(1, 100))
print(random.uniform(1,5))

output

0.03765019937131564
18
1.8458555362279228

22.zip 函式用法

zip() 函式將可迭代的物件作為引數,將物件中對應的元素打包成一個個元組,然後返回由這些元組組成的列表

list1 = ['zhangfei', 'guanyu', 'liubei', 'zhaoyun']
list2 = [0, 3, 2, 4]
list(zip(list1, list2))

output

[('zhangfei', 0), ('guanyu', 3), ('liubei', 2), ('zhaoyun', 4)]

23.range 和 xrange 的區別

range([start,] stop[, step]),根據start與stop指定的範圍以及step設定的步長,生成一個序列

而 xrange 生成一個生成器,可以很大的節約記憶體

24.with 方法開啟檔案的作用

開檔案在進行讀寫的時候可能會出現一些異常狀況,如果按照常規的 f.open 寫法,我們需要 try,except,finally,做異常判斷,並且檔案最終不管遇到什麼情況,都要執行 finally f.close() 關閉檔案,with 方法幫我們實現了 finally 中 f.close

25.什麼是正則的貪婪匹配

Python 中預設是貪婪匹配模式

貪婪模式:正則表示式一般趨向於最大長度匹配

非貪婪模式:在整個表示式匹配成功的前提下,儘可能少的匹配

26.為什麼不建議函式的預設引數傳入可變物件

例如:

def test(L=[]):
    L.append('test')
    print(L)

output

test() # ['test']
test() # ['test', 'test']

預設引數是一個列表,是可變物件[],Python 在函式定義的時候,預設引數 L 的值就被計算出來了,是[],每次呼叫函式,如果 L 的值變了,那麼下次呼叫時,預設引數的值就已經不再是[]了

27.字串轉列表

mystr = '1,2,3'
mystr.split(',')

output

['1', '2', '3']

28.字串轉整數

mylist = ['1', '2', '3']
list(map(lambda x: int(x), mylist))

output

[1, 2, 3]

29.刪除列表中的重複值

mylist = [1, 2, 3, 4, 5, 5]
list(set(mylist))

30.字串單詞統計

from collections import Counter
mystr = 'sdfsfsfsdfsd,were,hrhrgege.sdfwe!sfsdfs'
Counter(mystr)
output
 Counter({'s': 9,
          'd': 5,
          'f': 7,
          ',': 2,
          'w': 2,
          'e': 5,
          'r': 3,
          'h': 2,
          'g': 2,
         '.': 1,
         '!': 1})

31.列表推導,求奇偶數

[x for x in range(10) if x%2 == 1]

output

[1, 3, 5, 7, 9]

32.一行程式碼展開列表

list1 = [[1,2],[3,4],[5,6]]
[j for i in list1 for j in i]

output

[1, 2, 3, 4, 5, 6]

33.實現二分法查詢函式

二分查詢演算法也稱折半查詢,基本思想就是折半,對比大小後再折半查詢,必須是有序序列才可以使用二分查詢

遞迴演算法

def binary_search(data, item):
     # 遞迴
     n = len(data)
     if n > 0:
         mid = n // 2
         if data[mid] == item:
             return True
         elif data[mid] > item:
             return binary_search(data[:mid], item)
        else:
            return binary_search(data[mid+1:], item)
    return False
list1 = [1,4,5,66,78,99,100,101,233,250,444,890]
binary_search(list1, 999)

非遞迴演算法

def binary_search(data, item):
     # 非遞迴
     n = len(data)
     first = 0
     last = n - 1
     while first <= last:
         mid = (first + last)//2
         if data[mid] == item:
             return True
        elif data[mid] > item:
            last = mid - 1
        else:
            first = mid + 1
    return False
list1 = [1,4,5,66,78,99,100,101,233,250,444,890]
binary_search(list1, 99)

34.字典和 json 轉換

字典轉 json

import json
dict1 = {'zhangfei':1, "liubei":2, "guanyu": 4, "zhaoyun":3}
myjson = json.dumps(dict1)
myjson
output
'{"zhangfei": 1, "liubei": 2, "guanyu": 4, "zhaoyun": 3}'
json 轉字典
mydict = json.loads(myjson)
mydict

output

{'zhangfei': 1, 'liubei': 2, 'guanyu': 4, 'zhaoyun': 3}

35.列表推導式、字典推導式和生成器

import random
td_list=[i for i in range(10)]
print("列表推導式", td_list, type(td_list))
ge_list = (i for i in range(10))
print("生成器", ge_list)
dic = {k:random.randint(4, 9)for k in ["a", "b", "c", "d"]}
print("字典推導式",dic,type(dic))

output

列表推導式 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] <class 'list'>
生成器 <generator object <genexpr> at 0x0139F070>
字典推導式 {'a': 6, 'b': 5, 'c': 8, 'd': 9} <class 'dict'>

36.簡述 read、readline、readlines 的區別

read 讀取整個檔案

readline 讀取下一行,使用生成器方法

readlines 讀取整個檔案到一個迭代器以供我們遍歷

37.打亂一個列表

list2 = [1, 2, 3, 4, 5, 6]
random.shuffle(list2)
print(list2)

output

[4, 6, 5, 1, 2, 3]

38.反轉字串

str1 = 'luobodazahui'
str1[::-1]

output

'iuhazadoboul'

39.單下劃線和雙下劃線的作用

__foo__ :一種約定,Python 內部的名字,用來區別其他使用者自定義的命名,以防衝突,就是例如 __init__() , __del__() , __call__() 些特殊方法

_foo :一種約定,用來指定變數私有。不能用 from module import * 匯入,其他方面和公有變數一樣訪問

__foo :這個有真正的意義:解析器用 _classname__foo 來代替這個名字,以區別和其他類相同的命名,它無法直接像公有成員一樣隨便訪問,通過物件名 ._ 類名 __xxx 這樣的方式可以訪問

40.新式類和舊式類

a. 在 python 裡凡是繼承了 object 的類,都是新式類

b. Python3 裡只有新式類

c. Python2 裡面繼承 object 的是新式類,沒有寫父類的是經典類

d. 經典類目前在 Python 裡基本沒有應用

41.Python 面向物件中的繼承有什麼特點

a. 同時支援單繼承與多繼承,當只有一個父類時為單繼承,當存在多個父類時為多繼承

b. 子類會繼承父類所有的屬性和方法,子類也可以覆蓋父類同名的變數和方法

c. 在繼承中基類的構造( __init__() )方法不會被自動呼叫,它需要在其派生類的構造中專門呼叫

d. 在呼叫基類的方法時,需要加上基類的類名字首,且需要帶上 self 引數變數。區別於在類中呼叫普通函式時並不需要帶上 self 引數

42.super 函式的作用

super() 函式是用於呼叫父類(超類)的一個方法

class A():
     def funcA(self):
         print("this is func A")
 class B(A):
     def funcA_in_B(self):
         super(B, self).funcA()
 
     def funcC(self):
         print("this is func C")

ins = B()
ins.funcA_in_B()
ins.funcC()

output

this is func A
this is func C

43.類中的各種函式

主要分為例項方法、類方法和靜態方法

例項方法

定義:第一個引數必須是例項物件,該引數名一般約定為“self”,通過它來傳遞例項的屬性和方法(也可以傳類的屬性和方法)

呼叫:只能由例項物件呼叫

類方法

定義:使用裝飾器@classmethod。第一個引數必須是當前類物件,該引數名一般約定為“cls”,通過它來傳遞類的屬性和方法(不能傳例項的屬性和方法)

呼叫:例項物件和類物件都可以呼叫

靜態方法

定義:使用裝飾器@staticmethod。引數隨意,沒有“self”和“cls”引數,但是方法體中不能使用類或例項的任何屬性和方法

呼叫:例項物件和類物件都可以呼叫

靜態方法是類中的函式,不需要例項。靜態方法主要是用來存放邏輯性的程式碼,主要是一些邏輯屬於類,但是和類本身沒有互動。即在靜態方法中,不會涉及到類中的方法和屬性的操作。可以理解為將靜態方法存在此類的名稱空間中

類方法是將類本身作為物件進行操作的方法。他和靜態方法的區別在於:不管這個方式是從例項呼叫還是從類呼叫,它都用第一個引數把類傳遞過來

44.如何判斷是函式還是方法

與類和例項無繫結關係的 function 都屬於函式(function)

與類和例項有繫結關係的 function 都屬於方法(method)

普通函式:

def func1():
    pass
print(func1)
output
<function func1 at 0x01379348>
類中的函式:
 class People(object):
     def func2(self):
         pass
     @staticmethod
     def func3():
         pass
     @classmethod
     def func4(cls):
         pass
people = People()
print(people.func2)
print(people.func3)
print(people.func4)

output

<bound method People.func2 of <__main__.People object at 0x013B8C90>>
<function People.func3 at 0x01379390>
<bound method People.func4 of <class '__main__.People'>>

45.isinstance 的作用以及與 type()的區別

isinstance() 函式來判斷一個物件是否是一個已知的型別,類似 type()

區別:

type() 不會認為子類是一種父類型別,不考慮繼承關係

isinstance() 會認為子類是一種父類型別,考慮繼承關係

class A(object):
     pass
 class B(A):
     pass
 a = A()
 b = B()
 print(isinstance(a, A))
 print(isinstance(b, A))
 print(type(a) == A)
print(type(b) == A)

output

True
True
True
False

46.單例模式與工廠模式

單例模式:主要目的是確保某一個類只有一個例項存在

工廠模式:包涵一個超類,這個超類提供一個抽象化的介面來建立一個特定型別的物件,而不是決定哪個物件可以被建立

47.檢視目錄下的所有檔案

import os
print(os.listdir('.'))

48.計算1到5組成的互不重複的三位數

# 1到5組成的互不重複的三位數
 k = 0
 for i in range(1, 6):
     for j in range(1, 6):
         for z in range(1, 6):
             if (i != j) and (i != z) and (j != z):
                 k += 1
                 if k%6:
                     print("%s%s%s" %(i, j, z), end="|")
                else:
                    print("%s%s%s" %(i, j, z))

output

123|124|125|132|134|135
 142|143|145|152|153|154
 213|214|215|231|234|235
 241|243|245|251|253|254
 312|314|315|321|324|325
 341|342|345|351|352|354
 412|413|415|421|423|425
 431|432|435|451|452|453
 512|513|514|521|523|524
531|532|534|541|542|543

49.去除字串首尾空格

str1 = "   hello nihao    "
str1.strip()

output

'hello nihao'

50.去除字串中間的空格

str2 = "hello you are good"
print(str2.replace(" ", ""))
"".join(str2.split(" "))

output

helloyouaregood
'helloyouaregood'

51. 字串格式化方式

  1. 使用 % 操作符

    print("This is for %s" % "Python")
    print("This is for %s, and %s" %("Python", "You"))

    output

    This is for Python
    This is for Python, and You
  2. str.format

在 Python3 中,引入了這個新的字串格式化方法

print("This is my {}".format("chat"))
print("This is {name}, hope you can {do}".format(name="zhouluob", do="like"))

output

This is my chat
This is zhouluob, hope you can like
  1. f-strings

在 Python3-6 中,引入了這個新的字串格式化方法

name = "luobodazahui"
print(f"hello {name}")

output

hello luobodazahui

一個複雜些的例子:

def mytest(name, age):
    return f"hello {name}, you are {age} years old!"
people = mytest("luobo", 20)
print(people)

output

hello luobo, you are 20 years old!

52. 將"hello world"轉換為首字母大寫"Hello World"(不使用 title 函式)

str1 = "hello world"
print(str1.title())
" ".join(list(map(lambda x: x.capitalize(), str1.split(" "))))

output

Hello World
'Hello World'

53. 一行程式碼轉換列表中的整數為字串

如:[1, 2, 3] -> ["1", "2", "3"]

list1 = [1, 2, 3]
list(map(lambda x: str(x), list1))

output

['1', '2', '3']

54. 合併兩個元組到字典

如:("zhangfei", "guanyu"),(66, 80) -> {'zhangfei': 66, 'guanyu': 80}

a = ("zhangfei", "guanyu")
b = (66, 80)
dict(zip(a,b))

output

{'zhangfei': 66, 'guanyu': 80}

55. 給出如下程式碼的輸入,並簡單解釋

例子1:

a = (1,2,3,[4,5,6,7],8)
a[3] = 2

output

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-35-59469d550eb0> in <module>
      1 a = (1,2,3,[4,5,6,7],8)
----> 2 a[3] = 2
      3 #a

TypeError: 'tuple' object does not support item assignment

例子2:

a = (1,2,3,[4,5,6,7],8)
a[3][2] = 2
a

output

(1, 2, 3, [4, 5, 2, 7], 8)

從例子1的報錯中也可以看出,tuple 是不可變型別,不能改變 tuple 裡的元素,例子2中,list 是可變型別,改變其元素是允許的

56. Python 中的反射

反射就是通過字串的形式,匯入模組;通過字串的形式,去模組尋找指定函式,並執行。利用字串的形式去物件(模組)中操作(查詢/獲取/刪除/新增)成員,一種基於字串的事件驅動!

簡單理解就是用來判斷某個字串是什麼,是變數還是方法

class NewClass(object):
     def __init__(self, name, male):
         self.name = name
         self.male = male
     def myname(self):
         print(f'My name is {self.name}')
     def mymale(self):
         print(f'I am a {self.male}')
 people = NewClass('luobo', 'boy')
print(hasattr(people, 'name'))
print(getattr(people, 'name'))
setattr(people, 'male', 'girl')
print(getattr(people, 'male'))

output

True
luobo
girl

getattr,hasattr,setattr,delattr 對模組的修改都在記憶體中進行,並不會影響檔案中真實內容

57. 實現一個簡單的 API

使用 flask 構造 web 伺服器

from flask import Flask, request
 app = Flask(__name__)
 
 @app.route('/', methods=['POST'])
 def simple_api():
     result = request.get_json()
     return result
 
 
if __name__ == "__main__":
    app.run()

58. metaclass 元類

類與例項:

首先定義類以後,就可以根據這個類創建出例項,所以:先定義類,然後建立例項

類與元類:

先定義元類, 根據 metaclass 創建出類,所以:先定義 metaclass,然後建立類

class MyMetaclass(type):
    def __new__(cls, class_name, class_parents, class_attr):
        class_attr['print'] = "this is my metaclass's subclass %s" %class_name
        return type.__new__(cls, class_name, class_parents, class_attr)
class MyNewclass(object, metaclass=MyMetaclass):
    pass

myinstance = MyNewclass()
myinstance.print

output

"this is my metaclass's subclass MyNewclass"

59. sort 和 sorted 的區別

sort() 是可變物件列表(list)的方法,無引數,無返回值,sort() 會改變可變物件

dict1 = {'test1':1, 'test2':2}
list1 = [2, 1, 3]
print(list1.sort())
list1

output

None
[1, 2, 3]

sorted() 是產生一個新的物件。sorted(L) 返回一個排序後的L,不改變原始的L,sorted() 適用於任何可迭代容器

dict1 = {'test1':1, 'test2':2}
list1 = [2, 1, 3]
print(sorted(dict1))print(sorted(list1))

output

['test1', 'test2']
[1, 2, 3]

60. Python 中的 GIL

GIL 是 Python 的全域性直譯器鎖,同一程序中假如有多個執行緒執行,一個執行緒在執行 Python 程式的時候會佔用 Python 直譯器(加了一把鎖即 GIL),使該程序內的其他執行緒無法執行,等該執行緒執行完後其他執行緒才能執行。如果執行緒執行過程中遇到耗時操作,則直譯器鎖解開,使其他執行緒執行。所以在多執行緒中,執行緒的執行仍是有先後順序的,並不是同時進行

61. 產生8位隨機密碼

import random
"".join(random.choice(string.printable[:-7]) for i in range(8))

output

'd5^NdNJp'

62. 輸出原始字元

print('hello\nworld')
print(b'hello\nworld')
print(r'hello\nworld')

output

hello
world
b'hello\nworld'
hello\nworld

63. 列表內,字典按照 value 大小排序

list1 = [{'name': 'guanyu', 'age':29},
        {'name': 'zhangfei', 'age': 28},
        {'name': 'liubei', 'age':31}]
sorted(list1, key=lambda x:x['age'])

output

[{'name': 'zhangfei', 'age': 28},
{'name': 'guanyu', 'age': 29},
{'name': 'liubei', 'age': 31}]

64. 簡述 any() 和 all() 方法

all 如果存在 0 Null False 返回 False,否則返回 True;any 如果都是 0,None,False,Null 時,返回 True

print(all([1, 2, 3, 0]))
print(all([1, 2, 3]))
print(any([1, 2, 3, 0]))
print(any([0, None, False]))

output

False
True
True
False

65. 反轉整數

def reverse_int(x):
     if not isinstance(x, int):
         return False
     if -10 < x < 10:
         return x
     tmp = str(x)
     if tmp[0] != '-':
         tmp = tmp[::-1]
         return int(tmp)
    else:
        tmp = tmp[1:][::-1]
        x = int(tmp)
        return -x
reverse_int(-23837)

output

-73832

首先判斷是否是整數,再判斷是否是一位數字,最後再判斷是不是負數

66. 函數語言程式設計

函數語言程式設計是一種抽象程度很高的程式設計正規化,純粹的函數語言程式設計語言編寫的函式沒有變數,因此,任意一個函式,只要輸入是確定的,輸出就是確定的,這種純函式稱之為沒有副作用。而允許使用變數的程式設計語言,由於函式內部的變數狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函式是有副作用的。由於 Python 允許使用變數,因此,Python 不是純函數語言程式設計語言

函數語言程式設計的一個特點就是,允許把函式本身作為引數傳入另一個函式,還允許返回一個函式!

函式作為返回值例子:

def sum(*args):
     def inner_sum():
         tmp = 0
         for i in args:
             tmp += i
         return tmp
     return inner_sum
 mysum = sum(2, 4, 6)
 print(type(mysum))
mysum()

output

<class 'function'>
12

67. 簡述閉包

如果在一個內部函式裡,對在外部作用域(但不是在全域性作用域)的變數進行引用,那麼內部函式就被認為是閉包(closure)

附上函式作用域圖片

閉包特點

1.必須有一個內嵌函式

2.內嵌函式必須引用外部函式中的變數

3.外部函式的返回值必須是內嵌函式

68. 簡述裝飾器

裝飾器是一種特殊的閉包,就是在閉包的基礎上傳遞了一個函式,然後覆蓋原來函式的執行入口,以後呼叫這個函式的時候,就可以額外實現一些功能了

一個列印 log 的例子:

import time
 def log(func):
     def inner_log(*args, **kw):
         print("Call: {}".format(func.__name__))
         return func(*args, **kw)
     return inner_log
 
 
@log
def timer():
    print(time.time())

timer()

output

Call: timer
1560171403.5128365

本質上,decorator就是一個返回函式的高階函式

69. 協程的優點

子程式切換不是執行緒切換,而是由程式自身控制

沒有執行緒切換的開銷,和多執行緒比,執行緒數量越多,協程的效能優勢就越明顯

不需要多執行緒的鎖機制,因為只有一個執行緒,也不存在同時寫變數衝突,在協程中控制共享資源不加鎖

70. 實現一個斐波那契數列

斐波那契數列:

又稱黃金分割數列,指的是這樣一個數列:1、1、2、3、5、8、13、21、34、……在數學上,斐波納契數列以如下被以遞迴的方法定義:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)

生成器法:

def fib(n):
     if n == 0:
         return False
     if not isinstance(n, int) or (abs(n) != n): # 判斷是正整數
         return False
     a, b = 0, 1
     while n:
         a, b = b, a+b
         n -= 1
        yield a

[i for i in fib(10)]

output

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

遞迴法:

def fib(n):
     if n == 0:
         return False
     if not isinstance(n, int) or (abs(n) != n):
         return False
     if n <= 1:
         return n
     return fib(n-1)+ fib(n-2)
 
[fib(i) for i in range(1, 11)]

output

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

71. 正則切分字串

import re
str1 = 'hello world:luobo dazahui'
result = re.split(r":| ", str1)
print(result)

output

['hello', 'world', 'luobo', 'dazahui']

72. yield 用法

yield 是用來生成迭代器的語法,在函式中,如果包含了 yield,那麼這個函式就是一個迭代器。當代碼執行至 yield 時,就會中斷程式碼執行,直到程式呼叫 next() 函式時,才會在上次 yield 的地方繼續執行

def foryield():
    print("start test yield")
    while True:
        result = yield 5
        print("result:", result)
g = foryield()
print(next(g))
print("*"*20)
print(next(g))

output

start test yield
5
********************
result: None
5

可以看到,第一個呼叫 next() 函式,程式只執行到了 "result = yield 5" 這裡,同時由於 yield 中斷了程式,所以 result 也沒有被賦值,所以第二次執行 next() 時,result 是 None

73. 氣泡排序

list1 = [2, 5, 8, 9, 3, 11]
 def paixu(data, reverse=False):
     if not reverse:
         for i in range(len(data) - 1):
             for j in range(len(data) - 1 - i):
                 if data[j] > data[j+1]:
                     data[j], data[j+1] = data[j+1], data[j]
         return data
     else:
        for i in range(len(data) - 1):
            for j in range(len(data) - 1 - i):
                if data[j] < data[j+1]:
                    data[j], data[j+1] = data[j+1], data[j]
        return data

print(paixu(list1, reverse=True))

output

[11, 9, 8, 5, 3, 2]

74. 快速排序

快排的思想:首先任意選取一個數據(通常選用陣列的第一個數)作為關鍵資料,然後將所有比它小的數都放到它前面,所有比它大的數都放到它後面,這個過程稱為一趟快速排序,之後再遞迴排序兩邊的資料

挑選基準值:從數列中挑出一個元素,稱為"基準"(pivot)

分割:重新排序數列,所有比基準值小的元素擺放在基準前面,所有比基準值大的元素擺在基準後面(與基準值相等的數可以到任何一邊)

在這個分割結束之後,對基準值的排序就已經完成

遞迴排序子序列:遞迴地將小於基準值元素的子序列和大於基準值元素的子序列排序

list1 = [8, 5, 1, 3, 2, 10, 11, 4, 12, 20]
 def partition(arr,low,high): 
     i = ( low-1 )         # 最小元素索引
     pivot = arr[high]     
 
     for j in range(low , high): 
 
         # 當前元素小於或等於 pivot 
         if   arr[j] <= pivot: 

            i = i+1 
            arr[i],arr[j] = arr[j],arr[i] 

    arr[i+1],arr[high] = arr[high],arr[i+1] 

    return ( i+1 )

def quicksort(arr,low,high): 
    if low < high: 

        pi = partition(arr,low,high) 

        quicksort(arr, low, pi-1) 
        quicksort(arr, pi+1, high) 


quicksort(list1, 0, len(list1)-1)

print(list1)

output

[1, 2, 3, 4, 5, 8, 10, 11, 12, 20]

75. requests 簡介

該庫是發起 HTTP 請求的強大類庫,呼叫簡單,功能強大

import requests
 
 url = "http://www.luobodazahui.top"
 
 
 response = requests.get(url)  # 獲得請求
 
 response.encoding = "utf-8"  # 改變其編碼
 
html = response.text  # 獲得網頁內容

binary__content = response.content  # 獲得二進位制資料

raw = requests.get(url, stream=True)  # 獲得原始響應內容

headers = {'user-agent': 'my-test/0.1.1'}  # 定製請求頭
r = requests.get(url, headers=headers)

cookies = {"cookie": "# your cookie"}  # cookie的使用
r = requests.get(url, cookies=cookies)

76. 比較兩個 json 資料是否相等

dict1 = {"zhangfei": 12, "guanyu": 13, "liubei": 18}
 dict2 = {"zhangfei": 12, "guanyu": 13, "liubei": 18}
 def compare_dict(dict1, dict2):
     issame = []
     for k in dict1.keys():
         if k in dict2:
             if dict1[k] == dict2[k]:
                 issame.append(1)
             else:
                issame.append(2)
        else:
            issame.append(3)
    print(issame)
    sum_except = len(issame)
    sum_actually = sum(issame)
    if sum_except == sum_actually:
        print("this two dict are same!")
        return True
    else:
        print("this two dict are not same!")
        return False

test = compare_dict(dict1, dict2)

output

[1, 1, 1]
this two dict are same!

77. 讀取鍵盤輸入

input() 函式
def forinput():
    input_text = input()
    print("your input text is: ", input_text)
forinput()

output

hello
your input text is:  hello

78. enumerate

enumerate() 函式用於將一個可遍歷的資料物件(如列表、元組或字串)組合為一個索引序列,同時列出資料和資料下標,一般用在 for 迴圈當中

data1 = ['one', 'two', 'three', 'four']
for i, enu in enumerate(data1):
    print(i, enu)

output

0 one
1 two
2 three
3 four

79. pass 語句

pass 是空語句,是為了保持程式結構的完整性。pass 不做任何事情,一般用做佔位語句

def forpass(n):
    if n == 1:
        pass
    else:
        print('not 1')
forpass(1)

80. 正則匹配郵箱

import re
email_list= ["[email protected]","[email protected]", "[email protected]", "[email protected]" ]
for email in email_list:
    ret = re.match("[\w]{4,20}@(.*)\.com$",email)
    if ret:
        print("%s 是符合規定的郵件地址,匹配後結果是:%s" % (email,ret.group()))
    else:
        print("%s 不符合要求" % email)

output

[email protected] 是符合規定的郵件地址,匹配後結果是:[email protected]
[email protected] 不符合要求
[email protected] 不符合要求
[email protected] 是符合規定的郵件地址,匹配後結果是:[email protected]

81. 統計字串中大寫字母的數量

str2 = 'werrQWSDdiWuW'
counter = 0
for i in str2:
    if i.isupper():
        counter += 1
print(counter)

output

82. json 序列化時保留中文

普通序列化:

import json
dict1 = {'name': '蘿蔔', 'age': 18}
dict1_new = json.dumps(dict1)
print(dict1_new)

output

{"name": "\u841d\u535c", "age": 18}

保留中文

import json
dict1 = {'name': '蘿蔔', 'age': 18}
dict1_new = json.dumps(dict1, ensure_ascii=False)
print(dict1_new)

output

{"name": "蘿蔔", "age": 18}

83. 簡述繼承

一個類繼承自另一個類,也可以說是一個孩子類/派生類/子類,繼承自父類/基類/超類,同時獲取所有的類成員(屬性和方法)

繼承使我們可以重用程式碼,並且還可以更方便地建立和維護程式碼

Python 支援以下型別的繼承:

單繼承- 一個子類類繼承自單個基類

多重繼承- 一個子類繼承自多個基類

多級繼承- 一個子類繼承自一個基類,而基類繼承自另一個基類

分層繼承- 多個子類繼承自同一個基類

混合繼承- 兩種或兩種以上繼承型別的組合

84. 什麼是猴子補丁

猴子補丁是指在執行時動態修改類和模組

猴子補丁主要有以下幾個用處:

在執行時替換方法、屬性等

在不修改第三方程式碼的情況下增加原來不支援的功能

在執行時為記憶體中的物件增加 patch 而不是在磁碟的原始碼中增加

85. help() 函式和 dir() 函式

help() 函式返回幫助文件和引數說明:

help(dict)

output

Help on class dict in module builtins:
 
 class dict(object)
  |  dict() -> new empty dictionary
  |  dict(mapping) -> new dictionary initialized from a mapping object's
  |      (key, value) pairs
  |  dict(iterable) -> new dictionary initialized as if via:
  |      d = {}
  |      for k, v in iterable:
 |          d[k] = v
 |  dict(**kwargs) -> new dictionary initialized with the name=value pairs
 |      in the keyword argument list.  For example:  dict(one=1, two=2)
......

dir() 函式返回物件中的所有成員 (任何型別)

dir(dict)

output

['__class__',
  '__contains__',
  '__delattr__',
  '__delitem__',
  '__dir__',
  '__doc__',
  '__eq__',
  '__format__',
  '__ge__',
 '__getattribute__',
 '__getitem__',
......

86. 解釋 Python 中的 //** 運算子

// 運算子執行地板除法,返回結果的整數部分 (向下取整)

% 是取模符號,返回除法後的餘數

** 符號表示取冪. a**b 返回 a 的 b 次方

print(5//3)
print(5/3)
print(5%3)
print(5**3)

output

1
1.6666666666666667
2
125

87. 主動丟擲異常

使用 raise

def test_raise(n):
    if not isinstance(n, int):
        raise Exception('not a int type')
    else:
        print('good')
test_raise(8.9)

output

---------------------------------------------------------------------------
 Exception                                 Traceback (most recent call last)
 <ipython-input-262-b45324f5484e> in <module>
       4     else:
       5         print('good')
 ----> 6 test_raise(8.9)
 
 <ipython-input-262-b45324f5484e> in test_raise(n)
       1 def test_raise(n):
      2     if not isinstance(n, int):
----> 3         raise Exception('not a int type')
      4     else:
      5         print('good')

Exception: not a int type

88. tuple 和 list 轉換

tuple1 = (1, 2, 3, 4)
list1 = list(tuple1)
print(list1)
tuple2 = tuple(list1)
print(tuple2)

output

[1, 2, 3, 4](1, 2, 3, 4)

89. 簡述斷言

Python 的斷言就是檢測一個條件,如果條件為真,它什麼都不做;反之它觸發一個帶可選錯誤資訊的 AssertionError

def testassert(n):
    assert n == 2, "n is not 2"
    print('n is 2')
testassert(1)

output

---------------------------------------------------------------------------
 AssertionError                            Traceback (most recent call last)
 <ipython-input-268-a9dfd6c79e73> in <module>
       2     assert n == 2, "n is not 2"
       3     print('n is 2')
 ----> 4 testassert(1)
 
 <ipython-input-268-a9dfd6c79e73> in testassert(n)
       1 def testassert(n):
----> 2     assert n == 2, "n is not 2"
      3     print('n is 2')
      4 testassert(1)

AssertionError: n is not 2

90. 什麼是非同步非阻塞

同步非同步指的是呼叫者與被呼叫者之間的關係

所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不會返回,一旦呼叫返回,就得到了返回值

非同步的概念和同步相對,呼叫在發出之後,這個呼叫就直接返回了,所以沒有返回結果。當該非同步功能完成後,被呼叫者可以通過狀態、通知或回撥來通知呼叫者

阻塞非阻塞是執行緒或程序之間的關係

阻塞呼叫是指呼叫結果返回之前,當前執行緒會被掛起(如遇到io操作)。呼叫執行緒只有在得到結果之後才會返回。函式只有在得到結果之後才會將阻塞的執行緒啟用

非阻塞和阻塞的概念相對應,非阻塞呼叫指在不能立刻得到結果之前也會立刻返回,同時該函式不會阻塞當前執行緒

91. 什麼是負索引

Python 中的序列是有索引的,它由正數和負陣列成。正的數字使用'0'作為第一個索引,'1'作為第二個索引,以此類推

負數的索引從'-1'開始,表示序列中的最後一個索引,' - 2'作為倒數第二個索引,依次類推

92. 退出 Python 後,記憶體是否全部釋放

不是的,那些具有物件迴圈引用或者全域性名稱空間引用的變數,在 Python 退出時往往不會被釋放

另外不會釋放 C 庫保留的部分內容

93. Flask 和 Django 的異同

Flask 是 “microframework”,主要用來編寫小型應用程式,不過隨著 Python 的普及,很多大型程式也在使用 Flask。同時,在 Flask 中,我們必須使用外部庫

Django 適用於大型應用程式。它提供了靈活性,以及完整的程式框架和快速的專案生成方法。可以選擇不同的資料庫,URL結構,模板樣式等

94. 建立刪除作業系統上的檔案

import os
f = open('test.txt', 'w')
f.close()
os.listdir()
os.remove('test.txt')

95. 簡述 logging 模組

logging 模組是 Python 內建的標準模組,主要用於輸出執行日誌,可以設定輸出日誌的等級、日誌儲存路徑、日誌檔案回滾等;相比 print,具備如下優點:

可以通過設定不同的日誌等級,在 release 版本中只輸出重要資訊,而不必顯示大量的除錯資訊

print 將所有資訊都輸出到標準輸出中,嚴重影響開發者從標準輸出中檢視其它資料;logging 則可以由開發者決定將資訊輸出到什麼地方,以及怎麼輸出

簡單配置:

import logging
logging.debug("debug log")
logging.info("info log")
logging.warning("warning log")
logging.error("error log")
logging.critical("critica log")

output

WARNING:root:warning log
ERROR:root:error log
CRITICAL:root:critica log

預設情況下,只顯示了大於等於WARNING級別的日誌。logging.basicConfig()函式調整日誌級別、輸出格式等

96. 統計字串中單詞出現次數

from collections import Counter
str1 = "nihsasehndciswemeotpxc"
print(Counter(str1))

output

Counter({'s': 3, 'e': 3, 'n': 2, 'i': 2, 'h': 2, 'c': 2, 'a': 1, 'd': 1, 'w': 1, 'm': 1, 'o': 1, 't': 1, 'p': 1, 'x': 1})

97. 正則 re.complie 的作用

re.compile 是將正則表示式編譯成一個物件,加快速度,並重復使用

98. try except else finally 的意義

try..except..else 沒有捕獲到異常,執行 else 語句

try..except..finally 不管是否捕獲到異常,都執行 finally 語句

99.反轉列表

使用切片:

$ python -m timeit -n 1000000 -s 'import numpy as np' 'mylist=list(np.arange(0, 200))' 'mylist[::-1]'
1000000 loops, best of 5: 15.6 usec per loop

使用 reverse():

$ python -m timeit -n 1000000 -s 'import numpy as np' 'mylist=list(np.arange(0, 200))' 'mylist.reverse()'
1000000 loops, best of 5: 10.7 usec per loop

這兩種方法都可以反轉列表,但需要注意的是內建函式 reverse() 會更改原始列表,而切片方法會建立一個新列表。

顯然,內建函式 reverse() 比列表切片方法更快!

100. 字串中數字替換

使用 re 正則替換

import re
str1 = '我是周蘿蔔,今年18歲'
result = re.sub(r"\d+","20",str1)
print(result)

output

我是周蘿蔔,今年20歲

綜合篇:網路程式設計

101. 簡述 OSI 七層協議

是網路傳輸協議,人為的把網路傳輸的不同階段劃分成不同的層次

七層劃分為:應用層、表示層、會話層、傳輸層、網路層、資料鏈路層、物理層

五層劃分為:應用層、傳輸層、網路層、資料鏈路層、物理層

物理層:網線,電纜等物理裝置

資料鏈路層:Mac 地址

網路層:IP 地址

傳輸層:TCP,UDP 協議

應用層:FTP 協議,Email,WWW 等

102. 三次握手、四次揮手的流程

都發生在傳輸層

三次握手:

TCP 協議是主機對主機層的傳輸控制協議,提供可靠的連線服務,採用三次握手確認建立一個連線。 TCP 標誌位(位碼),有6種標示:SYN(synchronous建立聯機) ACK(acknowledgement 確認) PSH(push傳送) FIN(finish結束) RST(reset重置) URG(urgent緊急)

Sequence number(順序號碼) Acknowledge number(確認號碼)

第一次握手:主機 A 傳送位碼為 syn=1,隨機產生 seq number=1234567 的資料包到伺服器,並進入 SYN_SEND 狀態,主機 B 由 SYN=1 知道,A 要求建立聯機

第二次握手:主機 B 收到請求後要確認聯機資訊,向 A 傳送 ack number=(主機 A 的 seq+1),syn=1,ack=1,隨機產生 seq=7654321 的包,並進入 SYN_RECV 狀態

第三次握手:主機 A 收到後檢查 ack number 是否正確,即第一次傳送的 seq number+1,以及位碼 ack 是否為 1,若正確,主機 A 會再發送 ack number=(主機 B 的 seq+1),ack=1,主機 B 收到後確認 seq 值與 ack=1 則連線建立成功,兩個主機均進入 ESTABLISHED 狀態

以上完成三次握手,主機 A 與主機 B 開始傳送資料

四次揮手:

因為 TCP 連線是全雙工的,因此每個方向都必須單獨進行關閉。這個原則是當一方完成它的資料傳送任務後就能傳送一個 FIN 來終止這個方向的連線。收到一個 FIN 只意味著這一方向上沒有資料流動,一個 TCP 連線在收到一個 FIN 後仍能傳送資料。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉

伺服器 A 傳送一個 FIN,用來關閉 A 到伺服器 B 的資料傳送。

伺服器 B 收到這個 FIN,它發回一個 ACK,確認序號為收到的序號加1。和 SYN 一樣,一個 FIN 將佔用一個序號

伺服器 B 關閉與伺服器 A 的連線,傳送一個 FIN 給伺服器 A

伺服器 A 發回 ACK 報文確認,並將確認序號設定為收到序號加1

103. 什麼是 C/S 和 B/S 架構

B/S 又稱為瀏覽器/伺服器模式。比如各種網站,jupyter notebook 等。 優點:零安裝,維護簡單,共享性好。 缺點:安全性較差,個性化不足

C/S 又稱為客戶端/伺服器模式。比如微信客戶端,Oracle 客戶端等。 優點:安全性好,資料傳輸較快,穩定。 缺點:對 PC 機作業系統等有要求,當客戶端較多時,伺服器端負載較大

104. TCP 和 UDP 的區別

TCP 和 UDP 都是 OSI 模型中運輸層的協議。TCP 提供可靠的通訊傳輸,而 UDP 則常被用於廣播和細節控制交給應用的通訊傳輸。 UDP 不提供複雜的控制機制,利用 IP 提供面向無連線的通訊服務。 TCP 充分實現了資料傳輸時各種控制功能,可以進行丟包的重發控制,還可以對次序亂掉的分包進行順序控制

TCP 應用:FTP 傳輸,點對點簡訊等

UDP 應用:媒體流等

105. 區域網和廣域網

廣域網(WAN,Wide Area Network)也稱遠端網(long haul network )。通常跨接很大的物理範圍,所覆蓋的範圍從幾十公里到幾千公里,它能連線多個城市或國家,或橫跨幾個洲並能提供遠距離通訊,形成國際性的遠端網路

域網(Local Area Network,LAN)是指在某一區域內由多臺計算機互聯成的計算機組。一般是方圓幾千米以內。區域網可以實現檔案管理、應用軟體共享、印表機共享、工作組內的日程安排、電子郵件和傳真通訊服務等功能。區域網是封閉型的,可以由辦公室內的兩臺計算機組成,也可以由一個公司內的上千臺計算機組成

106. arp 協議

ARP(Address Resolution Protocol)即地址解析協議, 用於實現從 IP 地址到 MAC 地址的對映,即詢問目標 IP 對應的 MAC 地址

107. 什麼是 socket?簡述基於 TCP 協議的套接字通訊流程

socket 是對 TCP/IP 協議的封裝,它的出現只是使得程式設計師更方便地使用 TCP/IP 協議棧而已。socket 本身並不是協議,它是應用層與 TCP/IP 協議族通訊的中間軟體抽象層,是一組呼叫介面(TCP/IP網路的API函式)

“TCP/IP 只是一個協議棧,就像作業系統的執行機制一樣,必須要具體實現,同時還要提供對外的操作介面。 這個就像作業系統會提供標準的程式設計介面,比如win32程式設計介面一樣。TCP/IP 也要提供可供程式設計師做網路開發所用的介面,這就是 Socket 程式設計介面。”

Server:

import socket
 import threading
 def tcplink(sock, addr):
     print('Accept new connection from %s:%s...' % addr)
     sock.send(b'Welcome!')
     while True:
         data = sock.recv(1024)
         time.sleep(1)
         if not data or data.decode('utf-8') == 'exit':
            break
        sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
    sock.close()
    print('Connection from %s:%s closed.' % addr)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 監聽埠:
s.bind(('127.0.0.1', 9999))
s.listen(5)
print('Waiting for connection...')
while True:
    # 接受一個新連線:
    sock, addr = s.accept()
    # 建立新執行緒來處理TCP連線:
    t = threading.Thread(target=tcplink, args=(sock, addr))
    t.start()

Client:

import socket
 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 # 建立連線:
 s.connect(('127.0.0.1', 9999))
 # 接收歡迎訊息:
 print(s.recv(1024).decode('utf-8'))
 for data in [b'Michael', b'Tracy', b'Sarah']:
     # 傳送資料:
     s.send(data)
    print(s.recv(1024).decode('utf-8'))
s.send(b'exit')
s.close()

例子來源於廖雪峰的官網

108. 簡述 程序、執行緒、協程的區別以及應用場景

程序是具有一定獨立功能的程式關於某個資料集合上的一次執行活動,程序是系統進行資源分配和排程的一個獨立單位。每個程序都有自己的獨立記憶體空間,不同程序通過程序間通訊來通訊

執行緒是程序的一個實體,是CPU排程和分派的基本單位,它是比程序更小的能獨立執行的基本單位。執行緒自己基本上不擁有系統資源,只擁有一點在執行中必不可少的資源(如程式計數器,一組暫存器和棧),但是它可與同屬一個程序的其他的執行緒共享程序所擁有的全部資源

協程是一種使用者態的輕量級執行緒,協程的排程完全由使用者控制。協程擁有自己的暫存器上下文和棧

多程序:密集 CPU 任務,需要充分使用多核 CPU 資源(伺服器,大量的平行計算)的時候,用多程序。 缺陷:多個程序之間通訊成本高,切換開銷大

多執行緒:密集 I/O 任務(網路 I/O,磁碟 I/O,資料庫 I/O)使用多執行緒合適。 缺陷:同一個時間切片只能執行一個執行緒,不能做到高並行,但是可以做到高併發

協程:又稱微執行緒,在單執行緒上執行多個任務,用函式切換,開銷極小。不通過作業系統排程,沒有程序、執行緒的切換開銷。缺陷:單執行緒執行,處理密集 CPU 和本地磁碟 IO 的時候,效能較低。處理網路 I/O 效能還是比較高

多執行緒請求返回是無序的,哪個執行緒有資料返回就處理哪個執行緒,而協程返回的資料是有序的

109. 如何使用執行緒池和程序池

池的功能是限制啟動的程序數或執行緒數。當併發的任務數遠遠超過了計算機的承受能力時,即無法一次性開啟過多的程序數或執行緒數時,就應該用池的概念將開啟的程序數或執行緒數限制在計算機可承受的範圍內

多程序

from multiprocessing import Pool
 import os
 import time
 import random
 
 
 def long_time_task(name):
     print('Run task %s (%s)...' % (name, os.getpid()))
     start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('Task %s runs %0.2f seconds.' % (name, (end - start)))


def test_pool():
    print('Parent process %s.' % os.getpid())
    p = Pool(4)
    for i in range(5):
        p.apply_async(long_time_task, args=(i,))
    print('Waiting for all subprocesses done...')
    p.close()
    p.join()
    print('All subprocesses done.')


if __name__ == '__main__':
    test_pool()

output

Parent process 32432.
 Waiting for all subprocesses done...
 Run task 0 (15588)...
 Run task 1 (32372)...
 Run task 2 (12440)...
 Run task 3 (18956)...
 Task 2 runs 0.72 seconds.
 Run task 4 (12440)...
 Task 3 runs 0.82 seconds.
Task 1 runs 1.21 seconds.
Task 0 runs 3.00 seconds.
Task 4 runs 2.95 seconds.
All subprocesses done.

apply_async(func[, args[, kwds]]) :使用非阻塞方式呼叫 func(並行執行,堵塞方式必須等待上一個程序退出才能執行下一個程序),args 為傳遞給 func 的引數列表,kwds 為傳遞給 func 的關鍵字引數列表; close():關閉 Pool,使其不再接受新的任務; terminate():不管任務是否完成,立即終止; join():主程序阻塞,等待子程序的退出, 必須在 close 或 terminate 之後使用

也可以使用 concurrent.futures 模組提供的功能來實現

def test_future_process():
     print('Parent process %s.' % os.getpid())
     p = ProcessPoolExecutor(4)
     for i in range(5):
         p.submit(long_time_task, i)
     p.shutdown(wait=True)
     print('Finish')
 
 
if __name__ == '__main__':
    # test_pool()
    test_future_process()

output

Parent process 29368.
 Run task 0 (32148)...
 Run task 1 (31552)...
 Run task 2 (24012)...
 Run task 3 (29408)...
 Task 2 runs 0.52 seconds.
 Run task 4 (24012)...
 Task 3 runs 0.86 seconds.
 Task 1 runs 1.81 seconds.
Task 0 runs 1.83 seconds.
Task 4 runs 1.69 seconds.
Finish

多執行緒

def sayhello(a):
     print("hello: " + a)
     start = time.time()
     time.sleep(random.random() * 3)
     end = time.time()
     print('Task %s runs %0.2f seconds.' % (a, (end - start)))
 
 
 def test_future_thread():
    seed = ["a", "b", "c", "d"]
    start = time.time()
    with ThreadPoolExecutor(3) as executor:
        for i in seed:
            executor.submit(sayhello, i)
    end = time.time()
    print("Thread Run Time: " + str(end - start))

output

hello: a
hello: b
hello: c
Task a runs 0.40 seconds.
hello: d
Task b runs 0.56 seconds.
Task d runs 1.70 seconds.
Task c runs 2.92 seconds.
Thread Run Time: 2.9195945262908936

可以看出,由於是建立了限制為3的執行緒池,所以只有三個任務在同時執行

110. 程序之間如何進行通訊

def write(q):
     print("write(%s), 父程序為(%s)" % (os.getpid(), os.getppid()))
     for i in "Python":
         print("Put %s to Queue" % i)
         q.put(i)
 
 
 def read(q):
     print("read(%s), 父程序為(%s)" % (os.getpid(), os.getppid()))
    for i in range(q.qsize()):
        print("read 從 Queue 獲取到訊息: %s" % q.get(True))


def test_commun():
    print("(%s) start" % os.getpid())
    q = Manager().Queue()
    pw = Process(target=write, args=(q, ))
    pr = Process(target=read, args=(q, ))
    pw.start()
    pr.start()
    pw.join()
    pr.terminate()

output

(23544) start
 write(29856), 父程序為(23544)
 Put P to Queue
 Put y to Queue
 Put t to Queue
 Put h to Queue
 Put o to Queue
 Put n to Queue
 read(25016), 父程序為(23544)
read 從 Queue 獲取到訊息: P
read 從 Queue 獲取到訊息: y
read 從 Queue 獲取到訊息: t
read 從 Queue 獲取到訊息: h
read 從 Queue 獲取到訊息: o
read 從 Queue 獲取到訊息: n

Python 的 multiprocessing 模組包裝了底層的機制,提供了 Queue、Pipes 等多種方式來交換資料

111. 程序鎖和執行緒鎖

程序鎖:是為了控制同一作業系統中多個程序訪問一個共享資源,只是因為程式的獨立性,各個程序是無法控制其他程序對資源的訪問的,但是可以使用本地系統的訊號量控制。 訊號量(Semaphore),有時被稱為訊號燈,是在多執行緒環境下使用的一種設施,是可以用來保證兩個或多個關鍵程式碼段不被併發呼叫

執行緒鎖:當多個執行緒幾乎同時修改一個共享資料的時候,需要進行同步控制,執行緒同步能夠保證多個執行緒安全的訪問競爭資源(全域性內容),最簡單的同步機制就是使用互斥鎖。 某個執行緒要更改共享資料時,先將其鎖定,此時資源的狀態為鎖定狀態,其他執行緒就能更改,直到該執行緒將資源狀態改為非鎖定狀態,也就是釋放資源,其他的執行緒才能再次鎖定資源。互斥鎖保證了每一次只有一個執行緒進入寫入操作。從而保證了多執行緒下資料的安全性

112. 什麼是併發和並行

並行:多個 CPU 核心,不同的程式就分配給不同的 CPU 來執行。可以讓多個程式同時執行

併發:單個 CPU 核心,在一個時間切片裡一次只能執行一個程式,如果需要執行多個程式,則序列執行

113. threading.local 的作用

ThreadLocal 叫做執行緒本地變數,ThreadLocal 在每一個變數中都會建立一個副本,每個執行緒都可以訪問自己內部的副本變數,對其他執行緒時不可見的,修改之後也不會影響到其他執行緒

114. 什麼是域名解析

域名解析是指將域名解析為 IP 地址。也有反向的“逆解析”,將 IP 通過 DNS 伺服器查詢到對應的域名地址

DNS 是域名系統 (Domain Name System),域名系統為因特網上的主機分配域名地址和 IP 地址。使用者使用域名地址,該系統就會自動把域名地址轉為 IP 地址

115. LVS 是什麼及作用

LVS 是 Linux Virtual Server 的簡寫,意即 Linux 虛擬伺服器,是一個虛擬的伺服器集群系統,即負載均衡伺服器

LVS 工作模式分為 NAT 模式、TUN 模式、以及 DR 模式

116. Nginx 的作用

Nginx 主要功能:1、反向代理 2、負載均衡 3、HTTP 伺服器(包含動靜分離) 4、正向代理

正向代理:某些情況下,代理使用者去訪問伺服器,需要手動設定代理伺服器的 IP 和埠號

反向代理:是用來代理伺服器的,代理要訪問的目標伺服器。代理伺服器接受請求,然後將請求轉發給內部網路的伺服器(叢集化),並將從伺服器上得到的結果返回給客戶端,此時代理伺服器對外就表現為一個伺服器

負載均衡伺服器類似於 LVS HTTP 伺服器類似於 Tomcat 等

117. keepalived 及 HAProxy

HAProxy 提供高可用性、負載均衡,以及基於 TCP 和 HTTP 的應用程式代理。 keepalived 是叢集管理中保證叢集高可用的一個服務軟體,其功能類似於 heartbeat,用來防止單點故障

118. 什麼是 rpc

RPC 是指遠端過程呼叫,也就是說兩臺伺服器 A,B,一個應用部署在 A 伺服器上,想要呼叫 B 伺服器上應用提供的函式/方法,由於不在一個記憶體空間,不能直接呼叫,需要通過網路來表達呼叫的語義和傳達呼叫的資料

119. 從瀏覽器輸入一個網址到展示網址頁面的過程

瀏覽器通過 DNS 伺服器查詢到域名對應的 IP 地址

瀏覽器給 IP 對應的 web 伺服器傳送 HTTP 請求

web 伺服器接收到 HTTP 請求後,返回響應給瀏覽器

瀏覽器接收到響應後渲染頁面

120. 什麼是cdn

CDN 的全稱是 Content Delivery Network,即內容分發網路。CDN 是構建在網路之上的內容分發網路,依靠部署在各地的邊緣伺服器,通過中心平臺的負載均衡、內容分發、排程等功能模組,使使用者就近獲取所需內容,降低網路擁塞,提高使用者訪問響應速度和命中率。CDN 的關鍵技術主要有內容儲存和分發技術

綜合篇:資料庫和框架

121. 列舉常見的資料庫

關係型資料庫:MySQL,Oracle,SQLServer,SQLite,DB2

非關係型資料庫:MongoDB,Redis,HBase,Neo4j

122. 資料庫設計三大正規化

建立科學的,規範的的資料庫是需要滿足一些規範的,以此來優化資料資料儲存方式,在關係型資料庫中這些規範就可以稱為正規化

第一正規化:當關系模式 R 的所有屬性都不能在分解為更基本的資料單位時,稱 R 是滿足第一正規化的,簡記為 1NF

關係模式R的所有屬性不能再分解

第二正規化:如果關係模式 R 滿足第一正規化,並且 R 的所有非主屬性都完全依賴於 R 的每一個候選關鍵屬性,稱 R 滿足第二正規化,簡記為 2NF

非主屬性都要依賴於每一個關鍵屬性

三正規化:設 R 是一個滿足第一正規化條件的關係模式,X 是 R 的任意屬性集,如果 X 非傳遞依賴於 R 的任意一個候選關鍵字,稱 R 滿足第三正規化,簡記為 3NF

資料不能存在傳遞關係,即每個屬性都跟主鍵有直接關係而不是間接關係

123. 什麼是資料庫事務

事務(Transaction)是併發控制的基本單位。所謂的事務,它是一個操作序列,這些操作要麼都執行,要麼都不執行,它是一個不可分割的工作單位

在關係資料庫中,一個事務可以是一條 SQL 語句、一組 SQL 語句或整個程式。 四個屬性:原子性,一致性,隔離性和永續性

124. MySQL 索引種類

MySQL 目前主要有以下幾種索引型別:

  • 普通索引
  • 唯一索引
  • 主鍵索引
  • 組合索引
  • 全文索引

125. 資料庫設計中一對多和多對多的應用場景

一對一關係示例: 一個學生對應一個學生檔案材料,或者每個人都有唯一的身份證編號

一對多關係示例: 一個學生只屬於一個班,但是一個班級有多名學生

多對多關係示例: 一個學生可以選擇多門課,一門課也有多名學生

126. 簡述觸發器、函式、檢視、儲存過程

觸發器:觸發器是一個特殊的儲存過程,它是資料庫在 insert、update、delete 的時候自動執行的程式碼塊

函式:資料庫中提供了許多內建函式,還可以自定義函式,實現 sql 邏輯

檢視:檢視是由查詢結果形成的一張虛擬表,是表通過某種運算得到的一個投影

儲存過程:把一段程式碼封裝起來,當要執行這一段程式碼的時候,可以通過呼叫該儲存過程來實現(經過第一次編譯後再次呼叫不需要再次編譯,比一個個執行 sql 語句效率高)

127. 常用 SQL 語句

DML(資料操作語言)

  • SELECT - 從資料庫表中獲取資料
  • UPDATE - 更新資料庫表中的資料
  • DELETE - 從資料庫表中刪除資料
  • INSERT INTO - 向資料庫表中插入資料

DDL(資料定義語言)

  • CREATE DATABASE - 建立新資料庫
  • ALTER DATABASE - 修改資料庫
  • CREATE TABLE - 建立新表
  • ALTER TABLE - 變更(改變)資料庫表
  • DROP TABLE - 刪除表
  • CREATE INDEX - 建立索引(搜尋鍵)
  • DROP INDEX - 刪除索引

128. 主鍵和外來鍵的區別

定義主鍵和外來鍵主要是為了維護關係資料庫的完整性 主鍵是能確定一條記錄的唯一標識。不能重複,不允許為空

外來鍵用於與另一張表關聯。是能確定另一張表記錄的欄位,用於保持資料的一致性

主鍵外來鍵索引定義唯一標識一條記錄,不能重複,不允許為空表的外來鍵是另一表的主鍵,外來鍵可以重複,可以是空值該欄位沒有重複值,但可以有空值作用用來保證資料完整性用來和其他表建立聯絡提高查詢排序的速度個數只能有一個可有多個可有多個

129. 如何開啟 MySQL 慢日誌查詢

修改配置檔案,然後重啟服務生效

在linux下,vim /etc/my.cnf,在[mysqld]內容項下增加: slow_query_log = ON long_query_time = 2 # 查詢超過2秒的就會記錄

命令列,但是重啟服務後會失效 SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 2;

130. MySQL 資料庫備份命令

mysqldump -u 使用者名稱 -p 資料庫名 > 匯出的檔名

131. char 和 varchar 的區別

char:儲存定長資料很方便,CHAR 欄位上的索引效率級高,必須在括號裡定義長度,可以有預設值,比如定義 char(10)

varchar:儲存變長資料,但儲存效率沒有 CHAR 高,必須在括號裡定義長度,可以有預設值

132. 最左字首原則

mysql 建立多列索引(聯合索引)有最左字首的原則,即最左優先,如:

如果有一個2列的索引(col1,col2),則已經對(col1)、(col1,col2)上建立了索引

如果有一個3列索引(col1,col2,col3),則已經對(col1)、(col1,col2)、(col1,col2,col3)上建立了索引

133. 無法命中索引的情況

使用or關鍵字會導致無法命中索引

左前導查詢會導致無法命中索引,如 like '%a' 或者 like '%a%'

單列索引的索引列為 null 時全值匹配會使索引失效,組合索引全為 null 時索引失效

組合索引不符合左字首原則的列無法命中索引,如我們有4個列 a、b、c、d,我們建立一個組合索引 INDEX(a,b,c,d),那麼能命中索引的查詢為 a,ab,abc,abcd,除此之外都無法命中索引

強制型別轉換會導致索引失效

負向查詢條件會導致無法使用索引,比如 NOT IN,NOT LIKE,!= 等

如果 mysql 估計使用全表掃描要比使用索引快,則不使用索引

134. 資料庫讀寫分離

讀寫分離,就是將資料庫分為了主從庫,一個主庫用於寫資料,多個從庫完成讀資料的操作,主從庫之間通過某種機制進行資料的同步,是一種常見的資料庫架構

135. 資料庫分庫分表

資料庫水平切分,是一種常見的資料庫架構,是一種通過演算法,將資料庫進行分割的架構。一個水平切分叢集中的每個資料庫,通常稱為一個“分片”。每一個分片中的資料沒有重合,所有分片中的資料並集組成全部資料。

水平切分分為庫內分表和分庫分表,是根據表內資料內在的邏輯關係,將同一個表按不同的條件分散到多個數據庫或多個表中,每個表中只包含一部分資料,從而使得單個表的資料量變小,達到分散式的效果

136. redis 和 memcached 比較

redis 和 memcached 都是將資料存放在記憶體中,都是記憶體資料庫。不過 memcached 還可用於快取其他東西,例如圖片、影片等等

redis 不僅僅支援簡單的 k/v 型別的資料,同時還提供 list,set,hash 等資料結構的儲存

分散式設定, 都可以做一主多從或一主一從

儲存資料安全,memcached 掛掉後,資料完全丟失;redis 可以定期儲存到磁碟(持久化)

災難恢復,memcached 掛掉後,資料不可恢復; redis 資料丟失後可以通過 aof 恢復

137. redis中資料庫預設是多少個 db 及作用

redis 預設有16個數據庫,每個資料庫中的資料都是隔離的,這樣,在儲存資料的時候,就可以指定把不同的資料儲存到不同的資料庫中。 且只有單機才有,如果是叢集就沒有資料庫的概念

138. redis 有哪幾種持久化策略

RDB 持久化:是將 Reids 在記憶體中的資料庫記錄定時 dump 到磁碟上的持久化 AOF(append only file)持久化:將 Reids 的操作日誌以追加的方式寫入檔案

139. redis 支援的過期策略

通用的三種過期策略

定時刪除 在設定 key 的過期時間的同時,為該 key 建立一個定時器,讓定時器在 key 的過期時間來臨時,對 key 進行刪除

惰性刪除 key 過期的時候不刪除,每次從資料庫獲取 key 的時候去檢查是否過期,若過期,則刪除,返回 null

定期刪除 每隔一段時間執行一次刪除過期 key 操作

redis 採用惰性刪除+定期刪除策略

140. 如何保證 redis 中的資料都是熱點資料

限定 Redis 佔用的記憶體,Redis 會根據自身資料淘汰策略,載入熱資料到記憶體。 所以,計算一下所有熱點資料大約佔用的記憶體,然後設定一下 Redis 記憶體限制即可

141. Python 操作 redis

使用 redis 第三方庫來操作

import redis

# 建立一個 redis 連線池
def redis_conn_pool():
    pool = redis.ConnectionPool(host='redis-host', port=redis-port,
                                decode_responses=True, password='redis-pwd')
    r = redis.Redis(connection_pool=pool)
    return r

142. 基於 redis 實現釋出和訂閱

訂閱者

if __name__ == "__main__":
     conn = redis.Redis(host='',
                        port=12143, password='')
 
     ps = conn.pubsub()
     ps.subscribe('chat')  # 從 chat 訂閱訊息
     for item in ps.listen():  # 監聽狀態:有訊息釋出了就拿過來
         if item['type'] == 'message':
             print(item)
            print(item['channel'])
            print(item['data'])

釋出者

if __name__ == "__main__":
     number_list = ['300033', '300032', '300031', '300030']
     signal = ['1', '-1', '1', '-1']
     pool = redis.ConnectionPool(host='redis-12143.c8.us-east-1-3.ec2.cloud.redislabs.com', port=12143,
                                 decode_responses=True, password='pkAWNdYWfbLLfNOfxTJinm9SO16eSJFx')
     r = redis.Redis(connection_pool=pool)
     for i in range(len(number_list)):
         value_new = str(number_list[i]) + ' ' + str(signal[i])
         print(value_new)
        r.publish("chat", value_new)

143. 如何高效的找到 redis 中的某個 KEY

import redis
con = redis.Redis()
con.keys(pattern='key*') # *代表萬用字元

144. 基於 redis 實現先進先出、後進先出及優先順序佇列

class Zhan:
     def __init__(self,conn):
         self.conn = conn
     def push(self,val):
         self.conn.rpush('aaa',val)
     def pop(self):
         return self.conn.rpop('aaa')
 
 class Dui:
    def __init__(self,conn):
        self.conn = conn
    def push(self,val):
        self.conn.rpush('bbb',val)
    def get(self):
        return self.conn.lpop('bbb')

class Xu:
    def __init__(self,conn):
        self.conn = conn
    def push(self,val,count):
        self.conn.zadd('ccc',val,count)
    def get(self):
        a = self.conn.zrange('ccc', 0, 0)[0]
        self.conn.zrem('ccc', a)
        return a

145. redis 如何實現主從複製

在從伺服器中配置 SLAVEOF 127.0.0.1 6380 # 主伺服器 IP,埠

146. 迴圈獲取 redis 中某個非常大的列表資料

def list_iter(name):
    """
    自定義redis列表增量迭代
    :param name: redis中的name,即:迭代name對應的列表
    :return: yield 返回 列表元素
    """
    list_count = r.llen(name)
    for index in xrange(list_count):
        yield r.lindex(name, index)

147. redis 中的 watch 的命令的作用

watch 用於在進行事務操作的最後一步也就是在執行 exec 之前對某個 key 進行監視,如果這個被監視的 key 被改動,那麼事務就被取消,否則事務正常執行

148. redis 分散式鎖

為 redis 叢集設計的鎖,防止多個任務同時修改資料庫,其本質就是為叢集中的每個主機設定一個會超時的字串,當叢集中有一半多的機器設定成功後就認為加鎖成功,直至鎖過期或解鎖不會有第二個任務加鎖成功

149. http 協議

超文字傳輸協議(HTTP,HyperText Transfer Protocol)是網際網路上應用最為廣泛的一種網路協議。HTTP 是一個客戶端和伺服器端請求和應答的標準。客戶端是終端使用者,伺服器端是網站。一般由 HTTP 客戶端發起一個請求,建立一個到伺服器指定埠(預設是80埠)的 TCP 連線,HTTP 伺服器則在那個埠監聽客戶端傳送過來的請求,並給與響應

150. uwsgi,uWSGI 和 WSGI 的區別

WSGI:全稱是 Web Server Gateway Interface,是一種描述 web server 如何與 web application 通訊的規範。django,flask 等都遵循該協議

uwsgi:是伺服器和服務端應用程式的一種協議,規定了怎麼把請求轉發給應用程式和返回; uwsgi 是一種線路協議而不是通訊協議,在此常用於在 uWSGI 伺服器與其他網路伺服器的資料通訊

uWSGI:是一個 Web 伺服器,它實現了 WSGI 協議、uwsgi、http 等協議。Nginx 中 HttpUwsgiModule 的作用是與 uWSGI 伺服器進行交換

151. HTTP 狀態碼

1xx: 資訊

2xx:成功

3xx:重定向

4xx:客戶端錯誤

5xx:伺服器錯誤

152. HTTP常見請求方式

GET,POST,PUT,DELETE,PATCH 等

153. 響應式佈局

響應式佈局是 Ethan Marcotte 在2010年5月份提出的一個概念,簡而言之,就是一個網站能夠相容多個終端——而不是為每個終端做一個特定的版本

154. 實現一個簡單的 AJAX 請求

AJAX 是一種在無需重新載入整個網頁的情況下,能夠更新部分網頁的技術。

AJAX = 非同步 JavaScript 和 XML

$(function(){
     $('#send').click(function(){
          $.ajax({
              type: "GET",
              url: "test.json",
              data: {username:$("#username").val(), content:$("#content").val()},
              dataType: "json",
              success: function(data){
                          $('#resText').empty();   //清空resText裡面的所有內容
                         var html = ''; 
                         $.each(data, function(commentIndex, comment){
                               html += '<div class="comment"><h6>' + comment['username']
                                         + ':</h6><p class="para"' + comment['content']
                                         + '</p></div>';
                         });
                         $('#resText').html(html);
                      }
         });
    });
});

155. 同源策略

同源策略限制了從同一個源載入的文件或指令碼如何與來自另一個源的資源進行互動。這是一個用於隔離潛在惡意檔案的重要安全機制

如果兩個頁面的協議,埠(如果有指定)和主機都相同,則兩個頁面具有相同的源。我們也可以把它稱為“協議/主機/埠 tuple”,或簡單地叫做“tuple". ("tuple" ,“元”,是指一些事物組合在一起形成一個整體,比如(1,2)叫二元,(1,2,3)叫三元)

156. 什麼是 CORS

CORS 全稱是跨域資源共享(Cross-Origin Resource Sharing),是一種 AJAX 跨域請求資源的方式,支援現代瀏覽器

157. 什麼是 CSRF

CSRF(Cross-site request forgery),中文名稱:跨站請求偽造,也被稱為:one click attack/session riding,縮寫為:CSRF/XSRF

158. 前端實現輪詢、長輪詢

輪詢

var xhr = new XMLHttpRequest();
    setInterval(function(){
        xhr.open('GET','/user');
        xhr.onreadystatechange = function(){

        };
        xhr.send();
    },1000)

長輪詢

function ajax(){
        var xhr = new XMLHttpRequest();
        xhr.open('GET','/user');
        xhr.onreadystatechange = function(){
              ajax();
        };
        xhr.send();
    }

159. 簡述 MVC 和 MTV

所謂 MVC 就是把 web 應用分為模型(M),控制器(C),檢視(V)三層,他們之間以一種外掛似的,松耦合的方式連線在一起。 模型負責業務物件與資料庫的物件(ORM),檢視負責與使用者的互動(頁面),控制器(C)接受使用者的輸入呼叫模型和檢視完成使用者的請求

Django 中的 MTV 模式:

Model(模型):負責業務物件與資料庫的物件(ORM)

Template(模版):負責如何把頁面展示給使用者

View(檢視):負責業務邏輯,並在適當的時候呼叫 Model 和 Template,本質上與 MVC 相同

160. 介面的冪等性

介面冪等性就是使用者對於同一操作發起的一次請求或者多次請求的結果是一致的,不會因為多次點選而產生了副作用

161. Flask 框架的優勢

簡潔,輕巧,擴充套件性強,自由度高

162. 什麼是 ORM

ORM 的全稱是 Object Relational Mapping,即物件關係對映。它的實現思想就是將關係資料庫中表的資料對映成為物件,以物件的形式展現,這樣開發人員就可以把對資料庫的操作轉化為對這些物件的操作

163. PV、UV 的含義

PV:是(page view)訪問量,頁面瀏覽量或點選量,衡量網站使用者訪問的網頁數量。在一定統計週期內使用者每開啟或重新整理一個頁面就記錄1次,多次開啟或重新整理同一頁面則瀏覽量累計

UV:是(Unique Visitor)獨立訪客,統計一段時間內訪問某站點的使用者數(以cookie為依據)

164. supervisor 的作用

supervisor 管理程序,是通過 fork/exec 的方式將這些被管理的程序當作 supervisor 的子程序來啟動,所以我們只需要將要管理程序的可執行檔案的路徑新增到 supervisor 的配置檔案中即可

165. 使用 ORM 和原生 SQL 的優缺點

優點:

  • 方便的使用面向物件,語句清晰
  • 有效的防止 SQL 注入
  • 方便動態構造語句,對於不同的表的相同操作採用多型實現更優雅;
  • 一定程度上方便重構資料層
  • 方便設定設定鉤子函式

缺點:

  • 不太容易處理複雜查詢語句
  • 效能較直接用 SQL 差

166. 列舉一些 django 的內建元件

Admin 元件:是對 model 中對應的資料表進行增刪改查提供的元件

model 元件:負責操作資料庫

form 元件:生成 HTML 程式碼;資料有效性校驗;校驗資訊返回並展示

ModelForm 元件:用於資料庫操作,也可用於使用者請求的驗證

167. 列舉 Django 中執行原生 sql 的方法

使用 execute 執行自定義的 SQL 直接執行 SQL 語句(類似於 pymysql 的用法)

from django.db import connection
cursor = connection.cursor()
cursor.execute("SELECT DATE_FORMAT(create_time, '%Y-%m') FROM blog_article;")
ret = cursor.fetchall()
print(ret)
  • 使用 extra 方法:queryset.extra(select={"key": "原生的SQL語句"})
  • 使用 raw 方法
  • 執行原始 sql 並返回模型
  • 依賴於 model 模型,多用於查詢操作

168. cookie 和 session 的區別

cookie 是儲存在瀏覽器端的鍵值對,可以用來做使用者認證

sesseion 是將使用者的會話資訊儲存在服務端,key 值是隨機產生的字串,value 值是 session 的內容,依賴於 cookie 將每個使用者的隨機字串儲存到使用者瀏覽器中

169. beautifulsoup 模組的作用

BeautifulSoup 庫是解析、遍歷、維護“標籤樹”的功能庫

url = "http://www.baidu.com/"
request = requests.get(url)
html = request.content
soup = BeautifulSoup(html, "html.parser", from_encoding="utf-8")

170. Selenium 模組簡述

Selenium 是模擬操作瀏覽器的庫,可以根據我們的指令,讓瀏覽器自動載入頁面,獲取需要的資料,甚至頁面截圖,或者判斷網站上某些動作是否發生等

from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
print(browser.page_source) # browser.page_source 是獲取網頁的全部 html
browser.close()

好了,這就是今天分享的全部內容,如果喜歡就點個贊吧~

後臺回覆“面試題”獲取完整PDF版本

本文由mdnice多平臺釋出