何實現python的函數參數傳遞?
看兩個例子:
a = 1def fun(a): a = 2fun(a)print a # 1a = []def fun(a): a.append(1)fun(a)print a # [1]
所有變量都可以理解為內存中一個對象的“引用”,或者,可以看做C中的viod*的感覺
這裡記住的是類型是屬於對象的,而不是變量。而對象有兩種,“可更改”(mutable)與“不可更改”(immutable)對象。在python中,strings, tuples, 和numbers是不可更改的對象,而list,dict等則是可以修改的對象。(這就是這個問題的重點)
當一個引用傳遞給函數的時候,函數自動複製一份引用,這個函數里的引用和外邊的引用沒有半毛關係了.所以第一個例子裡函數把引用指向了一個不可變對象,當函數返回的時候,外面的引用沒半毛感覺.而第二個例子就不一樣了,函數內的引用指向的是可變對象,對它的操作就和定位了指針地址一樣,在內存裡進行修改.
如果還不明白的話,這裡有更好的解釋: http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference
python中的元類(metaclass)
這個非常的不常用,但是像ORM這種複雜的結構還是會需要的,詳情請看:《深刻理解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)"%xa=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)
更多關於這個問題:http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python
類變量和實例變量
class Person: name="aaa" p1=Person() #類變量p2=Person() #類變量p1.name="bbb" #實例變量print p1.name # bbbprint p2.name # aaaprint Person.name # aaa
類變量就是供類使用的變量,實例變量就是供實例使用的.
這裡p1.name="bbb"是實例調用了類變量,這其實和上面第一個問題一樣,就是函數傳參的問題,p1.name一開始是指向的類變量name="aaa",但是在實例的作用域裡把類變量的引用改變了,就變成了一個實例變量,self.name不再引用Person的類變量name了.
==可以看看下面的例子: (need check)==
==python中list是mutable的類變量, 實例化之後也是mutable的, 所以對第一個實例的name操作, 也會引起類變量以及其它的實例中list的更改==
==如何避免==
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自省
這個也是python彪悍的特性.
自省就是面向對象的語言所寫的程序在運行時,所能知道對象的類型.簡單一句就是運行時能夠獲得對象的類型.比如type(),dir(),getattr(),hasattr(),isinstance().
字典推導式:
d = {key: value for (key, value) in iterable}
你可以用任何方式的迭代器(元組,列表,生成器..),只要可迭代對象的元素中有兩個值.
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中單下劃線和雙下劃線
這篇文章討論Python中下劃線_的使用。跟Python中很多用法類似,下劃線_的不同用法絕大部分(不全是)都是一種慣例約定。
單下劃線(_)
主要有三種情況:
解釋器中_符號是指交互解釋器中最後一次執行語句的返回結果。這種用法最初出現在CPython解釋器中,其他解釋器後來也都跟進了。
>>> _Traceback (most recent call last): File "", line 1, in NameError: name '_' is not defined>>> 42>>> _42>>> 'alright!' if _ else ':(''alright!'>>> _'alright!'作為名稱使用
這個跟上面有點類似。_用作被丟棄的名稱。按照慣例,這樣做可以讓閱讀你代碼的人知道,這是個不會被使用的特定名稱。舉個例子,你可能無所謂一個循環計數的值:
n = 42for _ in range(n): do_something()i18n
_還可以被用作函數名。這種情況,單下劃線經常被用作國際化和本地化字符串翻譯查詢的函數名。這種慣例好像起源於C語言。舉個例子,在 Django documentation for translation 中你可能會看到:
from django.utils.translation import ugettext as _from django.http import HttpResponsedef my_view(request): output = _("Welcome to my site.") return HttpResponse(output)
第二種和第三種用法會引起衝突,所以在任意代碼塊中,如果使用了_作i18n翻譯查詢函數,就應該避免再用作被丟棄的變量名。
單下劃線前綴的名稱(例如_shahriar)
以單下劃線做前綴的名稱指定了這個名稱是“私有的”。在 有些 導入import * 的場景中,下一個使用你代碼的人(或者你本人)會明白這個名稱僅內部使用。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 * 的場景,是因為導入時解釋器確實對單下劃線開頭的名稱做了處理。如果你這麼寫from <module> import *,任何以單下劃線開頭的名稱都不會被導入,除非模塊/包的__all__列表明確包含了這些名稱。更多相關信息見““Importing * in Python”。/<module>
雙下劃線前綴的名稱(例如__shahriar)
以雙下劃線做前綴的名稱(特別是方法名)並不是一種慣例;它對解釋器有特定含義。Python會改寫這些名稱,以免與子類中定義的名稱產生衝突。Python documentation中提到,任何__spam這種形式(至少以兩個下劃線做開頭,絕大部分都還有一個下劃線做結尾)的標識符,都會文本上被替換為_classname__spam,其中classname是當前類名,並帶上一個下劃線做前綴。
看下面這個例子:
>>> 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。現在創建一個A的子類B(這可不是個好名字),就不會輕易的覆蓋掉A中的__method_name了:
>>> class B(A):... def __method_name(self):... pass... >>> dir(B())['_A__method_name', '_B__method_name', ..., '_internal_use']
這種特定的行為差不多等價於Java中的final方法和C++中的正常方法(非虛方法)。
前後都帶有雙下劃線的名稱(例如__init__)
這些是Python的特殊方法名,這僅僅是一種慣例,一種確保Python系統中的名稱不會跟用戶自定義的名稱發生衝突的方式。通常你可以覆寫這些方法,在Python調用它們時,產生你想得到的行為。例如,當寫一個類的時候經常會覆寫__init__方法。
你也可以寫出自己的“特殊方法”名(但是別這麼做):
>>> class C(object):... def __mine__(self):... pass...>>> dir(C)... [..., '__mine__', ...]
還是不要這樣寫方法名,只讓Python定義的特殊方法名使用這種慣例吧。
詳情見:http://stackoverflow.com/questions/1301346/the-meaning-of-a-single-and-a-double-underscore-before-an-object-name-in-python
或者: http://www.zhihu.com/question/19754941
字符串格式化:%和.format
.format在許多方面看起來更便利.對於%最煩人的是它無法同時傳遞一個變量和元組.你可能會想下面的代碼不會有什麼問題:
hi there %s" % name
但是,如果name恰好是(1,2,3),它將會拋出一個TypeError異常.為了保證它總是正確的,你必須這樣做:
hi there %s" % (name,) # 提供一個單元素的數組而不是一個參數
但是有點醜..format就沒有這些問題.你給的第二個問題也是這樣,.format好看多了.
你為什麼不用它?
不知道它(在讀這個之前)
為了和Python2.5兼容(譬如logging庫建議使用%(issue #4))
http://stackoverflow.com/questions/5082452/python-string-formatting-vs-format
迭代器和生成器
這個是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
當你創建了一個列表,你可以一個一個的讀取它的每一項,這叫做iteration:
>>> mylist = [1, 2, 3]>>> for i in mylist:... print(i)123
Mylist是可迭代的.當你用列表推導式的時候,你就創建了一個列表,而這個列表也是可迭代的:
>>> mylist = [x*x for x in range(3)]>>> for i in mylist:... print(i)014
所有你可以用在for...in...語句中的都是可迭代的:比如lists,strings,files...因為這些可迭代的對象你可以隨意的讀取所以非常方便易用,但是你必須把它們的值放到內存裡,當它們有很多值時就會消耗太多的內存.
Generators
生成器也是迭代器的一種,但是你只能迭代它們一次.原因很簡單,因為它們不是全部存在內存裡,它們只在要調用的時候在內存裡生成:
>>> mygenerator = (x*x for x in range(3))>>> for i in mygenerator:... print(i)014
生成器和迭代器的區別就是用()代替[],還有你不能用for i in mygenerator第二次調用生成器:首先計算0,然後會在內存裡丟掉0去計算1,直到計算完4.
Yield
Yield的用法和關鍵字return差不多,下面的函數將會返回一個生成器:
>>> def createGenerator():... mylist = range(3)... for i in mylist:... yield i*i...>>> mygenerator = createGenerator() # 創建生成器>>> print(mygenerator) # mygenerator is an object!<generator>>>> for i in mygenerator:... print(i)014/<generator>
在這裡這個例子好像沒什麼用,不過當你的函數要返回一個非常大的集合並且你希望只讀一次的話,那麼它就非常的方便了.
要理解Yield你必須先理解當你調用函數的時候,函數里的代碼並沒有運行.函數僅僅返回生成器對象,這就是它最微妙的地方:-)
然後呢,每當for語句迭代生成器的時候你的代碼才會運轉.
現在,到了最難的部分:
當for語句第一次調用函數里返回的生成器對象,函數里的代碼就開始運作,直到碰到yield,然後會返回本次循環的第一個返回值.所以下一次調用也將運行一次循環然後返回下一個值,直到沒有值可以返回.
一旦函數運行並沒有碰到yeild語句就認為生成器已經為空了.原因有可能是循環結束或者沒有滿足if/else之類的.
Itertools你的好基友
itertools模塊包含了一些特殊的函數可以操作可迭代對象.有沒有想過複製一個生成器?鏈接兩個生成器?把嵌套列表裡的值組織成一個列表?Map/Zip還不用創建另一個列表?
來吧import itertools
來一個例子?讓我們看看4匹馬比賽有多少個排名結果:
>>> horses = [1, 2, 3, 4]>>> races = itertools.permutations(horses)>>> print(races)<itertools.permutations>>>> 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)]/<itertools.permutations>
理解迭代的內部機制
迭代是可迭代對象(對應iter()方法)和迭代器(對應next()方法)的一個過程.可迭代對象就是任何你可以迭代的對象(廢話啊).迭代器就是可以讓你迭代可迭代對象的對象(有點繞口,意思就是這個意思)
*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. apple1. banana2. 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 = vegetableapple = fruit
*args和**kwargs 必須放在參數列表的後面。
面向切面編程AOP和裝飾器
這個AOP一聽起來有點懵,同學面阿里的時候就被問懵了…
裝飾器就是把其它函數當參數的函數。裝飾器是一個很著名的設計模式,經常被用於有切面需求的場景,較為經典的有插入日誌、性能測試、事務處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量函數中與函數功能本身無關的雷同代碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能。這個問題比較大,推薦: 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
看一個簡單的例子# 字體變粗裝飾器def makebold(fn): # 裝飾器將返回新的函數 def wrapper(): # 在之前或者之後插入新的代碼 return "" + fn() + "" return wrapper# 斜體裝飾器
def makeitalic(fn):
# 裝飾器將返回新的函數
def wrapper():
# 在之前或者之後插入新的代碼
return "" + fn() + ""
return wrapper
@makebold
@makeitalic
def say():
return "hello"
print say()
輸出: hello
這相當於
def say():
return "hello"
say = makebold(makeitalic(say))
print say()
輸出: hello
- 用法: 1. 傳統用法是給外部的不可更改的庫做擴展 2. Django用裝飾器管理緩存和試圖的權限. 3. Twisted用來修改異步函數的調用. 4. etc.# 鴨子類型“當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。”我們並不關心對象是什麼類型,到底是不是鴨子,只關心行為。比如在python中,有很多file-like的東西,比如StringIO,GzipFile,socket。它們有很多相同的方法,我們把它們當作文件使用。又比如list.extend()方法中,我們並不關心它的參數是不是list,只要它是可迭代的,所以它的參數可以是list/tuple/dict/字符串/生成器等.鴨子類型在動態語言中經常使用,非常靈活,使得python不想java那樣專門去弄一大堆的設計模式。# Python中重載引自知乎:http://www.zhihu.com/question/20053359函數重載主要是為了解決兩個問題: - 可變參數類型 - 可變參數個數另外,一個基本的設計原則是,僅僅當兩個函數除了參數類型和參數個數不同以外,其功能是完全相同的,此時才使用函數重載,如果兩個函數的功能其實不同,那麼不應當使用重載,而應當使用一個名字不同的函數。好吧,那麼對於情況 1 ,函數功能相同,但是參數類型不同,python 如何處理?答案是根本不需要處理,因為 python 可以接受任何類型的參數,如果函數的功能相同,那麼不同的參數類型在 python 中很可能是相同的代碼,沒有必要做成兩個不同函數。那麼對於情況 2 ,函數功能相同,但參數個數不同,python 如何處理?大家知道,答案就是缺省參數。對那些缺少的參數設定為缺省參數即可解決問題。因為你假設函數功能相同,那麼那些缺少的參數終歸是需要用的。好了,鑑於情況 1 跟 情況 2 都有了解決方案,==python 自然就不需要函數重載了==# 新式類與舊式類這個面試官問了,我說了老半天,不知道他問的真正意圖是什麼.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簡單的說,新式類是在創建的時候繼承內置object對象(或者是從內置類型,如list,dict等),而經典類是直接聲明的。使用dir()方法也可以看出新式類中定義很多新的屬性和方法,而經典類好像就2個:新式類很早在2.2就出現了,所以舊式類完全是兼容的問題,Python3裡的類全部都是新式類.這裡有一個MRO問題可以瞭解下(新式類是廣度優先,舊式類是深度優先),<python>裡講的也很多./<python>
新式類
class C(object):
pass
經典類
class B:
pass
# `__new__`和`__init__`的區別這個`__new__`確實很少見到,先做了解吧.`__new__`是一個靜態方法,而`__init__`是一個實例方法.`__new__`方法會返回一個創建的實例,而`__init__`什麼都不返回.只有在`__new__`返回一個cls的實例時後面的`__init__`才能被調用.當創建一個新實例時調用`__new__`,初始化一個實例時用`__init__`.stackoverflow(http://stackoverflow.com/questions/674304/pythons-use-of-new-and-init)ps: `__metaclass__`是創建類時起作用.所以我們可以分別使用`__metaclass__`,`__new__`和`__init__`來分別在類創建,實例創建和實例初始化的時候做一些小手腳.# 單例模式==這個絕對長考, 絕對要記住1~2個方法.==所謂單例,是指一個類的實例從始至終只能被創建一次。## 使用`__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
結果:
29922256 banana
29922256 banana
通過`__new__`方法,將類的實例在創建的時候綁定到類屬性`_inst`上。如果`cls._inst`為None,說明類還未實例化,實例化並將實例綁定到`cls._inst`,以後每次實例化的時候都返回第一次實例化創建的實例。注意從Singleton派生子類的時候,不要重載`__new__`。## 共享屬性有時候我們並不關心生成的實例是否具有同一id,而只關心其狀態和行為方式。我們可以允許許多個實例被創建,但所有的實例都共享狀態和行為方式:
class Borg(object):
_shared_state={}
def new
obj=super(Borg,cls).new(cls,args,**kwargs)
obj.dict=cls._shared_state
return obj
將所有實例的__dict__指向同一個字典,這樣實例就共享相同的方法和屬性。對任何實例的名字屬性的設置,無論是在__init__中修改還是直接修改,所有的實例都會受到影響。不過實例的id是不同的。要保證類實例能共享屬性,但不和子類共享,注意使用cls._shared_state,而不是Borg._shared_state。因為實例是不同的id,所以每個實例都可以做字典的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]
結果:
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.
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]
結果:
2
2
2
所有的實例都能當一個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:
...
## 基於元組當你編寫一個類的時候,某種機制會使用類名字,基類元組,類字典來創建一個類對象。新型類中這種機制默認為type,而且這種機制是可編程的,稱為元類__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)
結果:
34248016 34248016
id是相同的。例子中我們構造了一個Singleton元類,並使用`__call__`方法使其能夠模擬函數的行為。構造類A時,將其元類設為Singleton,那麼創建類對象A時,行為發生如下:`A=Singleton(name,bases,class_dict)`,A其實為Singleton類的一個實例。創建A的實例時,`A()=Singleton(name,bases,class_dict)()=Singleton(name,bases,class_dict).__call__()`,這樣就將A的所有實例都指向了A的屬性`_instance`上,這種方法與方法1其實是相同的。## 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 中,一個變量的作用域總是由在代碼中被賦值的地方所決定的。當 Python 遇到一個變量的話他會按照這樣的順序進行搜索:本地作用域(Local)→當前作用域被嵌入的本地作用域(Enclosing locals)→全局/模塊作用域(Global)→內置作用域(Built-in)## GIL線程全局鎖線程全局鎖(Global Interpreter Lock),即Python為了保證線程安全而採取的獨立線程運行的限制,說白了就是一個核只能在同一時間運行一個線程.見Python 最難的問題http://www.oschina.net/translate/pythons-hardest-problem==解決辦法就是多進程和下面的協程(協程也只是單CPU,但是能減小切換代價提升性能).==## 協程知乎被問到了,呵呵噠,跪了簡單點說協程是進程和線程的升級版,進程和線程都面臨著內核態和用戶態的切換問題而耗費許多切換時間,而協程就是用戶自己控制切換的時機,不再需要陷入系統的內核態.Python裡最常見的yield就是協程的思想!可以查看第九個問題.## 閉包閉包(closure)是函數式編程的重要的語法結構。閉包也是一種組織代碼的結構,它同樣提高了代碼的可重複使用性。當一個內嵌函數引用其外部作作用域的變量,我們就會得到一個閉包. 總結一下,創建一個閉包必須滿足以下幾點:必須有一個內嵌函數內嵌函數必須引用外部函數中的變量外部函數的返回值必須是內嵌函數感覺閉包還是有難度的,幾句話是說不明白的,還是查查相關資料.重點是函數運行後並不會被撤銷,就像16題的instance字典一樣,當函數運行完後,instance並不被銷燬,而是繼續留在內存空間裡.這個功能類似類裡的類變量,只不過遷移到了函數上.閉包就像個空心球一樣,你知道外面和裡面,但你不知道中間是什麼樣.## lambda函數其實就是一個匿名函數,為什麼叫lambda?因為和後面的函數式編程有關.推薦: 知乎(http://www.zhihu.com/question/20125256 )## python函數式編程這個需要適當的瞭解一下吧,畢竟函數式編程在Python中也做了引用.推薦: 酷殼(http://coolshell.cn/articles/10822.html )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
## python裡的拷貝引用和copy(),deepcopy()的區別:1. copy.copy 淺拷貝 只拷貝父對象,不會拷貝對象的內部的子對象。2. copy.deepcopy 深拷貝 拷貝對象及其子對象3. copy拷貝一個對象,但是對象的屬性還是引用原來的,deepcopy拷貝一個對象,把對象裡面的屬性也做了拷貝,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']]
## python 垃圾回收機制Python GC主要使用引用計數(reference counting)來跟蹤和回收垃圾。在引用計數的基礎上,通過“標記-清除”(mark and sweep)解決容器對象可能產生的循環引用問題,通過“分代回收”(generation collection)以空間換時間的方法提高垃圾回收效率。### 引用計數PyObject是每個對象必有的內容,其中`ob_refcnt`就是做為引用計數。當一個對象有新的引用時,它的`ob_refcnt`就會增加,當引用它的對象被刪除,它的ob_refcnt就會減少.引用計數為0時,該對象生命就結束了。- 優點: - 簡單 - 實時性- 缺點: - 維護引用計數消耗資源 - 循環引用## 標記\-清楚機制基本思路是先按需分配,等到沒有空閒內存的時候從寄存器和程序棧上的引用出發,遍歷以對象為節點、以引用為邊構成的圖,把所有可以訪問到的對象打上標記,然後清掃一遍內存空間,把所有沒標記的對象釋放。## 分代技術分代回收的整體思想是:將系統中的所有內存塊根據其存活時間劃分為不同的集合,每個集合就成為一個“代”,垃圾收集頻率隨著“代”的存活時間的增大而減小,存活時間通常利用經過幾次垃圾回收來度量。Python默認定義了三代對象集合,索引數越大,對象存活時間越長。舉例: 當某些內存塊M經過了3次垃圾收集的清洗之後還存活時,我們就將內存塊M劃到一個集合A中去,而新分配的內存都劃分到集合B中去。當垃圾收集開始工作時,大多數情況都只對集合B進行垃圾回收,而對集合A進行垃圾回收要隔相當長一段時間後才進行,這就使得垃圾收集機制需要處理的內存少了,效率自然就提高了。在這個過程中,集合B中的某些內存塊由於存活時間長而會被轉移到集合A中,當然,集合A中實際上也存在一些垃圾,這些垃圾的回收會因為這種分代的機制而被延遲。# python的list推薦: http://www.jianshu.com/p/J4U6rR (c語言的實現)- 基本列表操作: - 刪除 `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用於在列表末尾追加新的對象
lst = [1,2,3,4]
lst.append[4]
lst
[1,2,3,4]
- extend可以在列表末尾一次性追加另一個序列的多個值
a = [1,2,3]
b = [4,5,6]
a.extend(b)
a
[1,2,3,4,5,6]
看起來與`a+b`操作很像, 但是extend方法修改了被擴展序列,而`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只能彈出右側的一個元素, 而這個可以彈出指定的index元素有返回值, 返回值是彈出的元素, 並且修改了原列表- remove方法移除列表中某個值的第一個匹配項
L= [1,2,3,3,4]
[1,2,3,3,4]
L.remove(3)
L
[1,2,3,4]
沒有返回值,原位修改- sort方法sort方法用於在原位置對列表進行排序。
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()的關係- 相同: - 都是排序 - 都支持key, reverse參數, 其中key的話可以實現高級排序- 不同 - sort只對list起作用, 而sorted是全局函數,對任何可迭代的序列均可以使用 - sort是原位修改,而sorted()會返回新的列表詳情請看( https://github.com/qiwsir/algorithm/blob/master/python_sort.md )# python的isis是對比地址,==是對比值# read, readline和readlines- read 讀取整個文件- readline 讀取下一行,使用生成器方法- readlines 讀取整個文件到一個迭代器以供我們遍歷# python2和3的區別推薦:《Python 2.7.x 和 3.x 版本的重要區別》http://python.jobbole.com/80006/# 操作系統## select,poll和epoll其實所有的I/O都是輪詢的方法,只不過實現的層面不同罷了.這個問題可能有點深入了,但相信能回答出這個問題是對I/O多路複用有很好的瞭解了.其中tornado使用的就是epoll的.selec,poll和epoll區別總結(http://www.cnblogs.com/Anker/p/3265058.html )基本上select有3個缺點: - 連接數受限 - 查找配對速度慢 - 數據由內核拷貝到用戶態poll改善了第一個缺點epoll改了三個缺點.關於epoll的: http://www.cnblogs.com/my_life/articles/3968782.html## 調度算法1. 先來先服務(FCFS, First Come First Serve)2. 短作業優先(SJF, Shortest Job First)3. 最高優先權調度(Priority Scheduling)4. 時間片輪轉(RR, Round Robin)5. 多級反饋隊列調度(multilevel feedback queue6. scheduling)- 實時調度算法:1. 最早截至時間優先 EDF2. 最低鬆弛度優先 LLF## 死鎖- 原因:1. 競爭資源2. 程序推進順序不當- 必要條件:1. 互斥條件2. 請求和保持條件3. 不剝奪條件4. 環路等待條件- 處理死鎖基本方法:1. 預防死鎖(摒棄除1以外的條件)2. 避免死鎖(銀行家算法)3. 檢測死鎖(資源分配圖)4. 解除死鎖 1. 剝奪資源 2. 撤銷進程## 程序編譯與鏈接推薦: http://www.ruanyifeng.com/blog/2014/11/compiler.htmlBulid過程可以分解為4個步驟:預處理(Prepressing), 編譯(Compilation)、彙編(Assembly)、鏈接(Linking)以c語言為例:- 預處理預編譯過程主要處理那些源文件中的以“#”開始的預編譯指令,主要處理規則有:將所有的“#define”刪除,並展開所用的宏定義處理所有條件預編譯指令,比如“#if”、“#ifdef”、 “#elif”、“#endif”處理“#include”預編譯指令,將被包含的文件插入到該編譯指令的位置,注:此過程是遞歸進行的刪除所有註釋添加行號和文件名標識,以便於編譯時編譯器產生調試用的行號信息以及用於編譯時產生編譯錯誤或警告時可顯示行號保留所有的#pragma編譯器指令。- 編譯編譯過程就是把預處理完的文件進行一系列的詞法分析、語法分析、語義分析及優化後生成相應的彙編代碼文件。這個過程是整個程序構建的核心部分。- 彙編彙編器是將彙編代碼轉化成機器可以執行的指令,每一條彙編語句幾乎都是一條機器指令。經過編譯、鏈接、彙編輸出的文件成為目標文件(Object File)- 鏈接鏈接的主要內容就是把各個模塊之間相互引用的部分處理好,使各個模塊可以正確的拼接。鏈接的主要過程包塊 地址和空間的分配(Address and Storage Allocation)、符號決議(Symbol Resolution)和重定位(Relocation)等步驟。- 靜態鏈接和動態鏈接靜態鏈接方法:靜態鏈接的時候,載入代碼就會把程序會用到的動態代碼或動態代碼的地址確定下來靜態庫的鏈接可以使用靜態鏈接,動態鏈接庫也可以使用這種方法鏈接導入庫動態鏈接方法:使用這種方式的程序並不在一開始就完成動態鏈接,而是直到真正調用動態庫代碼時,載入程序才計算(被調用的那部分)動態代碼的邏輯地址,然後等到某個時候,程序又需要調用另外某塊動態代碼時,載入程序又去計算這部分代碼的邏輯地址,所以,這種方式使程序初始化時間較短,但運行期間的性能比不上靜態鏈接的程序- 虛擬內存技術虛擬存儲器是值具有請求調入功能和置換功能,能從邏輯上對內存容量加以擴充的一種存儲系統.- 分頁和分段分頁: 用戶程序的地址空間被劃分成若干固定大小的區域,稱為“頁”,相應地,內存空間分成若干個物理塊,頁和塊的大小相等。可將用戶程序的任一頁放在內存的任一塊中,實現了離散分配。分段: 將用戶程序地址空間分成若干個大小不等的段,每段可以定義一組相對完整的邏輯信息。存儲分配時,以段為單位,段與段在內存中可以不相鄰接,也實現了離散分配。分頁與分段的主要區別頁是信息的物理單位,分頁是為了實現非連續分配,以便解決內存碎片問題,或者說分頁是由於系統管理的需要.段是信息的邏輯單位,它含有一組意義相對完整的信息,分段的目的是為了更好地實現共享,滿足用戶的需要.頁的大小固定,由系統確定,將邏輯地址劃分為頁號和頁內地址是由機器硬件實現的.而段的長度卻不固定,決定於用戶所編寫的程序,通常由編譯程序在對源程序進行編譯時根據信息的性質來劃分.分頁的作業地址空間是一維的.分段的地址空間是二維的.- 頁面置換算法最佳置換算法OPT:不可能實現先進先出FIFO最近最久未使用算法LRU:最近一段時間裡最久沒有使用過的頁面予以置換.clock算法- 邊沿觸發和水平觸發邊緣觸發是指每當狀態變化時發生一個 io 事件,條件觸發是隻要滿足條件就發生一個 io 事件# 數據庫## 事物數據庫事務(Database Transaction) ,是指作為單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行。## 數據庫索引推薦: http://tech.meituan.com/mysql-index.htmlMySQL索引背後的數據結構及算法原理(http://blog.jobbole.com/24006/)聚集索引,非聚集索引,B-Tree,B+Tree,最左前綴原理## Redis原理## 樂觀鎖和悲觀鎖悲觀鎖:假定會發生併發衝突,屏蔽一切可能違反數據完整性的操作Python中最常見括號的區別:在Python語言中最常見的括號有三種,分別是:小括號()、中括號[]、花括號{};其作用也不相同,分別用來代表不同的Python基本內置數據類型。Python中的小括號():代表tuple元祖數據類型,元祖是一種不可變序列。創建方法很簡單,大多數時候都是小括號括起來的。 1 >>> tup = (1,2,3)2 >>> tup3 (1, 2, 3)4 >>> () #空元祖5 ()6 >>> 55,#一個值的元祖7 (55,)Python中的中括號[]:代表list列表數據類型,列表是一種可變序列。創建方法既簡單又特別。1 >>> list('Python')2 ['P', 'y', 't', 'h', 'o', 'n'] Python中的花括號{}:代表dict字典數據類型,字典是Python中唯一內建的映射類型。字典中的值沒有特殊的順序,但都是存儲在一個特定的鍵(key)下。鍵可以是數字、字符串甚至是元祖。1 >>> dic = {'jon':'boy','lili"':'girl'}2 >>> dic3 {'jon': 'boy', 'lili"': 'girl'}樂觀鎖:假設不會發生併發衝突,只在提交操作時檢查是否違反數據完整性。## MVCC## MyISAM和InnoDBMyISAM 適合於一些需要大量查詢的應用,但其對於有大量寫操作並不是很好。甚至你只是需要update一個字段,整個表都會被鎖起來,而別的進程,就算是讀進程都無法操作直到讀操作完成。另外,MyISAM 對於 SELECT COUNT(*) 這類的計算是超快無比的。好了,今天的知識就分享到這裡,歡迎關注愛編程的南風,私信關鍵詞:學習資料,獲取更多學習資源,如果文章對你有有幫助,請收藏關注,在今後與你分享更多學習python的文章。同時歡迎在下面評論區留言如何學習python。