Python基礎梳理 2

Python語言特性

1 Python的函數參數傳遞

看兩個例子:html

a = 1
def fun(a):
    a = 2
fun(a)
print a  # 1
複製代碼
a = []
def fun(a):
    a.append(1)
fun(a)
print a  # [1]
複製代碼

全部的變量均可以理解是內存中一個對象的「引用」,或者,也能夠看似c中void*的感受。前端

經過id來看引用a的內存地址能夠比較理解:python

a = 1
def fun(a):
    print "func_in",id(a)   # func_in 41322472
    a = 2
    print "re-point",id(a), id(2)   # re-point 41322448 41322448
print "func_out",id(a), id(1)  # func_out 41322472 41322472
fun(a)
print a  # 1
複製代碼

注:具體的值在不一樣電腦上運行時可能不一樣。mysql

能夠看到,在執行完a = 2以後,a引用中保存的值,即內存地址發生變化,由原來1對象的所在的地址變成了2這個實體對象的內存地址。linux

而第2個例子a引用保存的內存值就不會發生變化:nginx

a = []
def fun(a):
    print "func_in",id(a)  # func_in 53629256
    a.append(1)
print "func_out",id(a)     # func_out 53629256
fun(a)
print a  # [1]
複製代碼

這裏記住的是類型是屬於對象的,而不是變量。而對象有兩種,「可更改」(mutable)與「不可更改」(immutable)對象。在python中,strings, tuples, 和numbers是不可更改的對象,而 list, dict, set 等則是能夠修改的對象。(這就是這個問題的重點)git

當一個引用傳遞給函數的時候,函數自動複製一份引用,這個函數裏的引用和外邊的引用沒有半毛關係了.因此第一個例子裏函數把引用指向了一個不可變對象,當函數返回的時候,外面的引用沒半毛感受.而第二個例子就不同了,函數內的引用指向的是可變對象,對它的操做就和定位了指針地址同樣,在內存裏進行修改.程序員

若是還不明白的話,這裏有更好的解釋: stackoverflow.com/questions/9…github

2 Python中的元類(metaclass)

這個很是的不經常使用,可是像ORM這種複雜的結構仍是會須要的,詳情請看:stackoverflow.com/questions/1…web

3 @staticmethod和@classmethod

Python其實有3個方法,即靜態方法(staticmethod),類方法(classmethod)和實例方法,以下:

def foo(x):
    print "executing foo(%s)"%(x)

class A(object):
    def foo(self,x):
        print "executing foo(%s,%s)"%(self,x)

 @classmethod
    def class_foo(cls,x):
        print "executing class_foo(%s,%s)"%(cls,x)

 @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)"%x
        
a=A()

複製代碼

這裏先理解下函數參數裏面的self和cls.這個self和cls是對類或者實例的綁定,對於通常的函數來講咱們能夠這麼調用foo(x),這個函數就是最經常使用的,它的工做跟任何東西(類,實例)無關.對於實例方法,咱們知道在類裏每次定義方法的時候都須要綁定這個實例,就是foo(self, x),爲何要這麼作呢?由於實例方法的調用離不開實例,咱們須要把實例本身傳給函數,調用的時候是這樣的a.foo(x)(實際上是foo(a, x)).類方法同樣,只不過它傳遞的是類而不是實例,A.class_foo(x).注意這裏的self和cls能夠替換別的參數,可是python的約定是這倆,仍是不要改的好.

對於靜態方法其實和普通的方法同樣,不須要對誰進行綁定,惟一的區別是調用的時候須要使用a.static_foo(x)或者A.static_foo(x)來調用.

\ 實例方法 類方法 靜態方法
a = A() a.foo(x) a.class_foo(x) a.static_foo(x)
A 不可用 A.class_foo(x) A.static_foo(x)

更多關於這個問題: 相關連接1 相關連接2

4 類變量和實例變量

類變量:

​ 是可在類的全部實例之間共享的值(也就是說,它們不是單獨分配給每一個實例的)。例以下例中,num_of_instance 就是類變量,用於跟蹤存在着多少個Test 的實例。

實例變量:

實例化以後,每一個實例單獨擁有的變量。

class Test(object):  
    num_of_instance = 0  
    def __init__(self, name):  
        self.name = name  
        Test.num_of_instance += 1  
  
if __name__ == '__main__':  
    print Test.num_of_instance   # 0
    t1 = Test('jack')  
    print Test.num_of_instance   # 1
    t2 = Test('lucy')  
    print t1.name , t1.num_of_instance  # jack 2
    print t2.name , t2.num_of_instance  # lucy 2
複製代碼

補充的例子

class Person:
    name="aaa"

p1=Person()
p2=Person()
p1.name="bbb"
print p1.name  # bbb
print p2.name  # aaa
print Person.name  # aaa
複製代碼

這裏p1.name="bbb"是實例調用了類變量,這其實和上面第一個問題同樣,就是函數傳參的問題,p1.name一開始是指向的類變量name="aaa",可是在實例的做用域裏把類變量的引用改變了,就變成了一個實例變量,self.name再也不引用Person的類變量name了.

能夠看看下面的例子:

class Person:
    name=[]
p1=Person()
p2=Person()
p1.name.append(1)
print p1.name  # [1]
print p2.name  # [1]
print Person.name  # [1]
複製代碼

參考:

5 Python自省

這個也是python彪悍的特性.

自省就是面向對象的語言所寫的程序在運行時,所能知道對象的類型.簡單一句就是運行時可以得到對象的類型.好比type(),dir(),getattr(),hasattr(),isinstance().

a = [1,2,3]
b = {'a':1,'b':2,'c':3}
c = True
print type(a),type(b),type(c) # <type 'list'> <type 'dict'> <type 'bool'>
print isinstance(a,list)  # True
複製代碼

6 字典推導式

可能你見過列表推導時,卻沒有見過字典推導式,在2.7中才加入的:

d = {key: value for (key, value) in iterable}
複製代碼

7 Python中單下劃線和雙下劃線

>>> class MyClass():
...     def __init__(self):
...             self.__superprivate = "Hello"
...             self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}
複製代碼

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

_foo:一種約定,用來指定變量私有.程序員用來指定私有變量的一種方式.不能用from module import * 導入,其餘方面和公有同樣訪問;

__foo:這個有真正的意義:解析器用_classname__foo來代替這個名字,以區別和其餘類相同的命名,它沒法直接像公有成員同樣隨便訪問,經過對象名._類名__xxx這樣的方式能夠訪問.

詳情見:這裏

或者這裏

8 字符串格式化:%和.format

.format在許多方面看起來更便利.對於%最煩人的是它沒法同時傳遞一個變量和元組.你可能會想下面的代碼不會有什麼問題:

"hi there %s" % name
複製代碼

可是,若是name剛好是(1,2,3),它將會拋出一個TypeError異常.爲了保證它老是正確的,你必須這樣作:

"hi there %s" % (name,)   # 提供一個單元素的數組而不是一個參數
複製代碼

可是有點醜..format就沒有這些問題.你給的第二個問題也是這樣,.format好看多了.

你爲何不用它?

  • 不知道它(在讀這個以前)
  • 爲了和Python2.5兼容(譬如logging庫建議使用%(issue #4))

stackoverflow.com/questions/5…

9 迭代器和生成器

這個是stackoverflow裏python排名第一的問題,值得一看

這是中文版

這裏有個關於生成器的建立問題面試官有考: 問: 將列表生成式中[]改爲() 以後數據結構是否改變? 答案:是,從列表變爲生成器

>>> L = [x*x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x*x for x in range(10))
>>> g
<generator object <genexpr> at 0x0000028F8B774200>
複製代碼

經過列表生成式,能夠直接建立一個列表。可是,受到內存限制,列表容量確定是有限的。並且,建立一個包含百萬元素的列表,不只是佔用很大的內存空間,如:咱們只須要訪問前面的幾個元素,後面大部分元素所佔的空間都是浪費的。所以,沒有必要建立完整的列表(節省大量內存空間)。在Python中,咱們能夠採用生成器:邊循環,邊計算的機制—>generator

10 *args and **kwargs

*args**kwargs只是爲了方便並無強制使用它們.

當你不肯定你的函數裏將要傳遞多少參數時你能夠用*args.例如,它能夠傳遞任意數量的參數:

>>> def print_everything(*args):
        for count, thing in enumerate(args):
...         print '{0}. {1}'.format(count, thing)
...
>>> print_everything('apple', 'banana', 'cabbage')
0. apple
1. banana
2. cabbage
複製代碼

類似的,**kwargs容許你使用沒有事先定義的參數名:

>>> def table_things(**kwargs):
...     for name, value in kwargs.items():
...         print '{0} = {1}'.format(name, value)
...
>>> table_things(apple = 'fruit', cabbage = 'vegetable')
cabbage = vegetable
apple = fruit
複製代碼

你也能夠混着用.命名參數首先得到參數值而後全部的其餘參數都傳遞給*args**kwargs.命名參數在列表的最前端.例如:

def table_things(titlestring, **kwargs)
複製代碼

*args**kwargs能夠同時在函數的定義中,可是*args必須在**kwargs前面.

當調用函數時你也能夠用***語法.例如:

>>> def print_three_things(a, b, c):
...     print 'a = {0}, b = {1}, c = {2}'.format(a,b,c)
...
>>> mylist = ['aardvark', 'baboon', 'cat']
>>> print_three_things(*mylist)

a = aardvark, b = baboon, c = cat
複製代碼

就像你看到的同樣,它能夠傳遞列表(或者元組)的每一項並把它們解包.注意必須與它們在函數裏的參數相吻合.固然,你也能夠在函數定義或者函數調用時用*.

參考

11 Python中重載

引自知乎

函數重載主要是爲了解決兩個問題。

  1. 可變參數類型。
  2. 可變參數個數。

另外,一個基本的設計原則是,僅僅當兩個函數除了參數類型和參數個數不一樣之外,其功能是徹底相同的,此時才使用函數重載,若是兩個函數的功能其實不一樣,那麼不該當使用重載,而應當使用一個名字不一樣的函數。

好吧,那麼對於狀況 1 ,函數功能相同,可是參數類型不一樣,python 如何處理?答案是根本不須要處理,由於 python 能夠接受任何類型的參數,若是函數的功能相同,那麼不一樣的參數類型在 python 中極可能是相同的代碼,沒有必要作成兩個不一樣函數。

那麼對於狀況 2 ,函數功能相同,但參數個數不一樣,python 如何處理?你們知道,答案就是缺省參數。對那些缺乏的參數設定爲缺省參數便可解決問題。由於你假設函數功能相同,那麼那些缺乏的參數終歸是須要用的。

好了,鑑於狀況 1 跟 狀況 2 都有了解決方案,python 天然就不須要函數重載了。

12 新式類和舊式類

這個面試官問了,我說了老半天,不知道他問的真正意圖是什麼.

stackoverflow

這篇文章很好的介紹了新式類的特性:

新式類很早在2.2就出現了,因此舊式類徹底是兼容的問題,Python3裏的類所有都是新式類.這裏有一個MRO問題能夠了解下(新式類繼承是根據C3算法,舊式類是深度優先),<Python核心編程>裏講的也不少.

一箇舊式類的深度優先的例子

class A():
    def foo1(self):
        print "A"
class B(A):
    def foo2(self):
        pass
class C(A):
    def foo1(self):
        print "C"
class D(B, C):
    pass

d = D()
d.foo1()

# A
複製代碼

按照經典類的查找順序從左到右深度優先的規則,在訪問d.foo1()的時候,D這個類是沒有的..那麼往上查找,先找到B,裏面沒有,深度優先,訪問A,找到了foo1(),因此這時候調用的是A的foo1(),從而致使C重寫的foo1()被繞過

13 __new____init__的區別

這個__new__確實不多見到,先作了解吧.

  1. __new__是一個靜態方法,而__init__是一個實例方法.
  2. __new__方法會返回一個建立的實例,而__init__什麼都不返回.
  3. 只有在__new__返回一個cls的實例時後面的__init__才能被調用.
  4. 當建立一個新實例時調用__new__,初始化一個實例時用__init__.

stackoverflow

ps: __metaclass__是建立類時起做用.因此咱們能夠分別使用__metaclass__,__new____init__來分別在類建立,實例建立和實例初始化的時候作一些小手腳.

14 單例模式

​ 單例模式是一種經常使用的軟件設計模式。在它的核心結構中只包含一個被稱爲單例類的特殊類。經過單例模式能夠保證系統中一個類只有一個實例並且該實例易於外界訪問,從而方便對實例個數的控制並節約系統資源。若是但願在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。

__new__()__init__()以前被調用,用於生成實例對象。利用這個方法和類的屬性的特色能夠實現設計模式的單例模式。單例模式是指建立惟一對象,單例模式設計的類只能實例 這個絕對常考啊.絕對要記住1~2個方法,當時面試官是讓手寫的.

1 使用__new__方法

class Singleton(object):
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            orig = super(Singleton, cls)
            cls._instance = orig.__new__(cls, *args, **kw)
        return cls._instance

class MyClass(Singleton):
    a = 1
複製代碼

2 共享屬性

建立實例時把全部實例的__dict__指向同一個字典,這樣它們具備相同的屬性和方法.

class Borg(object):
    _state = {}
    def __new__(cls, *args, **kw):
        ob = super(Borg, cls).__new__(cls, *args, **kw)
        ob.__dict__ = cls._state
        return ob

class MyClass2(Borg):
    a = 1
複製代碼

3 裝飾器版本

def singleton(cls):
    instances = {}
    def getinstance(*args, **kw):
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]
    return getinstance

@singleton
class MyClass:
  ...
複製代碼

4 import方法

做爲python的模塊是自然的單例模式

# mysingleton.py
class My_Singleton(object):
    def foo(self):
        pass

my_singleton = My_Singleton()

# to use
from mysingleton import my_singleton

my_singleton.foo()

複製代碼

單例模式伯樂在線詳細解釋

15 Python中的做用域

Python 中,一個變量的做用域老是由在代碼中被賦值的地方所決定的。

當 Python 遇到一個變量的話他會按照這樣的順序進行搜索:

本地做用域(Local)→當前做用域被嵌入的本地做用域(Enclosing locals)→全局/模塊做用域(Global)→內置做用域(Built-in)

16 GIL線程全局鎖

線程全局鎖(Global Interpreter Lock),即Python爲了保證線程安全而採起的獨立線程運行的限制,說白了就是一個核只能在同一時間運行一個線程.對於io密集型任務,python的多線程起到做用,但對於cpu密集型任務,python的多線程幾乎佔不到任何優點,還有可能由於爭奪資源而變慢。

Python 最難的問題

解決辦法就是多進程和下面的協程(協程也只是單CPU,可是能減少切換代價提高性能).

17 協程

簡單點說協程是進程和線程的升級版,進程和線程都面臨着內核態和用戶態的切換問題而耗費許多切換時間,而協程就是用戶本身控制切換的時機,再也不須要陷入系統的內核態.

Python裏最多見的yield就是協程的思想!能夠查看第九個問題.

18 閉包

閉包(closure)是函數式編程的重要的語法結構。閉包也是一種組織代碼的結構,它一樣提升了代碼的可重複使用性。

當一個內嵌函數引用其外部做做用域的變量,咱們就會獲得一個閉包. 總結一下,建立一個閉包必須知足如下幾點:

  1. 必須有一個內嵌函數
  2. 內嵌函數必須引用外部函數中的變量
  3. 外部函數的返回值必須是內嵌函數

感受閉包仍是有難度的,幾句話是說不明白的,仍是查查相關資料.

重點是函數運行後並不會被撤銷,就像16題的instance字典同樣,當函數運行完後,instance並不被銷燬,而是繼續留在內存空間裏.這個功能相似類裏的類變量,只不過遷移到了函數上.

閉包就像個空心球同樣,你知道外面和裏面,但你不知道中間是什麼樣.

19 lambda函數

其實就是一個匿名函數,爲何叫lambda?由於和後面的函數式編程有關.

推薦: 知乎

20 Python函數式編程

這個須要適當的瞭解一下吧,畢竟函數式編程在Python中也作了引用.

推薦: 酷殼

python中函數式編程支持:

filter 函數的功能至關於過濾器。調用一個布爾函數bool_func來迭代遍歷每一個seq中的元素;返回一個使bool_seq返回值爲true的元素的序列。

>>>a = [1,2,3,4,5,6,7]
>>>b = filter(lambda x: x > 5, a)
>>>print b
>>>[6,7]
複製代碼

map函數是對一個序列的每一個項依次執行函數,下面是對一個序列每一個項都乘以2:

>>> a = map(lambda x:x*2,[1,2,3])
>>> list(a)
[2, 4, 6]
複製代碼

reduce函數是對一個序列的每一個項迭代調用函數,下面是求3的階乘:

>>> reduce(lambda x,y:x*y,range(1,4))
6
複製代碼

21 Python裏的拷貝

引用和copy(),deepcopy()的區別

import copy
a = [1, 2, 3, 4, ['a', 'b']]  #原始對象

b = a  #賦值,傳對象的引用
c = copy.copy(a)  #對象拷貝,淺拷貝
d = copy.deepcopy(a)  #對象拷貝,深拷貝

a.append(5)  #修改對象a
a[4].append('c')  #修改對象a中的['a', 'b']數組對象

print 'a = ', a
print 'b = ', b
print 'c = ', c
print 'd = ', d

輸出結果:
a =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
b =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
c =  [1, 2, 3, 4, ['a', 'b', 'c']]
d =  [1, 2, 3, 4, ['a', 'b']]
複製代碼

22 Python垃圾回收機制

Python垃圾回收機制:Python GC主要使用引用計數(reference counting)來跟蹤和回收垃圾。在引用計數的基礎上,經過「標記-清除」(mark and sweep)解決容器對象可能產生的循環引用問題,經過「分代回收」(generation collection)以空間換時間的方法提升垃圾回收效率。

1 引用計數

PyObject是每一個對象必有的內容,其中ob_refcnt就是作爲引用計數。當一個對象有新的引用時,它的ob_refcnt就會增長,當引用它的對象被刪除,它的ob_refcnt就會減小.引用計數爲0時,該對象生命就結束了。

優勢:

  1. 簡單
  2. 實時性

缺點:

  1. 維護引用計數消耗資源
  2. 循環引用

2 標記-清除機制

基本思路是先按需分配,等到沒有空閒內存的時候從寄存器和程序棧上的引用出發,遍歷以對象爲節點、以引用爲邊構成的圖,把全部能夠訪問到的對象打上標記,而後清掃一遍內存空間,把全部沒標記的對象釋放。

3 分代技術

分代回收的總體思想是:將系統中的全部內存塊根據其存活時間劃分爲不一樣的集合,每一個集合就成爲一個「代」,垃圾收集頻率隨着「代」的存活時間的增大而減少,存活時間一般利用通過幾回垃圾回收來度量。

Python默認定義了三代對象集合,索引數越大,對象存活時間越長。

舉例: 當某些內存塊M通過了3次垃圾收集的清洗以後還存活時,咱們就將內存塊M劃到一個集合A中去,而新分配的內存都劃分到集合B中去。當垃圾收集開始工做時,大多數狀況都只對集合B進行垃圾回收,而對集合A進行垃圾回收要隔至關長一段時間後才進行,這就使得垃圾收集機制須要處理的內存少了,效率天然就提升了。在這個過程當中,集合B中的某些內存塊因爲存活時間長而會被轉移到集合A中,固然,集合A中實際上也存在一些垃圾,這些垃圾的回收會由於這種分代的機制而被延遲。

23 read,readline和readlines

  • read 讀取整個文件
  • readline 讀取下一行,使用生成器方法
  • readlines 讀取整個文件到一個迭代器以供咱們遍歷

24 range and xrange

都在循環時使用,xrange內存性能更好。 for i in range(0, 20): for i in xrange(0, 20): What is the difference between range and xrange functions in Python 2.X? range creates a list, so if you do range(1, 10000000) it creates a list in memory with 9999999 elements. xrange is a sequence object that evaluates lazily.

相關連接

數據庫

1 事務

數據庫事務(Database Transaction) ,是指做爲單個邏輯工做單元執行的一系列操做,要麼徹底地執行,要麼徹底地不執行。 完全理解數據庫事務: www.hollischuang.com/archives/89…

2 數據庫索引

推薦:

MySQL索引背後的數據結構及算法原理

彙集索引,非彙集索引,B-Tree,B+Tree,最左前綴原理

3 Redis原理

Redis是什麼?

  1. 是一個徹底開源免費的key-value內存數據庫
  2. 一般被認爲是一個數據結構服務器,主要是由於其有着豐富的數據結構 strings、map、 list、sets、 sorted sets

Redis數據庫

​ 一般侷限點來講,Redis也以消息隊列的形式存在,做爲內嵌的List存在,知足實時的高併發需求。在使用緩存的時候,redis比memcached具備更多的優點,而且支持更多的數據類型,把redis看成一箇中間存儲系統,用來處理高併發的數據庫操做

  • 速度快:使用標準C寫,全部數據都在內存中完成,讀寫速度分別達到10萬/20萬
  • 持久化:對數據的更新採用Copy-on-write技術,能夠異步地保存到磁盤上,主要有兩種策略,一是根據時間,更新次數的快照(save 300 10 )二是基於語句追加方式(Append-only file,aof)
  • 自動操做:對不一樣數據類型的操做都是自動的,很安全
  • 快速的主--從複製,官方提供了一個數據,Slave在21秒即完成了對Amazon網站10G key set的複製。
  • Sharding技術: 很容易將數據分佈到多個Redis實例中,數據庫的擴展是個永恆的話題,在關係型數據庫中,主要是以添加硬件、以分區爲主要技術形式的縱向擴展解決了不少的應用場景,但隨着web2.0、移動互聯網、雲計算等應用的興起,這種擴展模式已經不太適合了,因此近年來,像採用主從配置、數據庫複製形式的,Sharding這種技術把負載分佈到多個特理節點上去的橫向擴展方式用處愈來愈多。

Redis缺點

  • 是數據庫容量受到物理內存的限制,不能用做海量數據的高性能讀寫,所以Redis適合的場景主要侷限在較小數據量的高性能操做和運算上。
  • Redis較難支持在線擴容,在集羣容量達到上限時在線擴容會變得很複雜。爲避免這一問題,運維人員在系統上線時必須確保有足夠的空間,這對資源形成了很大的浪費。

4 樂觀鎖和悲觀鎖

悲觀鎖:假定會發生併發衝突,屏蔽一切可能違反數據完整性的操做

樂觀鎖:假設不會發生併發衝突,只在提交操做時檢查是否違反數據完整性。

樂觀鎖與悲觀鎖的具體區別:

5 MVCC

​ 全稱是Multi-Version Concurrent Control,即多版本併發控制,在MVCC協議下,每一個讀操做會看到一個一致性的snapshot,而且能夠實現非阻塞的讀。MVCC容許數據具備多個版本,這個版本能夠是時間戳或者是全局遞增的事務ID,在同一個時間點,不一樣的事務看到的數據是不一樣的。

MySQL的innodb引擎是如何實現MVCC的

innodb會爲每一行添加兩個字段,分別表示該行建立的版本刪除的版本,填入的是事務的版本號,這個版本號隨着事務的建立不斷遞增。在repeated read的隔離級別(事務的隔離級別請看這篇文章)下,具體各類數據庫操做的實現:

  • select:知足如下兩個條件innodb會返回該行數據:
    • 該行的建立版本號小於等於當前版本號,用於保證在select操做以前全部的操做已經執行落地。
    • 該行的刪除版本號大於當前版本或者爲空。刪除版本號大於當前版本意味着有一個併發事務將該行刪除了。
  • insert:將新插入的行的建立版本號設置爲當前系統的版本號。
  • delete:將要刪除的行的刪除版本號設置爲當前系統的版本號。
  • update:不執行原地update,而是轉換成insert + delete。將舊行的刪除版本號設置爲當前版本號,並將新行insert同時設置建立版本號爲當前版本號。

其中,寫操做(insert、delete和update)執行時,須要將系統版本號遞增。

​ 因爲舊數據並不真正的刪除,因此必須對這些數據進行清理,innodb會開啓一個後臺線程執行清理工做,具體的規則是將刪除版本號小於當前系統版本的行刪除,這個過程叫作purge。

經過MVCC很好的實現了事務的隔離性,能夠達到repeated read級別,要實現serializable還必須加鎖。

參考:MVCC淺析

6 MyISAM和InnoDB

MyISAM 適合於一些須要大量查詢的應用,但其對於有大量寫操做並非很好。甚至你只是須要update一個字段,整個表都會被鎖起來,而別的進程,就算是讀進程都沒法操做直到讀操做完成。另外,MyISAM 對於 SELECT COUNT(*) 這類的計算是超快無比的。

InnoDB 的趨勢會是一個很是複雜的存儲引擎,對於一些小的應用,它會比 MyISAM 還慢。他是它支持「行鎖」 ,因而在寫操做比較多的時候,會更優秀。而且,他還支持更多的高級應用,好比:事務。

mysql 數據庫引擎: MySQL存儲引擎--MyISAM與InnoDB區別:

網絡

1 三次握手

  1. 客戶端經過向服務器端發送一個SYN來建立一個主動打開,做爲三次握手的一部分。客戶端把這段鏈接的序號設定爲隨機數 A。
  2. 服務器端應當爲一個合法的SYN回送一個SYN/ACK。ACK 的確認碼應爲 A+1,SYN/ACK 包自己又有一個隨機序號 B。
  3. 最後,客戶端再發送一個ACK。當服務端受到這個ACK的時候,就完成了三路握手,並進入了鏈接建立狀態。此時包序號被設定爲收到的確認號 A+1,而響應則爲 B+1。

2 四次揮手

注意: 中斷鏈接端能夠是客戶端,也能夠是服務器端. 下面僅以客戶端斷開鏈接舉例, 反之亦然.

  1. 客戶端發送一個數據分段, 其中的 FIN 標記設置爲1. 客戶端進入 FIN-WAIT 狀態. 該狀態下客戶端只接收數據, 再也不發送數據.
  2. 服務器接收到帶有 FIN = 1 的數據分段, 發送帶有 ACK = 1 的剩餘數據分段, 確認收到客戶端發來的 FIN 信息.
  3. 服務器等到全部數據傳輸結束, 向客戶端發送一個帶有 FIN = 1 的數據分段, 並進入 CLOSE-WAIT 狀態, 等待客戶端發來帶有 ACK = 1 的確認報文.
  4. 客戶端收到服務器發來帶有 FIN = 1 的報文, 返回 ACK = 1 的報文確認, 爲了防止服務器端未收到須要重發, 進入 TIME-WAIT 狀態. 服務器接收到報文後關閉鏈接. 客戶端等待 2MSL 後未收到回覆, 則認爲服務器成功關閉, 客戶端關閉鏈接.

圖解:

3 ARP協議

地址解析協議(Address Resolution Protocol),其基本功能爲透過目標設備的IP地址,查詢目標的MAC地址,以保證通訊的順利進行。它是IPv4網絡層必不可少的協議,不過在IPv6中已再也不適用,並被鄰居發現協議(NDP)所替代。

4 urllib和urllib2的區別

這個面試官確實問過,當時答的urllib2能夠Post而urllib不能夠.

  1. urllib提供urlencode方法用來GET查詢字符串的產生,而urllib2沒有。這是爲什麼urllib常和urllib2一塊兒使用的緣由。
  2. urllib2能夠接受一個Request類的實例來設置URL請求的headers,urllib僅能夠接受URL。這意味着,你不能夠假裝你的User Agent字符串等。

5 Post和Get

GET和POST有什麼區別?及爲何網上的多數答案都是錯的 知乎回答

get: RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1 post: RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1

6 Cookie和Session

Cookie Session
儲存位置 客戶端 服務器端
目的 跟蹤會話,也能夠保存用戶偏好設置或者保存用戶名密碼等 跟蹤會話
安全性 不安全 安全

session技術是要使用到cookie的,之因此出現session技術,主要是爲了安全。

7 apache和nginx的區別

nginx 相對 apache 的優勢:

  • 輕量級,一樣起web 服務,比apache 佔用更少的內存及資源
  • 抗併發,nginx 處理請求是異步非阻塞的,支持更多的併發鏈接,而apache 則是阻塞型的,在高併發下nginx 能保持低資源低消耗高性能
  • 配置簡潔
  • 高度模塊化的設計,編寫模塊相對簡單
  • 社區活躍

apache 相對nginx 的優勢:

  • rewrite ,比nginx 的rewrite 強大
  • 模塊超多,基本想到的均可以找到
  • 少bug ,nginx 的bug 相對較多
  • 超穩定

8 網站用戶密碼保存

  1. 明文保存
  2. 明文hash後保存,如md5
  3. MD5+Salt方式,這個salt能夠隨機
  4. 知乎使用了Bcrypy(好像)加密

9 HTTP和HTTPS

狀態碼 定義
1xx 報告 接收到請求,繼續進程
2xx 成功 步驟成功接收,被理解,並被接受
3xx 重定向 爲了完成請求,必須採起進一步措施
4xx 客戶端出錯 請求包括錯的順序或不能完成
5xx 服務器出錯 服務器沒法完成顯然有效的請求

403: Forbidden 404: Not Found

HTTPS握手,對稱加密,非對稱加密,TLS/SSL,RSA

10 XSRF和XSS

  • CSRF(Cross-site request forgery)跨站請求僞造
  • XSS(Cross Site Scripting)跨站腳本攻擊

CSRF重點在請求,XSS重點在腳本

11 冪等 Idempotence

HTTP方法的冪等性是指一次和屢次請求某一個資源應該具備一樣的反作用。(注意是反作用)

GET http://www.bank.com/account/123456,不會改變資源的狀態,不論調用一次仍是N次都沒有反作用。請注意,這裏強調的是一次和N次具備相同的反作用,而不是每次GET的結果相同。GET http://www.news.com/latest-news這個HTTP請求可能會每次獲得不一樣的結果,但它自己並無產生任何反作用,於是是知足冪等性的。

DELETE方法用於刪除資源,有反作用,但它應該知足冪等性。好比:DELETE http://www.forum.com/article/4231,調用一次和N次對系統產生的反作用是相同的,即刪掉id爲4231的帖子;所以,調用者能夠屢次調用或刷新頁面而沒必要擔憂引發錯誤。

POST所對應的URI並不是建立的資源自己,而是資源的接收者。好比:POST http://www.forum.com/articles的語義是在http://www.forum.com/articles下建立一篇帖子,HTTP響應中應包含帖子的建立狀態以及帖子的URI。兩次相同的POST請求會在服務器端建立兩份資源,它們具備不一樣的URI;因此,POST方法不具有冪等性。

PUT所對應的URI是要建立或更新的資源自己。好比:PUT http://www.forum/articles/4231的語義是建立或更新ID爲4231的帖子。對同一URI進行屢次PUT的反作用和一次PUT是相同的;所以,PUT方法具備冪等性。

12 RESTful架構(SOAP,RPC)

推薦:

13 SOAP

SOAP(原爲Simple Object Access Protocol的首字母縮寫,即簡單對象訪問協議)是交換數據的一種協議規範,使用在計算機網絡Web服務(web service)中,交換帶結構信息。SOAP爲了簡化網頁服務器(Web Server)從XML數據庫中提取數據時,節省去格式化頁面時間,以及不一樣應用程序之間按照HTTP通訊協議,聽從XML格式執行資料互換,使其抽象於語言實現、平臺和硬件。

14 RPC

RPC(Remote Procedure Call Protocol)——遠程過程調用協議,它是一種經過網絡從遠程計算機程序上請求服務,而不須要了解底層網絡技術的協議。RPC協議假定某些傳輸協議的存在,如TCP或UDP,爲通訊程序之間攜帶信息數據。在OSI網絡通訊模型中,RPC跨越了傳輸層和應用層。RPC使得開發包括網絡分佈式多程序在內的應用程序更加容易。

總結:服務提供的兩大流派.傳統意義以方法調用爲導向通稱RPC。爲了企業SOA,若干廠商聯合推出webservice,制定了wsdl接口定義,傳輸soap.當互聯網時代,臃腫SOA被簡化爲http+xml/json.可是簡化出現各類混亂。以資源爲導向,任何操做無非是對資源的增刪改查,因而統一的REST出現了.

進化的順序: RPC -> SOAP -> RESTful

15 CGI和WSGI

CGI是通用網關接口,是鏈接web服務器和應用程序的接口,用戶經過CGI來獲取動態數據或文件等。 CGI程序是一個獨立的程序,它能夠用幾乎全部語言來寫,包括perl,c,lua,python等等。

WSGI, Web Server Gateway Interface,是Python應用程序或框架和Web服務器之間的一種接口,WSGI的其中一個目的就是讓用戶能夠用統一的語言(Python)編寫先後端。

官方說明:PEP-3333

16 中間人攻擊

在GFW裏家常便飯的,呵呵.

中間人攻擊(Man-in-the-middle attack,一般縮寫爲MITM)是指攻擊者與通信的兩端分別建立獨立的聯繫,並交換其所收到的數據,使通信的兩端認爲他們正在經過一個私密的鏈接與對方直接對話,但事實上整個會話都被攻擊者徹底控制。

17 c10k問題

所謂c10k問題,指的是服務器同時支持成千上萬個客戶端的問題,也就是concurrent 10 000 connection(這也是c10k這個名字的由來)。 推薦:

18 socket

推薦:

Socket=Ip address+ TCP/UDP + port

19 瀏覽器緩存

推薦:

304 Not Modified

20 HTTP1.0和HTTP1.1

推薦:

  1. 請求頭Host字段,一個服務器多個網站
  2. 長連接
  3. 文件斷點續傳
  4. 身份認證,狀態管理,Cache緩存

HTTP請求8種方法介紹 HTTP/1.1協議中共定義了8種HTTP請求方法,HTTP請求方法也被叫作「請求動做」,不一樣的方法規定了不一樣的操做指定的資源方式。服務端也會根據不一樣的請求方法作不一樣的響應。

GET GET請求會顯示請求指定的資源。通常來講GET方法應該只用於數據的讀取,而不該當用於會產生反作用的非冪等的操做中。 GET會方法請求指定的頁面信息,並返回響應主體,GET被認爲是不安全的方法,由於GET方法會被網絡蜘蛛等任意的訪問。

HEAD HEAD方法與GET方法同樣,都是向服務器發出指定資源的請求。可是,服務器在響應HEAD請求時不會回傳資源的內容部分,即:響應主體。這樣,咱們能夠不傳輸所有內容的狀況下,就能夠獲取服務器的響應頭信息。HEAD方法常被用於客戶端查看服務器的性能。

POST POST請求會 向指定資源提交數據,請求服務器進行處理,如:表單數據提交、文件上傳等,請求數據會被包含在請求體中。POST方法是非冪等的方法,由於這個請求可能會建立新的資源或/和修改現有資源。

PUT PUT請求會身向指定資源位置上傳其最新內容,PUT方法是冪等的方法。經過該方法客戶端能夠將指定資源的最新數據傳送給服務器取代指定的資源的內容。

DELETE DELETE請求用於請求服務器刪除所請求URI(統一資源標識符,Uniform Resource Identifier)所標識的資源。DELETE請求後指定資源會被刪除,DELETE方法也是冪等的。

CONNECT CONNECT方法是HTTP/1.1協議預留的,可以將鏈接改成管道方式的代理服務器。一般用於SSL加密服務器的連接與非加密的HTTP代理服務器的通訊。

OPTIONS OPTIONS請求與HEAD相似,通常也是用於客戶端查看服務器的性能。 這個方法會請求服務器返回該資源所支持的全部HTTP請求方法,該方法會用’*’來代替資源名稱,向服務器發送OPTIONS請求,能夠測試服務器功能是否正常。JavaScript的XMLHttpRequest對象進行CORS跨域資源共享時,就是使用OPTIONS方法發送嗅探請求,以判斷是否有對指定資源的訪問權限。 容許

TRACE TRACE請求服務器回顯其收到的請求信息,該方法主要用於HTTP請求的測試或診斷。 HTTP/1.1以後增長的方法 在HTTP/1.1標準制定以後,又陸續擴展了一些方法。其中使用中較多的是 PATCH 方法: PATCH PATCH方法出現的較晚,它在2010年的RFC 5789標準中被定義。PATCH請求與PUT請求相似,一樣用於資源的更新。兩者有如下兩點不一樣: 但PATCH通常用於資源的部分更新,而PUT通常用於資源的總體更新。 當資源不存在時,PATCH會建立一個新的資源,而PUT只會對已在資源進行更新。

21 Ajax

AJAX,Asynchronous JavaScript and XML(異步的 JavaScript 和 XML), 是與在不從新加載整個頁面的狀況下,與服務器交換數據並更新部分網頁的技術。

*NIX

unix進程間通訊方式(IPC)

  1. 管道(Pipe):管道可用於具備親緣關係進程間的通訊,容許一個進程和另外一個與它有共同祖先的進程之間進行通訊。
  2. 命名管道(named pipe):命名管道克服了管道沒有名字的限制,所以,除具備管道所具備的功能外,它還容許無親緣關係進程間的通訊。命名管道在文件系統中有對應的文件名。命名管道經過命令mkfifo或系統調用mkfifo來建立。
  3. 信號(Signal):信號是比較複雜的通訊方式,用於通知接受進程有某種事件發生,除了用於進程間通訊外,進程還能夠發送信號給進程自己;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數sigaction(實際上,該函數是基於BSD的,BSD爲了實現可靠信號機制,又可以統一對外接口,用sigaction函數從新實現了signal函數)。
  4. 消息(Message)隊列:消息隊列是消息的連接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程能夠向隊列中添加消息,被賦予讀權限的進程則能夠讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩衝區大小受限等缺
  5. 共享內存:使得多個進程能夠訪問同一塊內存空間,是最快的可用IPC形式。是針對其餘通訊機制運行效率較低而設計的。每每與其它通訊機制,如信號量結合使用,來達到進程間的同步及互斥。
  6. 內存映射(mapped memory):內存映射容許任何多個進程間通訊,每個使用該機制的進程經過把一個共享的文件映射到本身的進程地址空間來實現它。
  7. 信號量(semaphore):主要做爲進程間以及同一進程不一樣線程之間的同步手段。
  8. 套接口(Socket):更爲通常的進程間通訊機制,可用於不一樣機器之間的進程間通訊。起初是由Unix系統的BSD分支開發出來的,但如今通常能夠移植到其它類Unix系統上:Linux和System V的變種都支持套接字。
相關文章
相關標籤/搜索