python的函數(shù)參數(shù)傳遞
看兩個(gè)例子:
a = 1
def fun(a):
a = 2
fun(a)
print a # 1
a = []
def fun(a):
a.append(1)
fun(a)
print a # [1]
所有變量都可以理解為內(nèi)存中一個(gè)對(duì)象的“引用”,或者时捌,可以看做C中的viod*的感覺
這里記住的是類型是屬于對(duì)象的,而不是變量压固。而對(duì)象有兩種,“可更改”(mutable)與“不可更改”(immutable)對(duì)象巩踏。在python中秃诵,strings, tuples, 和numbers是不可更改的對(duì)象,而list,dict等則是可以修改的對(duì)象塞琼。(這就是這個(gè)問題的重點(diǎn))
當(dāng)一個(gè)引用傳遞給函數(shù)的時(shí)候,函數(shù)自動(dòng)復(fù)制一份引用,這個(gè)函數(shù)里的引用和外邊的引用沒有半毛關(guān)系了.所以第一個(gè)例子里函數(shù)把引用指向了一個(gè)不可變對(duì)象,當(dāng)函數(shù)返回的時(shí)候,外面的引用沒半毛感覺.而第二個(gè)例子就不一樣了,函數(shù)內(nèi)的引用指向的是可變對(duì)象,對(duì)它的操作就和定位了指針地址一樣,在內(nèi)存里進(jìn)行修改.
如果還不明白的話,這里有更好的解釋: http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference
python中的元類(metaclass)
這個(gè)非常的不常用,但是像ORM這種復(fù)雜的結(jié)構(gòu)還是會(huì)需要的,詳情請(qǐng)看:《深刻理解Python中的元類(metaclass)》
@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()
這里先理解下函數(shù)參數(shù)里面的self和cls.這個(gè)self和cls是對(duì)類或者實(shí)例的綁定,對(duì)于一般的函數(shù)來說我們可以這么調(diào)用foo(x),這個(gè)函數(shù)就是最常用的,它的工作跟任何東西(類,實(shí)例)無關(guān).對(duì)于實(shí)例方法,我們知道在類里每次定義方法的時(shí)候都需要綁定這個(gè)實(shí)例,就是foo(self, x),為什么要這么做呢?因?yàn)閷?shí)例方法的調(diào)用離不開實(shí)例,我們需要把實(shí)例自己傳給函數(shù),調(diào)用的時(shí)候是這樣的a.foo(x)(其實(shí)是foo(a, x)).類方法一樣,只不過它傳遞的是類而不是實(shí)例,A.class_foo(x).注意這里的self和cls可以替換別的參數(shù),但是python的約定是這倆,還是不要改的好.
對(duì)于靜態(tài)方法其實(shí)和普通的方法一樣,不需要對(duì)誰進(jìn)行綁定,唯一的區(qū)別是調(diào)用的時(shí)候需要使用a.static_foo(x)或者A.static_foo(x)來調(diào)用.
\ | 實(shí)例方法 | 類方法 | 靜態(tài)方法 |
---|---|---|---|
a = A() |
a.foo(x) |
A.class_foo(x) |
A.static_foo(x) |
A |
不可用 | A.class_foo(x) |
A.static_foo(x) |
更多關(guān)于這個(gè)問題:http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python
類變量和實(shí)例變量
class Person:
name="aaa"
p1=Person() #類變量
p2=Person() #類變量
p1.name="bbb" #實(shí)例變量
print p1.name # bbb
print p2.name # aaa
print Person.name # aaa
類變量就是供類使用的變量,實(shí)例變量就是供實(shí)例使用的.
這里p1.name="bbb"是實(shí)例調(diào)用了類變量,這其實(shí)和上面第一個(gè)問題一樣,就是函數(shù)傳參的問題,p1.name一開始是指向的類變量name="aaa",但是在實(shí)例的作用域里把類變量的引用改變了,就變成了一個(gè)實(shí)例變量,self.name不再引用Person的類變量name了.
==可以看看下面的例子: (need check)==
==python中l(wèi)ist是mutable的類變量菠净, 實(shí)例化之后也是mutable的, 所以對(duì)第一個(gè)實(shí)例的name操作彪杉, 也會(huì)引起類變量以及其它的實(shí)例中l(wèi)ist的更改==
==如何避免==
class Person:
name=[]
p1=Person()
p2=Person()
p1.name.append(1)
print p1.name # [1]
print p2.name # [1]
print Person.name # [1]
參考:http://stackoverflow.com/questions/6470428/catch-multiple-exceptions-in-one-line-except-block
python自省
這個(gè)也是python彪悍的特性.
自省就是面向?qū)ο蟮恼Z言所寫的程序在運(yùn)行時(shí),所能知道對(duì)象的類型.簡(jiǎn)單一句就是運(yùn)行時(shí)能夠獲得對(duì)象的類型.比如type(),dir(),getattr(),hasattr(),isinstance().
字典推導(dǎo)式:
d = {key: value for (key, value) in iterable}
你可以用任何方式的迭代器(元組,列表,生成器..),只要可迭代對(duì)象的元素中有兩個(gè)值.
d = {value: foo(value) for value in sequence if bar(value)}
def key_value_gen(k):
yield chr(k+65)
yield chr((k+13)%26+65)
d = dict(map(key_value_gen, range(26)))
python中單下劃線和雙下劃線
這篇文章討論P(yáng)ython中下劃線_的使用毅往。跟Python中很多用法類似,下劃線_的不同用法絕大部分(不全是)都是一種慣例約定派近。
單下劃線(_)
主要有三種情況:
- 解釋器中
_符號(hào)是指交互解釋器中最后一次執(zhí)行語句的返回結(jié)果攀唯。這種用法最初出現(xiàn)在CPython解釋器中,其他解釋器后來也都跟進(jìn)了渴丸。
>>> _
Traceback (most recent call last):
File "", line 1, in
NameError: name '_' is not defined
>>> 42
>>> _
42
>>> 'alright!' if _ else ':('
'alright!'
>>> _
'alright!'
- 作為名稱使用
這個(gè)跟上面有點(diǎn)類似侯嘀。_用作被丟棄的名稱。按照慣例曙强,這樣做可以讓閱讀你代碼的人知道残拐,這是個(gè)不會(huì)被使用的特定名稱。舉個(gè)例子碟嘴,你可能無所謂一個(gè)循環(huán)計(jì)數(shù)的值:
n = 42
for _ in range(n):
do_something()
- i18n
_還可以被用作函數(shù)名。這種情況囊卜,單下劃線經(jīng)常被用作國(guó)際化和本地化字符串翻譯查詢的函數(shù)名娜扇。這種慣例好像起源于C語言。舉個(gè)例子栅组,在 Django documentation for translation 中你可能會(huì)看到:
from django.utils.translation import ugettext as _
from django.http import HttpResponse
def my_view(request):
output = _("Welcome to my site.")
return HttpResponse(output)
第二種和第三種用法會(huì)引起沖突雀瓢,所以在任意代碼塊中,如果使用了_作i18n翻譯查詢函數(shù)玉掸,就應(yīng)該避免再用作被丟棄的變量名刃麸。
單下劃線前綴的名稱(例如_shahriar)
以單下劃線做前綴的名稱指定了這個(gè)名稱是“私有的”。在 有些 導(dǎo)入import * 的場(chǎng)景中司浪,下一個(gè)使用你代碼的人(或者你本人)會(huì)明白這個(gè)名稱僅內(nèi)部使用泊业。Python documentation里面寫道:
a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.
之所以說在在 有些 import * 的場(chǎng)景,是因?yàn)閷?dǎo)入時(shí)解釋器確實(shí)對(duì)單下劃線開頭的名稱做了處理啊易。如果你這么寫from <module/package> import *
吁伺,任何以單下劃線開頭的名稱都不會(huì)被導(dǎo)入,除非模塊/包的__all__
列表明確包含了這些名稱租谈。更多相關(guān)信息見““Importing * in Python”
篮奄。
雙下劃線前綴的名稱(例如__shahriar
)
以雙下劃線做前綴的名稱(特別是方法名)并不是一種慣例;它對(duì)解釋器有特定含義。Python會(huì)改寫這些名稱窟却,以免與子類中定義的名稱產(chǎn)生沖突昼丑。Python documentation中提到,任何__spam這種形式(至少以兩個(gè)下劃線做開頭夸赫,絕大部分都還有一個(gè)下劃線做結(jié)尾)的標(biāo)識(shí)符菩帝,都會(huì)文本上被替換為_classname__spam,其中classname是當(dāng)前類名憔足,并帶上一個(gè)下劃線做前綴胁附。
看下面這個(gè)例子:
>>> class A(object):
... def _internal_use(self):
... pass
... def __method_name(self):
... pass
...
>>> dir(A())
['_A__method_name', ..., '_internal_use']
正如所料,_internal_
use沒有變化滓彰,但__method_name
被改寫成了_ClassName__method_name
】仄蓿現(xiàn)在創(chuàng)建一個(gè)A的子類B(這可不是個(gè)好名字),就不會(huì)輕易的覆蓋掉A中的__method_name
了:
>>> class B(A):
... def __method_name(self):
... pass
...
>>> dir(B())
['_A__method_name', '_B__method_name', ..., '_internal_use']
這種特定的行為差不多等價(jià)于Java中的final方法和C++中的正常方法(非虛方法)揭绑。
前后都帶有雙下劃線的名稱(例如__init__
)
這些是Python的特殊方法名弓候,這僅僅是一種慣例,一種確保Python系統(tǒng)中的名稱不會(huì)跟用戶自定義的名稱發(fā)生沖突的方式他匪。通常你可以覆寫這些方法菇存,在Python調(diào)用它們時(shí),產(chǎn)生你想得到的行為邦蜜。例如依鸥,當(dāng)寫一個(gè)類的時(shí)候經(jīng)常會(huì)覆寫__init__
方法。
你也可以寫出自己的“特殊方法”名(但是別這么做):
>>> class C(object):
... def __mine__(self):
... pass
...
>>> dir(C)
... [..., '__mine__', ...]
還是不要這樣寫方法名悼沈,只讓Python定義的特殊方法名使用這種慣例吧贱迟。
或者: http://www.zhihu.com/question/19754941
字符串格式化:%和.format
.format在許多方面看起來更便利.對(duì)于%最煩人的是它無法同時(shí)傳遞一個(gè)變量和元組.你可能會(huì)想下面的代碼不會(huì)有什么問題:
hi there %s" % name
但是,如果name恰好是(1,2,3),它將會(huì)拋出一個(gè)TypeError異常.為了保證它總是正確的,你必須這樣做:
hi there %s" % (name,) # 提供一個(gè)單元素的數(shù)組而不是一個(gè)參數(shù)
但是有點(diǎn)丑..format就沒有這些問題.你給的第二個(gè)問題也是這樣,.format好看多了.
你為什么不用它?
不知道它(在讀這個(gè)之前)
為了和Python2.5兼容(譬如logging庫(kù)建議使用%(issue #4))
http://stackoverflow.com/questions/5082452/python-string-formatting-vs-format
迭代器和生成器
這個(gè)是stackoverflow里python排名第一的問題,值得一看: http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python
這是中文版: http://taizilongxu.gitbooks.io/stackoverflow-about-python/content/1/README.html
Iterables
當(dāng)你創(chuàng)建了一個(gè)列表,你可以一個(gè)一個(gè)的讀取它的每一項(xiàng),這叫做iteration:
>>> mylist = [1, 2, 3]
>>> for i in mylist:
... print(i)
1
2
3
Mylist是可迭代的.當(dāng)你用列表推導(dǎo)式的時(shí)候,你就創(chuàng)建了一個(gè)列表,而這個(gè)列表也是可迭代的:
>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
... print(i)
0
1
4
所有你可以用在for...in...語句中的都是可迭代的:比如lists,strings,files...因?yàn)檫@些可迭代的對(duì)象你可以隨意的讀取所以非常方便易用,但是你必須把它們的值放到內(nèi)存里,當(dāng)它們有很多值時(shí)就會(huì)消耗太多的內(nèi)存.
Generators
生成器也是迭代器的一種,但是你只能迭代它們一次.原因很簡(jiǎn)單,因?yàn)樗鼈儾皇侨看嬖趦?nèi)存里,它們只在要調(diào)用的時(shí)候在內(nèi)存里生成:
>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
... print(i)
0
1
4
生成器和迭代器的區(qū)別就是用()代替[],還有你不能用for i in mygenerator第二次調(diào)用生成器:首先計(jì)算0,然后會(huì)在內(nèi)存里丟掉0去計(jì)算1,直到計(jì)算完4.
Yield
Yield的用法和關(guān)鍵字return差不多,下面的函數(shù)將會(huì)返回一個(gè)生成器:
>>> def createGenerator():
... mylist = range(3)
... for i in mylist:
... yield i*i
...
>>> mygenerator = createGenerator() # 創(chuàng)建生成器
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
... print(i)
0
1
4
在這里這個(gè)例子好像沒什么用,不過當(dāng)你的函數(shù)要返回一個(gè)非常大的集合并且你希望只讀一次的話,那么它就非常的方便了.
要理解Yield你必須先理解當(dāng)你調(diào)用函數(shù)的時(shí)候,函數(shù)里的代碼并沒有運(yùn)行.函數(shù)僅僅返回生成器對(duì)象,這就是它最微妙的地方:-)
然后呢,每當(dāng)for語句迭代生成器的時(shí)候你的代碼才會(huì)運(yùn)轉(zhuǎn).
現(xiàn)在,到了最難的部分:
當(dāng)for語句第一次調(diào)用函數(shù)里返回的生成器對(duì)象,函數(shù)里的代碼就開始運(yùn)作,直到碰到y(tǒng)ield,然后會(huì)返回本次循環(huán)的第一個(gè)返回值.所以下一次調(diào)用也將運(yùn)行一次循環(huán)然后返回下一個(gè)值,直到?jīng)]有值可以返回.
一旦函數(shù)運(yùn)行并沒有碰到y(tǒng)eild語句就認(rèn)為生成器已經(jīng)為空了.原因有可能是循環(huán)結(jié)束或者沒有滿足if/else
之類的.
Itertools你的好基友
itertools模塊包含了一些特殊的函數(shù)可以操作可迭代對(duì)象.有沒有想過復(fù)制一個(gè)生成器?鏈接兩個(gè)生成器?把嵌套列表里的值組織成一個(gè)列表?Map/Zip還不用創(chuàng)建另一個(gè)列表?
來吧import itertools
來一個(gè)例子?讓我們看看4匹馬比賽有多少個(gè)排名結(jié)果:
>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
(1, 2, 4, 3),
(1, 3, 2, 4),
(1, 3, 4, 2),
(1, 4, 2, 3),
(1, 4, 3, 2),
(2, 1, 3, 4),
(2, 1, 4, 3),
(2, 3, 1, 4),
(2, 3, 4, 1),
(2, 4, 1, 3),
(2, 4, 3, 1),
(3, 1, 2, 4),
(3, 1, 4, 2),
(3, 2, 1, 4),
(3, 2, 4, 1),
(3, 4, 1, 2),
(3, 4, 2, 1),
(4, 1, 2, 3),
(4, 1, 3, 2),
(4, 2, 1, 3),
(4, 2, 3, 1),
(4, 3, 1, 2),
(4, 3, 2, 1)]
理解迭代的內(nèi)部機(jī)制
迭代是可迭代對(duì)象(對(duì)應(yīng)iter()方法)和迭代器(對(duì)應(yīng)next()方法)的一個(gè)過程.可迭代對(duì)象就是任何你可以迭代的對(duì)象(廢話啊).迭代器就是可以讓你迭代可迭代對(duì)象的對(duì)象(有點(diǎn)繞口,意思就是這個(gè)意思)
*args and **kwargs
用*args和**kwargs只是為了方便并沒有強(qiáng)制使用它們.
當(dāng)你不確定你的函數(shù)里將要傳遞多少參數(shù)時(shí)你可以用*args.例如,它可以傳遞任意數(shù)量的參數(shù):
>>> 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允許你使用沒有事先定義的參數(shù)名:
>>> 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 必須放在參數(shù)列表的后面。
面向切面編程AOP和裝飾器
這個(gè)AOP一聽起來有點(diǎn)懵,同學(xué)面阿里的時(shí)候就被問懵了…
- 裝飾器就是把其它函數(shù)當(dāng)參數(shù)的函數(shù)絮供。
裝飾器是一個(gè)很著名的設(shè)計(jì)模式衣吠,經(jīng)常被用于有切面需求的場(chǎng)景,較為經(jīng)典的有插入日志壤靶、性能測(cè)試缚俏、事務(wù)處理等。裝飾器是解決這類問題的絕佳設(shè)計(jì)贮乳,有了裝飾器忧换,我們就可以抽離出大量函數(shù)中與函數(shù)功能本身無關(guān)的雷同代碼并繼續(xù)重用。概括的講塘揣,裝飾器的作用就是為已經(jīng)存在的對(duì)象添加額外的功能包雀。
這個(gè)問題比較大,推薦: http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python
中文: http://taizilongxu.gitbooks.io/stackoverflow-about-python/content/3/README.html
- 看一個(gè)簡(jiǎn)單的例子
# 字體變粗裝飾器
def makebold(fn):
# 裝飾器將返回新的函數(shù)
def wrapper():
# 在之前或者之后插入新的代碼
return "<b>" + fn() + "</b>"
return wrapper
# 斜體裝飾器
def makeitalic(fn):
# 裝飾器將返回新的函數(shù)
def wrapper():
# 在之前或者之后插入新的代碼
return "<i>" + fn() + "</i>"
return wrapper
@makebold
@makeitalic
def say():
return "hello"
print say()
輸出: <b><i>hello</i></b>
這相當(dāng)于
def say():
return "hello"
say = makebold(makeitalic(say))
print say()
輸出: <b><i>hello</i></b>
- 用法:
1. 傳統(tǒng)用法是給外部的不可更改的庫(kù)做擴(kuò)展
2. Django用裝飾器管理緩存和試圖的權(quán)限.
3. Twisted用來修改異步函數(shù)的調(diào)用.
4. etc.
# 鴨子類型
“當(dāng)看到一只鳥走起來像鴨子、游泳起來像鴨子亲铡、叫起來也像鴨子才写,那么這只鳥就可以被稱為鴨子葡兑。”
我們并不關(guān)心對(duì)象是什么類型赞草,到底是不是鴨子讹堤,只關(guān)心行為。
比如在python中厨疙,有很多file-like的東西洲守,比如StringIO,GzipFile,socket。它們有很多相同的方法沾凄,我們把它們當(dāng)作文件使用梗醇。
又比如list.extend()方法中,我們并不關(guān)心它的參數(shù)是不是list,只要它是可迭代的,所以它的參數(shù)可以是list/tuple/dict/字符串/生成器等.
鴨子類型在動(dòng)態(tài)語言中經(jīng)常使用,非常靈活撒蟀,使得python不想java那樣專門去弄一大堆的設(shè)計(jì)模式叙谨。
# Python中重載
引自知乎:http://www.zhihu.com/question/20053359
函數(shù)重載主要是為了解決兩個(gè)問題:
- 可變參數(shù)類型
- 可變參數(shù)個(gè)數(shù)
另外,一個(gè)基本的設(shè)計(jì)原則是保屯,僅僅當(dāng)兩個(gè)函數(shù)除了參數(shù)類型和參數(shù)個(gè)數(shù)不同以外手负,其功能是完全相同的,此時(shí)才使用函數(shù)重載姑尺,如果兩個(gè)函數(shù)的功能其實(shí)不同竟终,那么不應(yīng)當(dāng)使用重載,而應(yīng)當(dāng)使用一個(gè)名字不同的函數(shù)切蟋。
好吧统捶,那么對(duì)于情況 1 ,函數(shù)功能相同柄粹,但是參數(shù)類型不同瘾境,python 如何處理?答案是根本不需要處理镰惦,因?yàn)?python 可以接受任何類型的參數(shù),如果函數(shù)的功能相同犬绒,那么不同的參數(shù)類型在 python 中很可能是相同的代碼旺入,沒有必要做成兩個(gè)不同函數(shù)。
那么對(duì)于情況 2 凯力,函數(shù)功能相同茵瘾,但參數(shù)個(gè)數(shù)不同,python 如何處理咐鹤?大家知道拗秘,答案就是缺省參數(shù)。對(duì)那些缺少的參數(shù)設(shè)定為缺省參數(shù)即可解決問題祈惶。因?yàn)槟慵僭O(shè)函數(shù)功能相同雕旨,那么那些缺少的參數(shù)終歸是需要用的扮匠。
好了,鑒于情況 1 跟 情況 2 都有了解決方案凡涩,==python 自然就不需要函數(shù)重載了==
# 新式類與舊式類
這個(gè)面試官問了,我說了老半天,不知道他問的真正意圖是什么.
stackoverflow(http://stackoverflow.com/questions/54867/what-is-the-difference-between-old-style-and-new-style-classes-in-python)
這篇文章很好的介紹了新式類的特性: http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html
簡(jiǎn)單的說棒搜,新式類是在創(chuàng)建的時(shí)候繼承內(nèi)置object對(duì)象(或者是從內(nèi)置類型,如list,dict等)活箕,而經(jīng)典類是直
接聲明的力麸。使用dir()方法也可以看出新式類中定義很多新的屬性和方法,而經(jīng)典類好像就2個(gè):
新式類很早在2.2就出現(xiàn)了,所以舊式類完全是兼容的問題,Python3里的類全部都是新式類.這里有一個(gè)MRO問題可以了解下(新式類是廣度優(yōu)先,舊式類是深度優(yōu)先),<Python核心編程>里講的也很多.
新式類
class C(object):
pass
經(jīng)典類
class B:
pass
# `__new__`和`__init__`的區(qū)別
這個(gè)`__new__`確實(shí)很少見到,先做了解吧.
`__new__`是一個(gè)靜態(tài)方法,而`__init__`是一個(gè)實(shí)例方法.
`__new__`方法會(huì)返回一個(gè)創(chuàng)建的實(shí)例,而`__init__`什么都不返回.
只有在`__new__`返回一個(gè)cls的實(shí)例時(shí)后面的`__init__`才能被調(diào)用.
當(dāng)創(chuàng)建一個(gè)新實(shí)例時(shí)調(diào)用`__new__`,初始化一個(gè)實(shí)例時(shí)用`__init__`.
stackoverflow(http://stackoverflow.com/questions/674304/pythons-use-of-new-and-init)
ps: `__metaclass__`是創(chuàng)建類時(shí)起作用.所以我們可以分別使用`__metaclass__`,`__new__`和`__init__`來分別在類創(chuàng)建,實(shí)例創(chuàng)建和實(shí)例初始化的時(shí)候做一些小手腳.
# 單例模式
==這個(gè)絕對(duì)長(zhǎng)考育韩, 絕對(duì)要記住1~2個(gè)方法.==
所謂單例克蚂,是指一個(gè)類的實(shí)例從始至終只能被創(chuàng)建一次。
## 使用`__new__`方法
class Singleton(object):
def new(cls,args,kwargs):
if not hasattr(cls,'_inst'):
cls._inst=super(Singleton,cls).new(cls,args,**kwargs)
return cls._inst
if name=='main':
class A(Singleton):
def init(self,s):
self.s=s
a=A('apple')
b=A('banana')
print id(a),a.s
print id(b),b.s
結(jié)果:
29922256 banana
29922256 banana
通過`__new__`方法筋讨,將類的實(shí)例在創(chuàng)建的時(shí)候綁定到類屬性`_inst`上埃叭。如果`cls._inst`為None,說明類還未實(shí)例化版仔,實(shí)例化并將實(shí)例綁定到`cls._inst`游盲,以后每次實(shí)例化的時(shí)候都返回第一次實(shí)例化創(chuàng)建的實(shí)例。注意從Singleton派生子類的時(shí)候蛮粮,不要重載`__new__`益缎。
## 共享屬性
有時(shí)候我們并不關(guān)心生成的實(shí)例是否具有同一id,而只關(guān)心其狀態(tài)和行為方式然想。我們可以允許許多個(gè)實(shí)例被創(chuàng)建莺奔,但所有的實(shí)例都共享狀態(tài)和行為方式:
class Borg(object):
_shared_state={}
def new(cls,args,kwargs):
obj=super(Borg,cls).new(cls,args,**kwargs)
obj.dict=cls._shared_state
return obj
將所有實(shí)例的__dict__指向同一個(gè)字典,這樣實(shí)例就共享相同的方法和屬性变泄。對(duì)任何實(shí)例的名字屬性的設(shè)置令哟,無論是在__init__中修改還是直接修改,所有的實(shí)例都會(huì)受到影響妨蛹。不過實(shí)例的id是不同的屏富。要保證類實(shí)例能共享屬性,但不和子類共享蛙卤,注意使用cls._shared_state,而不是Borg._shared_state狠半。
因?yàn)閷?shí)例是不同的id,所以每個(gè)實(shí)例都可以做字典的key:
if name=='main':
class Example(Borg):
pass
a=Example()
b=Example()
c=Example()
adict={}
j=0
for i in a,b,c:
adict[i]=j
j+=1
for i in a,b,c:
print adict[i]
結(jié)果:
0
1
2
如果這種行為不是你想要的颤难,可以為Borg類添加__eq__和__hash__方法神年,使其更接近于單例模式的行為:
class Borg(object):
_shared_state={}
def new(cls,args,kwargs):
obj=super(Borg,cls).new(cls,args,**kwargs)
obj.dict=cls._shared_state
return obj
def hash(self):
return 1
def eq(self,other):
try:
return self.dict is other.dict
except:
return False
if name=='main':
class Example(Borg):
pass
a=Example()
b=Example()
c=Example()
adict={}
j=0
for i in a,b,c:
adict[i]=j
j+=1
for i in a,b,c:
print adict[i]
結(jié)果:
2
2
2
所有的實(shí)例都能當(dāng)一個(gè)key使用了。
## 裝飾器版本
def singleton(cls, *args, *kw):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls(args, **kw)
return instances[cls]
return getinstance
@singleton
class MyClass:
...
## 基于元組
當(dāng)你編寫一個(gè)類的時(shí)候行嗤,某種機(jī)制會(huì)使用類名字已日,基類元組,類字典來創(chuàng)建一個(gè)類對(duì)象栅屏。新型類中這種機(jī)制默認(rèn)為type飘千,而且這種機(jī)制是可編程的堂鲜,稱為元類__metaclass__ 。
class Singleton(type):
def init(self,name,bases,class_dict):
super(Singleton,self).init(name,bases,class_dict)
self._instance=None
def call(self,args,kwargs):
if self._instance is None:
self._instance=super(Singleton,self).call(args,**kwargs)
return self._instance
if name=='main':
class A(object):
metaclass=Singleton
a=A()
b=A()
print id(a),id(b)
結(jié)果:
34248016 34248016
id是相同的占婉。
例子中我們構(gòu)造了一個(gè)Singleton元類泡嘴,并使用`__call__`方法使其能夠模擬函數(shù)的行為。構(gòu)造類A時(shí)逆济,將其元類設(shè)為Singleton酌予,那么創(chuàng)建類對(duì)象A時(shí),行為發(fā)生如下:
`A=Singleton(name,bases,class_dict)`,A其實(shí)為Singleton類的一個(gè)實(shí)例奖慌。
創(chuàng)建A的實(shí)例時(shí)抛虫,`A()=Singleton(name,bases,class_dict)()=Singleton(name,bases,class_dict).__call__()`,這樣就將A的所有實(shí)例都指向了A的屬性`_instance`上简僧,這種方法與方法1其實(shí)是相同的建椰。
## 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()
## python中的作用域
Python 中,一個(gè)變量的作用域總是由在代碼中被賦值的地方所決定的岛马。
當(dāng) Python 遇到一個(gè)變量的話他會(huì)按照這樣的順序進(jìn)行搜索:
本地作用域(Local)→當(dāng)前作用域被嵌入的本地作用域(Enclosing locals)→全局/模塊作用域(Global)→內(nèi)置作用域(Built-in)
## GIL線程全局鎖
線程全局鎖(Global Interpreter Lock),即Python為了保證線程安全而采取的獨(dú)立線程運(yùn)行的限制,說白了就是一個(gè)核只能在同一時(shí)間運(yùn)行一個(gè)線程.
見Python 最難的問題http://www.oschina.net/translate/pythons-hardest-problem
==解決辦法就是多進(jìn)程和下面的協(xié)程(協(xié)程也只是單CPU,但是能減小切換代價(jià)提升性能).==
## 協(xié)程
知乎被問到了,呵呵噠,跪了
簡(jiǎn)單點(diǎn)說協(xié)程是進(jìn)程和線程的升級(jí)版,進(jìn)程和線程都面臨著內(nèi)核態(tài)和用戶態(tài)的切換問題而耗費(fèi)許多切換時(shí)間,而協(xié)程就是用戶自己控制切換的時(shí)機(jī),不再需要陷入系統(tǒng)的內(nèi)核態(tài).
Python里最常見的yield就是協(xié)程的思想!可以查看第九個(gè)問題.
## 閉包
閉包(closure)是函數(shù)式編程的重要的語法結(jié)構(gòu)棉姐。閉包也是一種組織代碼的結(jié)構(gòu),它同樣提高了代碼的可重復(fù)使用性啦逆。
當(dāng)一個(gè)內(nèi)嵌函數(shù)引用其外部作作用域的變量,我們就會(huì)得到一個(gè)閉包. 總結(jié)一下,創(chuàng)建一個(gè)閉包必須滿足以下幾點(diǎn):
必須有一個(gè)內(nèi)嵌函數(shù)
內(nèi)嵌函數(shù)必須引用外部函數(shù)中的變量
外部函數(shù)的返回值必須是內(nèi)嵌函數(shù)
感覺閉包還是有難度的,幾句話是說不明白的,還是查查相關(guān)資料.
重點(diǎn)是函數(shù)運(yùn)行后并不會(huì)被撤銷,就像16題的instance字典一樣,當(dāng)函數(shù)運(yùn)行完后,instance并不被銷毀,而是繼續(xù)留在內(nèi)存空間里.這個(gè)功能類似類里的類變量,只不過遷移到了函數(shù)上.
閉包就像個(gè)空心球一樣,你知道外面和里面,但你不知道中間是什么樣.
## lambda函數(shù)
其實(shí)就是一個(gè)匿名函數(shù),為什么叫l(wèi)ambda?因?yàn)楹秃竺娴暮瘮?shù)式編程有關(guān).
推薦: 知乎(http://www.zhihu.com/question/20125256 )
## python函數(shù)式編程
這個(gè)需要適當(dāng)?shù)牧私庖幌掳?畢竟函數(shù)式編程在Python中也做了引用.
推薦: 酷殼(http://coolshell.cn/articles/10822.html )
python中函數(shù)式編程支持:
filter 函數(shù)的功能相當(dāng)于過濾器伞矩。調(diào)用一個(gè)布爾函數(shù)bool_func來迭代遍歷每個(gè)seq中的元素;返回一個(gè)使bool_seq返回值為true的元素的序列夏志。
a = [1,2,3,4,5,6,7]
b = filter(lambda x: x > 5, a)
print b
[6,7]
map函數(shù)是對(duì)一個(gè)序列的每個(gè)項(xiàng)依次執(zhí)行函數(shù)乃坤,下面是對(duì)一個(gè)序列每個(gè)項(xiàng)都乘以2:
a = map(lambda x:x*2,[1,2,3])
list(a)
[2, 4, 6]
reduce函數(shù)是對(duì)一個(gè)序列的每個(gè)項(xiàng)迭代調(diào)用函數(shù),下面是求3的階乘:
reduce(lambda x,y:x*y,range(1,4))
6
## python里的拷貝
引用和copy(),deepcopy()的區(qū)別:
1. copy.copy 淺拷貝 只拷貝父對(duì)象沟蔑,不會(huì)拷貝對(duì)象的內(nèi)部的子對(duì)象湿诊。
2. copy.deepcopy 深拷貝 拷貝對(duì)象及其子對(duì)象
3. copy拷貝一個(gè)對(duì)象,但是對(duì)象的屬性還是引用原來的瘦材,deepcopy拷貝一個(gè)對(duì)象厅须,把對(duì)象里面的屬性也做了拷貝,deepcopy之后完全是另一個(gè)對(duì)象了
import copy
a = [1, 2, 3, 4, ['a', 'b']] #原始對(duì)象
b = a #賦值食棕,傳對(duì)象的引用
c = copy.copy(a) #對(duì)象拷貝九杂,淺拷貝,里面的[]還是引用原來的
d = copy.deepcopy(a) #對(duì)象拷貝,深拷貝宣蠕, 所有的屬性引用全部是新的
a.append(5) #修改對(duì)象a
a[4].append('c') #修改對(duì)象a中的['a', 'b']數(shù)組對(duì)象
print 'a = ', a
print 'b = ', b
print 'c = ', c
print 'd = ', d
輸出結(jié)果:
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']]
## python 垃圾回收機(jī)制
Python GC主要使用引用計(jì)數(shù)(reference counting)來跟蹤和回收垃圾。在引用計(jì)數(shù)的基礎(chǔ)上甥捺,通過“標(biāo)記-清除”(mark and sweep)解決容器對(duì)象可能產(chǎn)生的循環(huán)引用問題抢蚀,通過“分代回收”(generation collection)以空間換時(shí)間的方法提高垃圾回收效率。
### 引用計(jì)數(shù)
PyObject是每個(gè)對(duì)象必有的內(nèi)容镰禾,其中`ob_refcnt`就是做為引用計(jì)數(shù)皿曲。當(dāng)一個(gè)對(duì)象有新的引用時(shí)唱逢,它的`ob_refcnt`就會(huì)增加,當(dāng)引用它的對(duì)象被刪除屋休,它的ob_refcnt就會(huì)減少.引用計(jì)數(shù)為0時(shí)坞古,該對(duì)象生命就結(jié)束了。
- 優(yōu)點(diǎn):
- 簡(jiǎn)單
- 實(shí)時(shí)性
- 缺點(diǎn):
- 維護(hù)引用計(jì)數(shù)消耗資源
- 循環(huán)引用
## 標(biāo)記\-清楚機(jī)制
基本思路是先按需分配劫樟,等到?jīng)]有空閑內(nèi)存的時(shí)候從寄存器和程序棧上的引用出發(fā)痪枫,遍歷以對(duì)象為節(jié)點(diǎn)、以引用為邊構(gòu)成的圖叠艳,把所有可以訪問到的對(duì)象打上標(biāo)記奶陈,然后清掃一遍內(nèi)存空間,把所有沒標(biāo)記的對(duì)象釋放附较。
## 分代技術(shù)
分代回收的整體思想是:將系統(tǒng)中的所有內(nèi)存塊根據(jù)其存活時(shí)間劃分為不同的集合吃粒,每個(gè)集合就成為一個(gè)“代”,垃圾收集頻率隨著“代”的存活時(shí)間的增大而減小拒课,存活時(shí)間通常利用經(jīng)過幾次垃圾回收來度量徐勃。
Python默認(rèn)定義了三代對(duì)象集合,索引數(shù)越大早像,對(duì)象存活時(shí)間越長(zhǎng)僻肖。
舉例:
當(dāng)某些內(nèi)存塊M經(jīng)過了3次垃圾收集的清洗之后還存活時(shí),我們就將內(nèi)存塊M劃到一個(gè)集合A中去扎酷,而新分配的內(nèi)存都劃分到集合B中去檐涝。當(dāng)垃圾收集開始工作時(shí),大多數(shù)情況都只對(duì)集合B進(jìn)行垃圾回收法挨,而對(duì)集合A進(jìn)行垃圾回收要隔相當(dāng)長(zhǎng)一段時(shí)間后才進(jìn)行谁榜,這就使得垃圾收集機(jī)制需要處理的內(nèi)存少了,效率自然就提高了凡纳。在這個(gè)過程中窃植,集合B中的某些內(nèi)存塊由于存活時(shí)間長(zhǎng)而會(huì)被轉(zhuǎn)移到集合A中,當(dāng)然荐糜,集合A中實(shí)際上也存在一些垃圾巷怜,這些垃圾的回收會(huì)因?yàn)檫@種分代的機(jī)制而被延遲。
# python的list
推薦: http://www.reibang.com/p/J4U6rR (c語言的實(shí)現(xiàn))
- 基本列表操作:
- 刪除
`del list[2]`
- 分片賦值
`name[2:] = list('ar')`
- append
list.append(2)
- count
x = [[1,2],1,1,[2,1,[1,2]]]
x.count([1,2])
1
x.count(1)
2
- append
用于在列表末尾追加新的對(duì)象
lst = [1,2,3,4]
lst.append[4]
lst
[1,2,3,4]
- extend
可以在列表末尾一次性追加另一個(gè)序列的多個(gè)值
a = [1,2,3]
b = [4,5,6]
a.extend(b)
a
[1,2,3,4,5,6]
看起來與`a+b`操作很像暴氏, 但是extend方法修改了被擴(kuò)展序列延塑,而`a+b`則是返回新的序列
a = [1,2,3]
b = [4,5,6]
a+b
[1,2,3,4,5,6]
a
[1,2,3]
- index方法
查找元素在列表中的位置
L= [1,2,3,3]
[1,2,3,3]
L.index(3)
2
- insert方法
L= [1,2,3]
[1,2,3]
L.insert(0,10)
[10,1,2,3]
- pop方法
L= [1,2,3]
[1,2,3]
L.pop(0)
1
L
[2,3]
Perl的列表array里面pop只能彈出右側(cè)的一個(gè)元素, 而這個(gè)可以彈出指定的index元素
有返回值答渔, 返回值是彈出的元素关带, 并且修改了原列表
- remove方法
移除列表中某個(gè)值的第一個(gè)匹配項(xiàng)
L= [1,2,3,3,4]
[1,2,3,3,4]
L.remove(3)
L
[1,2,3,4]
沒有返回值,原位修改
- sort方法
sort方法用于在原位置對(duì)列表進(jìn)行排序沼撕。
L= [1,2,3,5,4]
L.sort()
L
[1,2,3,4,5]
- reverse方法
L= [1,2,3,3,4]
[1,2,3,3,4]
L.reverse()
L
[4,3,3,2,1]
- sort 與sorted()的關(guān)系
- 相同:
- 都是排序
- 都支持key, reverse參數(shù)宋雏, 其中key的話可以實(shí)現(xiàn)高級(jí)排序
- 不同
- sort只對(duì)list起作用芜飘, 而sorted是全局函數(shù),對(duì)任何可迭代的序列均可以使用
- sort是原位修改,而sorted()會(huì)返回新的列表
詳情請(qǐng)看( https://github.com/qiwsir/algorithm/blob/master/python_sort.md )
# python的is
is是對(duì)比地址,==是對(duì)比值
# read, readline和readlines
- read 讀取整個(gè)文件
- readline 讀取下一行,使用生成器方法
- readlines 讀取整個(gè)文件到一個(gè)迭代器以供我們遍歷
# python2和3的區(qū)別
推薦:《Python 2.7.x 和 3.x 版本的重要區(qū)別》http://python.jobbole.com/80006/
# 操作系統(tǒng)
## select,poll和epoll
其實(shí)所有的I/O都是輪詢的方法,只不過實(shí)現(xiàn)的層面不同罷了.
這個(gè)問題可能有點(diǎn)深入了,但相信能回答出這個(gè)問題是對(duì)I/O多路復(fù)用有很好的了解了.其中tornado使用的就是epoll的.
selec,poll和epoll區(qū)別總結(jié)(http://www.cnblogs.com/Anker/p/3265058.html )
基本上select有3個(gè)缺點(diǎn):
- 連接數(shù)受限
- 查找配對(duì)速度慢
- 數(shù)據(jù)由內(nèi)核拷貝到用戶態(tài)
poll改善了第一個(gè)缺點(diǎn)
epoll改了三個(gè)缺點(diǎn).
關(guān)于epoll的: http://www.cnblogs.com/my_life/articles/3968782.html
## 調(diào)度算法
1. 先來先服務(wù)(FCFS, First Come First Serve)
2. 短作業(yè)優(yōu)先(SJF, Shortest Job First)
3. 最高優(yōu)先權(quán)調(diào)度(Priority Scheduling)
4. 時(shí)間片輪轉(zhuǎn)(RR, Round Robin)
5. 多級(jí)反饋隊(duì)列調(diào)度(multilevel feedback queue
6. scheduling)
- 實(shí)時(shí)調(diào)度算法:
1. 最早截至?xí)r間優(yōu)先 EDF
2. 最低松弛度優(yōu)先 LLF
## 死鎖
- 原因:
1. 競(jìng)爭(zhēng)資源
2. 程序推進(jìn)順序不當(dāng)
- 必要條件:
1. 互斥條件
2. 請(qǐng)求和保持條件
3. 不剝奪條件
4. 環(huán)路等待條件
- 處理死鎖基本方法:
1. 預(yù)防死鎖(摒棄除1以外的條件)
2. 避免死鎖(銀行家算法)
3. 檢測(cè)死鎖(資源分配圖)
4. 解除死鎖
1. 剝奪資源
2. 撤銷進(jìn)程
## 程序編譯與鏈接
推薦: http://www.ruanyifeng.com/blog/2014/11/compiler.html
Bulid過程可以分解為4個(gè)步驟:預(yù)處理(Prepressing), 編譯(Compilation)、匯編(Assembly)椭更、鏈接(Linking)
以c語言為例:
- 預(yù)處理
預(yù)編譯過程主要處理那些源文件中的以“#”開始的預(yù)編譯指令,主要處理規(guī)則有:
將所有的“#define”刪除娶牌,并展開所用的宏定義
處理所有條件預(yù)編譯指令,比如“#if”邻薯、“#ifdef”裙戏、 “#elif”、“#endif”
處理“#include”預(yù)編譯指令厕诡,將被包含的文件插入到該編譯指令的位置累榜,注:此過程是遞歸進(jìn)行的
刪除所有注釋
添加行號(hào)和文件名標(biāo)識(shí),以便于編譯時(shí)編譯器產(chǎn)生調(diào)試用的行號(hào)信息以及用于編譯時(shí)產(chǎn)生編譯錯(cuò)誤或警告時(shí)可顯示行號(hào)
保留所有的#pragma編譯器指令灵嫌。
- 編譯
編譯過程就是把預(yù)處理完的文件進(jìn)行一系列的詞法分析壹罚、語法分析、語義分析及優(yōu)化后生成相應(yīng)的匯編代碼文件寿羞。這個(gè)過程是整個(gè)程序構(gòu)建的核心部分猖凛。
- 匯編
匯編器是將匯編代碼轉(zhuǎn)化成機(jī)器可以執(zhí)行的指令,每一條匯編語句幾乎都是一條機(jī)器指令绪穆。經(jīng)過編譯辨泳、鏈接、匯編輸出的文件成為目標(biāo)文件(Object File)
- 鏈接
鏈接的主要內(nèi)容就是把各個(gè)模塊之間相互引用的部分處理好玖院,使各個(gè)模塊可以正確的拼接菠红。
鏈接的主要過程包塊 地址和空間的分配(Address and Storage Allocation)、符號(hào)決議(Symbol Resolution)和重定位(Relocation)等步驟难菌。
- 靜態(tài)鏈接和動(dòng)態(tài)鏈接
靜態(tài)鏈接方法:靜態(tài)鏈接的時(shí)候试溯,載入代碼就會(huì)把程序會(huì)用到的動(dòng)態(tài)代碼或動(dòng)態(tài)代碼的地址確定下來
靜態(tài)庫(kù)的鏈接可以使用靜態(tài)鏈接,動(dòng)態(tài)鏈接庫(kù)也可以使用這種方法鏈接導(dǎo)入庫(kù)
動(dòng)態(tài)鏈接方法:使用這種方式的程序并不在一開始就完成動(dòng)態(tài)鏈接郊酒,而是直到真正調(diào)用動(dòng)態(tài)庫(kù)代碼時(shí)遇绞,載入程序才計(jì)算(被調(diào)用的那部分)動(dòng)態(tài)代碼的邏輯地址,然后等到某個(gè)時(shí)候燎窘,程序又需要調(diào)用另外某塊動(dòng)態(tài)代碼時(shí)摹闽,載入程序又去計(jì)算這部分代碼的邏輯地址,所以褐健,這種方式使程序初始化時(shí)間較短钩骇,但運(yùn)行期間的性能比不上靜態(tài)鏈接的程序
- 虛擬內(nèi)存技術(shù)
虛擬存儲(chǔ)器是值具有請(qǐng)求調(diào)入功能和置換功能,能從邏輯上對(duì)內(nèi)存容量加以擴(kuò)充的一種存儲(chǔ)系統(tǒng).
- 分頁和分段
分頁: 用戶程序的地址空間被劃分成若干固定大小的區(qū)域,稱為“頁”,相應(yīng)地倘屹,內(nèi)存空間分成若干個(gè)物理塊,頁和塊的大小相等慢叨∨Τ祝可將用戶程序的任一頁放在內(nèi)存的任一塊中,實(shí)現(xiàn)了離散分配拍谐。
分段: 將用戶程序地址空間分成若干個(gè)大小不等的段烛缔,每段可以定義一組相對(duì)完整的邏輯信息。存儲(chǔ)分配時(shí)轩拨,以段為單位践瓷,段與段在內(nèi)存中可以不相鄰接,也實(shí)現(xiàn)了離散分配亡蓉。
分頁與分段的主要區(qū)別
頁是信息的物理單位,分頁是為了實(shí)現(xiàn)非連續(xù)分配,以便解決內(nèi)存碎片問題,或者說分頁是由于系統(tǒng)管理的需要.段是信息的邏輯單位,它含有一組意義相對(duì)完整的信息,分段的目的是為了更好地實(shí)現(xiàn)共享,滿足用戶的需要.
頁的大小固定,由系統(tǒng)確定,將邏輯地址劃分為頁號(hào)和頁內(nèi)地址是由機(jī)器硬件實(shí)現(xiàn)的.而段的長(zhǎng)度卻不固定,決定于用戶所編寫的程序,通常由編譯程序在對(duì)源程序進(jìn)行編譯時(shí)根據(jù)信息的性質(zhì)來劃分.
分頁的作業(yè)地址空間是一維的.分段的地址空間是二維的.
- 頁面置換算法
最佳置換算法OPT:不可能實(shí)現(xiàn)
先進(jìn)先出FIFO
最近最久未使用算法LRU:最近一段時(shí)間里最久沒有使用過的頁面予以置換.
clock算法
- 邊沿觸發(fā)和水平觸發(fā)
邊緣觸發(fā)是指每當(dāng)狀態(tài)變化時(shí)發(fā)生一個(gè) io 事件晕翠,條件觸發(fā)是只要滿足條件就發(fā)生一個(gè) io 事件
# 數(shù)據(jù)庫(kù)
## 事物
數(shù)據(jù)庫(kù)事務(wù)(Database Transaction) ,是指作為單個(gè)邏輯工作單元執(zhí)行的一系列操作砍濒,要么完全地執(zhí)行淋肾,要么完全地不執(zhí)行。
## 數(shù)據(jù)庫(kù)索引
推薦: http://tech.meituan.com/mysql-index.html
MySQL索引背后的數(shù)據(jù)結(jié)構(gòu)及算法原理(http://blog.jobbole.com/24006/)
聚集索引,非聚集索引,B-Tree,B+Tree,最左前綴原理
## Redis原理
## 樂觀鎖和悲觀鎖
悲觀鎖:假定會(huì)發(fā)生并發(fā)沖突爸邢,屏蔽一切可能違反數(shù)據(jù)完整性的操作
樂觀鎖:假設(shè)不會(huì)發(fā)生并發(fā)沖突樊卓,只在提交操作時(shí)檢查是否違反數(shù)據(jù)完整性。
## MVCC
## MyISAM和InnoDB
MyISAM 適合于一些需要大量查詢的應(yīng)用杠河,但其對(duì)于有大量寫操作并不是很好碌尔。甚至你只是需要update一個(gè)字段,整個(gè)表都會(huì)被鎖起來券敌,而別的進(jìn)程唾戚,就算是讀進(jìn)程都無法操作直到讀操作完成。另外陪白,MyISAM 對(duì)于 SELECT COUNT(*) 這類的計(jì)算是超快無比的颈走。
InnoDB 的趨勢(shì)會(huì)是一個(gè)非常復(fù)雜的存儲(chǔ)引擎,對(duì)于一些小的應(yīng)用咱士,它會(huì)比 MyISAM 還慢立由。他是它支持“行鎖” ,于是在寫操作比較多的時(shí)候序厉,會(huì)更優(yōu)秀锐膜。并且,他還支持更多的高級(jí)應(yīng)用弛房,比如:事務(wù)道盏。