1. 程式人生 > >比較全面的概述。

比較全面的概述。

copy 布爾 指定 開始 number from 兼容 作文件 mutable

1 Python的函數參數傳遞

看兩個例子:

a = 1def fun(a): a = 2fun(a)print a # 1
a = []def fun(a): a.append(1)fun(a)print a # [1]

所有的變量都可以理解是內存中一個對象的“引用”,或者,也可以看似c中void*的感覺。

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

當一個引用傳遞給函數的時候,函數自動復制一份引用,這個函數裏的引用和外邊的引用沒有半毛關系了.所以第一個例子裏函數把引用指向了一個不可變對象,當函數返回的時候,外面的引用沒半毛感覺.而第二個例子就不一樣了,函數內的引用指向的是可變對象,對它的操作就和定位了指針地址一樣,在內存裏進行修改.

如果還不明白的話,這裏有更好的解釋: http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference

2 Python中的元類(metaclass)

這個非常的不常用,但是像ORM這種復雜的結構還是會需要的,詳情請看:《兩句話掌握Python最難知識點——元類》

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)"%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)來調用.

4 類變量和實例變量

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了.

可以看看下面的例子:

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().

技術分享

想要系統學習python和免費學習資料的 可以加裙 474534951

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.__superprivateTraceback (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內部的名字,用來區別其他用戶自定義的命名,以防沖突.

_foo:一種約定,用來指定變量私有.程序員用來指定私有變量的一種方式.

__foo:這個有真正的意義:解析器用_classname__foo來代替這個名字,以區別和其他類相同的命名.

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

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

"hi there %s" % name

但是,如果name恰好是(1,2,3),它將會拋出一個TypeError異常.為了保證它總是正確的,你必須這樣做:

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

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

你為什麽不用它?

  • 不知道它(在讀這個之前)

  • 為了和Python2.5兼容(譬如logging庫建議使用%(issue #4))

9 叠代器和生成器

這個是stackoverflow裏python排名第一的問題,值得一看: http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python

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. 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.命名參數在列表的最前端.例如:

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 面向切面編程AOP和裝飾器

這個AOP一聽起來有點懵,同學面阿裏的時候就被問懵了…

裝飾器是一個很著名的設計模式,經常被用於有切面需求的場景,較為經典的有插入日誌、性能測試、事務處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量函數中與函數功能本身無關的雷同代碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能。

這個問題比較大,推薦: http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python

技術分享

想要系統學習python和免費學習資料的 可以加裙 474534951

12 鴨子類型

“當看到一只鳥走起來像鴨子、遊泳起來像鴨子、叫起來也像鴨子,那麽這只鳥就可以被稱為鴨子。”

我們並不關心對象是什麽類型,到底是不是鴨子,只關心行為。

比如在python中,有很多file-like的東西,比如StringIO,GzipFile,socket。它們有很多相同的方法,我們把它們當作文件使用。

又比如list.extend()方法中,我們並不關心它的參數是不是list,只要它是可叠代的,所以它的參數可以是list/tuple/dict/字符串/生成器等.

鴨子類型在動態語言中經常使用,非常靈活,使得python不想java那樣專門去弄一大堆的設計模式。

13 Python中重載

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

  1. 可變參數類型。

  2. 可變參數個數。

另外,一個基本的設計原則是,僅僅當兩個函數除了參數類型和參數個數不同以外,其功能是完全相同的,此時才使用函數重載,如果兩個函數的功能其實不同,那麽不應當使用重載,而應當使用一個名字不同的函數。

好吧,那麽對於情況 1 ,函數功能相同,但是參數類型不同,python 如何處理?答案是根本不需要處理,因為 python 可以接受任何類型的參數,如果函數的功能相同,那麽不同的參數類型在 python 中很可能是相同的代碼,沒有必要做成兩個不同函數。

那麽對於情況 2 ,函數功能相同,但參數個數不同,python 如何處理?大家知道,答案就是缺省參數。對那些缺少的參數設定為缺省參數即可解決問題。因為你假設函數功能相同,那麽那些缺少的參數終歸是需要用的。

好了,鑒於情況 1 跟 情況 2 都有了解決方案,python 自然就不需要函數重載了。

14 新式類和舊式類

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

新式類很早在2.2就出現了,所以舊式類完全是兼容的問題,Python3裏的類全部都是新式類.這裏有一個MRO問題可以了解下(新式類是廣度優先,舊式類是深度優先),<Python核心編程>裏講的也很多.

15 __new__和__init__的區別

這個__new__確實很少見到,先做了解吧.

  1. __new__是一個靜態方法,而__init__是一個實例方法.

  2. __new__方法會返回一個創建的實例,而__init__什麽都不返回.

  3. 只有在__new__返回一個cls的實例時後面的__init__才能被調用.

  4. 當創建一個新實例時調用__new__,初始化一個實例時用__init__.

ps: __metaclass__是創建類時起作用.所以我們可以分別使用__metaclass__,__new__和__init__來分別在類創建,實例創建和實例初始化的時候做一些小手腳.

16 單例模式

這個絕對常考啊.絕對要記住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._instanceclass 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 obclass MyClass2(Borg): a = 1

3 裝飾器版本

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

4 import方法

作為python的模塊是天然的單例模式

# mysingleton.pyclass My_Singleton(object): def foo(self): passmy_singleton = My_Singleton()# to usefrom mysingleton import my_singletonmy_singleton.foo()

17 Python中的作用域

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

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

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

技術分享

18 GIL線程全局鎖

線程全局鎖(Global Interpreter Lock),即Python為了保證線程安全而采取的獨立線程運行的限制,說白了就是一個核只能在同一時間運行一個線程.

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

19 協程

知乎被問到了,呵呵噠,跪了

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

Python裏最常見的yield就是協程的思想!可以查看第九個問題.

20 閉包

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

當一個內嵌函數引用其外部作作用域的變量,我們就會得到一個閉包. 總結一下,創建一個閉包必須滿足以下幾點:

  1. 必須有一個內嵌函數

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

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

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

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

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

21 lambda函數

其實就是一個匿名函數,為什麽叫lambda?因為和後面的函數式編程有關.

22 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

23 Python裏的拷貝

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

import copya = [1, 2, 3, 4, [‘a‘, ‘b‘]] #原始對象b = a #賦值,傳對象的引用c = copy.copy(a) #對象拷貝,淺拷貝d = copy.deepcopy(a) #對象拷貝,深拷貝a.append(5) #修改對象aa[4].append(‘c‘) #修改對象a中的[‘a‘, ‘b‘]數組對象print ‘a = ‘, aprint ‘b = ‘, bprint ‘c = ‘, cprint ‘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‘]]

技術分享

24 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中實際上也存在一些垃圾,這些垃圾的回收會因為這種分代的機制而被延遲。

25 Python的List

26 Python的is

is是對比地址,==是對比值

27 read,readline和readlines

  • read 讀取整個文件

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

  • readlines 讀取整個文件到一個叠代器以供我們遍歷

28 Python2和3的區別

推薦:《Python 2.7.x 和 3.x 版本的重要區別》

比較全面的概述。