python最新面試題整理(二)

1 Python的函數參數傳遞

看兩個如下例子,分析運行結果: 代碼一:

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*的感覺。 這裡記住的是類型是屬於對象的,而不是變量。而對象有兩種,“可更改”(mutable)與“不可更改”(immutable)對象。在python中,strings, tuples, 和numbers是不可更改的對象,而list,dict等則是可以修改的對象。(這就是這個問題的重點) 當一個引用傳遞給函數的時候,函數自動複製一份引用,這個函數里的引用和外邊的引用沒有半毛關係了.所以第一個例子裡函數把引用指向了一個不可變對象,當函數返回的時候,外面的引用沒半毛感覺.而第二個例子就不一樣了,函數內的引用指向的是可變對象,對它的操作就和定位了指針地址一樣,在內存裡進行修改.

2 Python中的元類(metaclass)

元類就是用來創建類的“東西”。你創建類就是為了創建類的實例對象,但是我們已經學習到了Python中的類也是對象。好吧,元類就是用來創建這些類(對象)的,元類就是類的類 這個非常的不常用,詳情請看:《深刻理解Python中的元類(metaclass)》

3 @staticmethod和@classmethod

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

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

<code>@classmethoddef class_foo(cls,x):    print( "executing class_foo(%s,%s)"%(cls,x))@staticmethoddef static_foo(x):    print ("executing static_foo(%s)"%x)/<code>

a=A() 這裡先理解下函數參數裡面的self和cls.這個self和cls是對類或者實例的綁定.對於實例方法,我們知道在類裡每次定義方法的時候都需要綁定這個實例,就是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)

4 類變量和實例變量

<code>class Person:name="aaa"/<code>

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了. 可以看看下面的例子:

<code>class Person:name=[]/<code>

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

6 字典推導式

可能你見過列表推導時,卻沒有見過字典推導式,在2.7中才加入的: d = {key: value for (key, value) in iterable}

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

<code>1>>> class MyClass():2 ...     def __init__(self):3 ...             self.__superprivate = "Hello"4 ...             self._semiprivate = ", world!"5 ...6 >>> mc = MyClass()7 >>> print(mc.__superprivate)8 Traceback (most recent call last):9  File "<stdin>", line 1, in <module>10 AttributeError: myClass instance has no attribute '__superprivate'11 >>> print(mc._semiprivate)12 , world!13 >>> print mc.__dict__14 {'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}/<module>/<stdin>/<code>

foo:一種約定,Python內部的名字,用來區別其他用戶自定義的命名,以防衝突. -foo:一種約定,用來指定變量私有.程序員用來指定私有變量的一種方式. -foo:這個有真正的意義:解析器用_classname__foo來代替這個名字,以區別和其他類相同的命名. 詳情見: http://www.zhihu.com/question/19754941

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

.format在許多方面看起來更便利.對於%最煩人的是它無法同時傳遞一個變量和元組.你可能會想下面的代碼不會有什麼問題: Python: "hi there %s" % name 但是,如果name恰好是(1,2,3),它將會拋出一個TypeError異常.為了保證它總是正確的,你必須這樣做: "hi there %s" % (name,) # 提供一個單元素的數組而不是一個參數

9 迭代器和生成器

在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。 可以被next()函數調用並不斷返回下一個值的對象稱為迭代器:Iterator。

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

10 * args and ** kwargs

用args和**kwargs只是為了方便並沒有強制使用它們. 當你不確定你的函數里將要傳遞多少參數時你可以用args.例如,它可以傳遞任意數量的參數:

<code>1>>> def print_everything(*args):2     for count, thing in enumerate(args):3 ...       print '{0}. {1}'.format(count, thing)4 ..5 >>> print_everything('apple', 'banana', 'cabbage')6 0. apple7 1. banana8 2. cabbage/<code>

相似的 ** kwargs允許你使用沒有事先定義的參數名:

<code>1>>> def table_things(**kwargs):2 ...     for name, value in kwargs.items():3 ...         print '{0} = {1}'.format(name, value)4 ...5 >>> table_things(apple = 'fruit', cabbage = 'vegetable')6 cabbage = vegetable7 apple = fruit/<code>

你也可以混著用.命名參數首先獲得參數值然後所有的其他參數都傳遞給*args和**kwargs.命名參數在列表的最前端.例如:

<code>1def table_things(titlestring, ** kwargs)/<code>
  • args和** kwargs可以同時在函數的定義中,但是args必須在**kwargs前面. 當調用函數時你也可以用和**語法.例如:
<code>1>>> def print_three_things(a, b, c):2 ...     print 'a = {0}, b = {1}, c = {2}'.format(a,b,c)3 ...4 >>> mylist = ['aardvark', 'baboon', 'cat']5 >>> print_three_things(*mylist)67 a = aardvark, b = baboon, c = cat/<code>

就像你看到的一樣,它可以傳遞列表(或者元組)的每一項並把它們解包.注意必須與它們在函數里的參數相吻合.當然,你也可以在函數定義或者函數調用時用*. http://stackoverflow.com/questions/3394835/args-and-kwargs

11 面向切面編程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

12 鴨子類型

“當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。” 我們並不關心對象是什麼類型,到底是不是鴨子,只關心行為。 比如在python中,有很多file-like的東西,比如StringIO,GzipFile,socket。它們有很多相同的方法,我們把它們當作文件使用。 又比如list.extend()方法中,我們並不關心它的參數是不是list,只要它是可迭代的,所以它的參數可以是list/tuple/dict/字符串/生成器等. 鴨子類型在動態語言中經常使用,非常靈活,使得python不想java那樣專門去弄一大堆的設計模式。

13 Python中重載

引自知乎:http://www.zhihu.com/question/20053359 函數重載主要是為了解決兩個問題。

  1. 可變參數類型。
  2. 可變參數個數。 另外,一個基本的設計原則是,僅僅當兩個函數除了參數類型和參數個數不同以外,其功能是完全相同的,此時才使用函數重載,如果兩個函數的功能其實不同,那麼不應當使用重載,而應當使用一個名字不同的函數。 好吧,那麼對於情況 1 ,函數功能相同,但是參數類型不同,python 如何處理?答案是根本不需要處理,因為 python 可以接受任何類型的參數,如果函數的功能相同,那麼不同的參數類型在 python 中很可能是相同的代碼,沒有必要做成兩個不同函數。 那麼對於情況 2 ,函數功能相同,但參數個數不同,python 如何處理?大家知道,答案就是缺省參數。對那些缺少的參數設定為缺省參數即可解決問題。因為你假設函數功能相同,那麼那些缺少的參數終歸是需要用的。 好了,鑑於情況 1 跟 情況 2 都有了解決方案,python 自然就不需要函數重載了。

14 新式類和舊式類

這個面試官問了,我說了老半天,不知道他問的真正意圖是什麼. 這篇文章很好的介紹了新式類的特性: http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html 新式類很早在2.2就出現了,所以舊式類完全是兼容的問題,Python3裡的類全部都是新式類.這裡有一個MRO問題可以瞭解下(新式類是廣度優先,舊式類是深度優先),<python>裡講的也很多./<python>

15 __new__和__init__的區別;

<code>這個__new__確實很少見到,先做了解吧.1.__new__是一個靜態方法,而__init__是一個實例方法.2.__new__方法會返回一個創建的實例,而__init__什麼都不返回.3.只有在__new__返回一個cls的實例時後面的__init__才能被調用.4.當創建一個新實例時調用__new__,初始化一個實例時用__init__.ps: __metaclass__是創建類時起作用.所以我們可以分別使用__metaclass__,__new__和__init__來分別在類創建,實例創建和實例初始化的時候做一些小手腳./<code>

16 單例模式

這個絕對常考啊.絕對要記住1~2個方法,當時面試官是讓手寫的.

1 使用__new__方法
<code>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/<code>
2 共享屬性

創建實例時把所有實例的__dict__指向同一個字典,這樣它們具有相同的屬性和方法.

<code>1class Borg(object):2    _state = {}3    def __new__(cls, *args, **kw):4        ob = super(Borg, cls).__new__(cls, *args, **kw)5        ob.__dict__ = cls._state6      return ob78 class MyClass2(Borg):9    a = 1/<code>
3 裝飾器版本
<code>1def singleton(cls, *args, **kw):2    instances = {}3  def getinstance():4        if cls not in instances:5            instances[cls] = cls(*args, **kw)6        return instances[cls]7    return getinstance89 @singleton10 class MyClass:/<code>
4 import方法

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

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

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的元素的序列。

<code>>>>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/<code> 

23 Python裡的拷貝

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

<code>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']]/<code>

24 Python垃圾回收機制

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

1 引用計數

PyObject是每個對象必有的內容,其中ob_refcnt就是做為引用計數。當一個對象有新的引用時,它的ob_refcnt就會增加,當引用它的對象被刪除,它的ob_refcnt就會減少.引用計數為0時,該對象生命就結束了。 優點:

  1. 簡單
  2. 實時性 缺點:
  3. 維護引用計數消耗資源
  4. 循環引用
2 標記-清除機制

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

3 分代技術

分代回收的整體思想是:將系統中的所有內存塊根據其存活時間劃分為不同的集合,每個集合就成為一個“代”,垃圾收集頻率隨著“代”的存活時間的增大而減小,存活時間通常利用經過幾次垃圾回收來度量。 Python默認定義了三代對象集合,索引數越大,對象存活時間越長。 舉例: 當某些內存塊M經過了3次垃圾收集的清洗之後還存活時,我們就將內存塊M劃到一個集合A中去,而新分配的內存都劃分到集合B中去。當垃圾收集開始工作時,大多數情況都只對集合B進行垃圾回收,而對集合A進行垃圾回收要隔相當長一段時間後才進行,這就使得垃圾收集機制需要處理的內存少了,效率自然就提高了。在這個過程中,集合B中的某些內存塊由於存活時間長而會被轉移到集合A中,當然,集合A中實際上也存在一些垃圾,這些垃圾的回收會因為這種分代的機制而被延遲。

25 Python裡面如何實現tuple和list的轉換?

答:tuple,可以說是不可變的list,訪問方式還是通過索引下標的方式。 當你明確定義個tuple是,如果僅有一個元素,必須帶有,例如:(1,)。 當然,在2.7以後的版,python裡還增加了命名式的tuple!

至於有什麼用,首先第一點,樓主玩過python都知道,python的函數可以有多返回值的,而python裡,多返回值,就是用tuple來表示,這是用的最廣的了,

比如說,你需要定義一個常量的列表,但你又不想使用list,那也可以是要你管tuple,例如: if a in ('A','B','C'):pass

26 Python的is

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

27 read,readline和readlines

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

28 Python2和3的區別

大部分Python庫都同時支持Python 2.7.x和3.x版本的,所以不論選擇哪個版本都是可以的。但為了在使用Python時避開某些版本中一些常見的陷阱,或需要移植某個Python項目 使用__future__模塊 print函數 整數除法 Unicode xrange 觸發異常 處理異常 next()函數和.next()方法 For循環變量與全局命名空間洩漏 比較無序類型 使用input()解析輸入內容 返回可迭代對象,而不是列表

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

29 到底什麼是Python?你可以在回答中與其他技術進行對比

答案 下面是一些關鍵點: • Python是一種解釋型語言。這就是說,與C語言和C的衍生語言不同,Python代碼在運行之前不需要編譯。其他解釋型語言還包括PHP和Ruby。 • Python是動態類型語言,指的是你在聲明變量時,不需要說明變量的類型。你可以直接編寫類似x=111和x="I'm a string"這樣的代碼,程序不會報錯。 • Python非常適合面向對象的編程(OOP),因為它支持通過組合(composition)與繼承(inheritance)的方式定義類(class)。Python中沒有訪問說明符(access specifier,類似C++中的public和private),這麼設計的依據是“大家都是成年人了”。 • 在Python語言中,函數是第一類對象(first-class objects)。這指的是它們可以被指定給變量,函數既能返回函數類型,也可以接受函數作為輸入。類(class)也是第一類對象。 • Python代碼編寫快,但是運行速度比編譯語言通常要慢。好在Python允許加入基於C語言編寫的擴展,因此我們能夠優化代碼,消除瓶頸,這點通常是可以實現的。numpy就是一個很好地例子,它的運行速度真的非常快,因為很多算術運算其實並不是通過Python實現的。 • Python用途非常廣泛——網絡應用,自動化,科學建模,大數據應用,等等。它也常被用作“膠水語言”,幫助其他語言和組件改善運行狀況。 • Python讓困難的事情變得容易,因此程序員可以專注於算法和數據結構的設計,而不用處理底層的細節。 為什麼提這個問題: 如果你應聘的是一個Python開發崗位,你就應該知道這是門什麼樣的語言,以及它為什麼這麼酷。以及它哪裡不好。

30 補充缺失的代碼

def print_directory_contents(sPath): """ 這個函數接受文件夾的名稱作為輸入參數, 返回該文件夾中文件的路徑, 以及其包含文件夾中文件的路徑。 """ # 補充代碼 答案 def print_directory_contents(sPath): import osfor sChild in os.listdir(sPath):sChildPath = os.path.join(sPath,sChild) if os.path.isdir(sChildPath): print_directory_contents(sChildPath) else: print sChildPath 特別要注意以下幾點: • 命名規範要統一。如果樣本代碼中能夠看出命名規範,遵循其已有的規範。 • 遞歸函數需要遞歸併終止。確保你明白其中的原理,否則你將面臨無休無止的調用棧(callstack)。 • 我們使用os模塊與操作系統進行交互,同時做到交互方式是可以跨平臺的。你可以把代碼寫成sChildPath = sPath + '/' + sChild,但是這個在Windows系統上會出錯。 • 熟悉基礎模塊是非常有價值的,但是別想破腦袋都背下來,記住Google是你工作中的良師益友。 • 如果你不明白代碼的預期功能,就大膽提問。 • 堅持KISS原則!保持簡單,不過腦子就能懂! 為什麼提這個問題: • 說明面試者對與操作系統交互的基礎知識 • 遞歸真是太好用啦

31 閱讀下面的代碼,寫出A0,A1至An的最終值。

A0 = dict(zip(('a','b','c','d','e'),(1,2,3,4,5))) A1 = range(10) A2 = [i for i in A1 if i in A0] A3 = [A0[s] for s in A0] A4 = [i for i in A1 if i in A3] A5 = {i:ii for i in A1} A6 = [[i,ii] for i in A1] 答案 A0 = {'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4} A1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] A2 = [] A3 = [1, 3, 2, 5, 4] A4 = [1, 2, 3, 4, 5] A5 = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81} A6 = [[0, 0], [1, 1], [2, 4], [3, 9], [4, 16], [5, 25], [6, 36], [7, 49], [8, 64], [9, 81]] 為什麼提這個問題: • 列表解析(list comprehension)十分節約時間,對很多人來說也是一個大的學習障礙。 • 如果你讀懂了這些代碼,就很可能可以寫下正確地值。 • 其中部分代碼故意寫的怪怪的。因為你共事的人之中也會有怪人。

32 下面代碼會輸出什麼:

def f(x,l=[]): for i in range(x): l.append(i*i) print(l)

f(2) f(3,[3,2,1]) f(3) 答案: [0, 1] [3, 2, 1, 0, 1, 4] [0, 1, 0, 1, 4] 呃? 第一個函數調用十分明顯,for循環先後將0和1添加至了空列表l中。l是變量的名字,指向內存中存儲的一個列表。第二個函數調用在一塊新的內存中創建了新的列表。l這時指向了新生成的列表。之後再往新列表中添加0、1和4。很棒吧。第三個函數調用的結果就有些奇怪了。它使用了之前內存地址中存儲的舊列表。這就是為什麼它的前兩個元素是0和1了。

33 你如何管理不同版本的代碼?

答案: 版本管理!被問到這個問題的時候,你應該要表現得很興奮,甚至告訴他們你是如何使用Git(或是其他你最喜歡的工具)追蹤自己和奶奶的書信往來。我偏向於使用Git作為版本控制系統(VCS),但還有其他的選擇,比如subversion(SVN)。 為什麼提這個問題: 因為沒有版本控制的代碼,就像沒有杯子的咖啡。有時候我們需要寫一些一次性的、可以隨手扔掉的腳本,這種情況下不作版本控制沒關係。但是如果你面對的是大量的代碼,使用版本控制系統是有利的。版本控制能夠幫你追蹤誰對代碼庫做了什麼操作;發現新引入了什麼bug;管理你的軟件的不同版本和發行版;在團隊成員中分享源代碼;部署及其他自動化處理。它能讓你回滾到出現問題之前的版本,單憑這點就特別棒了。還有其他的好功能。怎麼一個棒字了得!

34 “猴子補丁”(monkey patching)指的是什麼?這種做法好嗎?

答案: “猴子補丁”就是指,在函數或對象已經定義之後,再去改變它們的行為。 舉個例子: import datetime datetime.datetime.now = lambda: datetime.datetime(2012, 12, 12) 大部分情況下,這是種很不好的做法 - 因為函數在代碼庫中的行為最好是都保持一致。打“猴子補丁”的原因可能是為了測試。mock包對實現這個目的很有幫助。 為什麼提這個問題? 答對這個問題說明你對單元測試的方法有一定了解。你如果提到要避免“猴子補丁”,可以說明你不是那種喜歡花裡胡哨代碼的程序員(公司裡就有這種人,跟他們共事真是糟糕透了),而是更注重可維護性。還記得KISS原則碼?答對這個問題還說明你明白一些Python底層運作的方式,函數實際是如何存儲、調用等等。 另外:如果你沒讀過mock模塊的話,真的值得花時間讀一讀。這個模塊非常有用。

35 閱讀下面的代碼,它的輸出結果是什麼?

class A(object): def go(self): print "go A go!" def stop(self): print "stop A stop!" def pause(self): raise Exception("Not Implemented")

class B(A): def go(self): super(B, self).go() print "go B go!"

class C(A): def go(self): super(C, self).go() print "go C go!" def stop(self): super(C, self).stop() print "stop C stop!"

class D(B,C): def go(self): super(D, self).go() print "go D go!" def stop(self): super(D, self).stop() print "stop D stop!" def pause(self): print "wait D wait!"

class E(B,C): pass

a = A() b = B() c = C() d = D() e = E()

說明下列代碼的輸出結果

a.go() b.go() c.go() d.go() e.go()

a.stop() b.stop() c.stop() d.stop() e.stop()

a.pause() b.pause() c.pause() d.pause() e.pause() 答案 輸出結果以註釋的形式表示: a.go()

go A go!

b.go()

go A go!

go B go!

c.go()

go A go!

go C go!

d.go()

go A go!

go C go!

go B go!

go D go!

e.go()

go A go!

go C go!

go B go!

a.stop()

stop A stop!

b.stop()

stop A stop!

c.stop()

stop A stop!

stop C stop!

d.stop()

stop A stop!

stop C stop!

stop D stop!

e.stop()

stop A stop!

a.pause()

... Exception: Not Implemented

b.pause()

... Exception: Not Implemented

c.pause()

... Exception: Not Implemented

d.pause()

wait D wait!

e.pause()

...Exception: Not Implemented

為什麼提這個問題? 因為面向對象的編程真的真的很重要。不騙你。答對這道問題說明你理解了繼承和Python中super函數的用法。

36 閱讀下面的代碼,它的輸出結果是什麼?

<code>class Node(object):    def __init__(self,sName):        self._lChildren = []        self.sName = sName    def __repr__(self):        return "<node>".format(self.sName)    def append(self,*args,**kwargs):        self._lChildren.append(*args,**kwargs)    def print_all_1(self):        print self        for oChild in self._lChildren:            oChild.print_all_1()    def print_all_2(self):        def gen(o):            lAll = [o,]            while lAll:                oNext = lAll.pop(0)                lAll.extend(oNext._lChildren)                yield oNext        for oNode in gen(self):            print oNodeoRoot = Node("root")oChild1 = Node("child1")oChild2 = Node("child2")oChild3 = Node("child3")oChild4 = Node("child4")oChild5 = Node("child5")oChild6 = Node("child6")oChild7 = Node("child7")oChild8 = Node("child8")oChild9 = Node("child9")oChild10 = Node("child10")oRoot.append(oChild1)oRoot.append(oChild2)oRoot.append(oChild3)oChild1.append(oChild4)oChild1.append(oChild5)oChild2.append(oChild6)oChild4.append(oChild7)oChild3.append(oChild8)oChild3.append(oChild9)oChild6.append(oChild10)# 說明下面代碼的輸出結果oRoot.print_all_1()oRoot.print_all_2()/<node>/<code>
<code>答案oRoot.print_all_1()會打印下面的結果:<node><node><node><node><node><node><node><node><node><node><node>oRoot.print_all_1()會打印下面的結果:<node><node><node><node><node><node><node><node><node><node><node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<code>

為什麼提這個問題? 因為對象的精髓就在於組合(composition)與對象構造(object construction)。對象需要有組合成分構成,而且得以某種方式初始化。這裡也涉及到遞歸和生成器(generator)的使用。 生成器是很棒的數據類型。你可以只通過構造一個很長的列表,然後打印列表的內容,就可以取得與print_all_2類似的功能。生成器還有一個好處,就是不用佔據很多內存。 有一點還值得指出,就是print_all_1會以深度優先(depth-first)的方式遍歷樹(tree),而print_all_2則是寬度優先(width-first)。有時候,一種遍歷方式比另一種更合適。但這要看你的應用的具體情況。

37 介紹一下except的用法和作用?

答:try…except…except…[else…][finally…] 執行try下的語句,如果引發異常,則執行過程會跳到except語句。對每個except分支順序嘗試執行,如果引發的異常與except中的異常組匹配,執行相應的語句。如果所有的except都不匹配,則異常會傳遞到下一個調用本代碼的最高層try代碼中。 try下的語句正常執行,則執行else塊代碼。如果發生異常,就不會執行 如果存在finally語句,最後總是會執行。

38 Python中pass語句的作用是什麼?

答:pass語句不會執行任何操作,一般作為佔位符或者創建佔位程序,whileFalse:pass

39 介紹一下Python下range()函數的用法?

答:列出一組數據,經常用在for in range()循環中

40 Python裡面match()和search()的區別?

答:re模塊中match(pattern,string[,flags]),檢查string的開頭是否與pattern匹配。 re模塊中research(pattern,string[,flags]),在string搜索pattern的第一個匹配值。

print(re.match(‘super’, ‘superstition’).span()) (0, 5) print(re.match(‘super’, ‘insuperable’)) None print(re.search(‘super’, ‘superstition’).span()) (0, 5) print(re.search(‘super’, ‘insuperable’).span()) (2, 7)

41 用Python匹配HTML tag的時候,<.>和<.>有什麼區別?

答:術語叫貪婪匹配( <.> )和非貪婪匹配(<.> ) 例如: test <.> : test <.> :

42 Python裡面如何生成隨機數?

答:random模塊 隨機整數:random.randint(a,b):返回隨機整數x,a<=x<=b random.randrange(start,stop,[,step]):返回一個範圍在(start,stop,step)之間的隨機整數,不包括結束值。 隨機實數:random.random( ):返回0到1之間的浮點數 random.uniform(a,b):返回指定範圍內的浮點數。

43 有沒有一個工具可以幫助查找python的bug和進行靜態的代碼分析?

答:PyChecker是一個python代碼的靜態分析工具,它可以幫助查找python代碼的bug, 會對代碼的複雜度和格式提出警告 Pylint是另外一個工具可以進行codingstandard檢查

44 如何在一個function裡面設置一個全局的變量?

答:解決方法是在function的開始插入一個global聲明: def f() global x

45 單引號,雙引號,三引號的區別

答:單引號和雙引號是等效的,如果要換行,需要符號(),三引號則可以直接換行,並且可以包含註釋 如果要表示Let’s go 這個字符串 單引號:s4 = ‘Let\\’s go’ 雙引號:s5 = “Let’s go” s6 = ‘I realy like“python”!’ 這就是單引號和雙引號都可以表示字符串的原因了

46 Python和多線程(multi-threading)。這是個好主意嗎?列舉一些讓Python代碼以並行方式運行的方法。

答案:Python並不支持真正意義上的多線程。Python中提供了多線程包,但是如果你想通過多線程提高代碼的速度,使用多線程包並不是個好主意。Python中有一個被稱為Global Interpreter Lock(GIL)的東西,它會確保任何時候你的多個線程中,只有一個被執行。線程的執行速度非常之快,會讓你誤以為線程是並行執行的,但是實際上都是輪流執行。經過GIL這一道關卡處理,會增加執行的開銷。這意味著,如果你想提高代碼的運行速度,使用threading包並不是一個很好的方法。 不過還是有很多理由促使我們使用threading包的。如果你想同時執行一些任務,而且不考慮效率問題,那麼使用這個包是完全沒問題的,而且也很方便。但是大部分情況下,並不是這麼一回事,你會希望把多線程的部分外包給操作系統完成(通過開啟多個進程),或者是某些調用你的Python代碼的外部程序(例如Spark或Hadoop),又或者是你的Python代碼調用的其他代碼(例如,你可以在Python中調用C函數,用於處理開銷較大的多線程工作)。 為什麼提這個問題 因為GIL就是個混賬東西(A-hole)。很多人花費大量的時間,試圖尋找自己多線程代碼中的瓶頸,直到他們明白GIL的存在。

47 將下面的函數按照執行效率高低排序。

它們都接受由0至1之間的數字構成的列表作為輸入。這個列表可以很長。一個輸入列表的示例如下:[random.random() for i in range(100000)]。你如何證明自己的答案是正確的。 def f1(lIn): l1 = sorted(lIn) l2 = [i for i in l1 if i<0.5] return [i*i for i in l2]

def f2(lIn): l1 = [i for i in lIn if i<0.5] l2 = sorted(l1) return [i*i for i in l2]

def f3(lIn): l1 = [ii for i in lIn] l2 = sorted(l1) return [i for i in l1 if i

答案:按執行效率從高到低排列:f2、f1和f3。要證明這個答案是對的,你應該知道如何分析自己代碼的性能。Python中有一個很好的程序分析包,可以滿足這個需求。 import cProfile lIn = [random.random() for i in range(100000)] cProfile.run('f1(lIn)') cProfile.run('f2(lIn)') cProfile.run('f3(lIn)')

為了向大家進行完整地說明,下面我們給出上述分析代碼的輸出結果:

<code>>>> cProfile.run('f1(lIn)')         4 function calls in 0.045 seconds   Ordered by: standard name   ncalls  tottime  percall  cumtime  percall filename:lineno(function)        1    0.009    0.009    0.044    0.044 <stdin>:1(f1)        1    0.001    0.001    0.045    0.045 <string>:1(<module>)        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}        1    0.035    0.035    0.035    0.035 {sorted}>>> cProfile.run('f2(lIn)')         4 function calls in 0.024 seconds   Ordered by: standard name   ncalls  tottime  percall  cumtime  percall filename:lineno(function)        1    0.008    0.008    0.023    0.023 <stdin>:1(f2)        1    0.001    0.001    0.024    0.024 <string>:1(<module>)        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}        1    0.016    0.016    0.016    0.016 {sorted}>>> cProfile.run('f3(lIn)')         4 function calls in 0.055 seconds   Ordered by: standard name   ncalls  tottime  percall  cumtime  percall filename:lineno(function)        1    0.016    0.016    0.054    0.054 <stdin>:1(f3)        1    0.001    0.001    0.055    0.055 <string>:1(<module>)        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}        1    0.038    0.038    0.038    0.038 {sorted}/<module>/<string>/<stdin>/<module>/<string>/<stdin>/<module>/<string>/<stdin>/<code>

為什麼提這個問題? 定位並避免代碼瓶頸是非常有價值的技能。想要編寫許多高效的代碼,最終都要回答常識上來——在上面的例子中,如果列表較小的話,很明顯是先進行排序更快,因此如果你可以在排序前先進行篩選,那通常都是比較好的做法。其他不顯而易見的問題仍然可以通過恰當的工具來定位。因此瞭解這些工具是有好處的。

48 如何用Python來進行查詢和替換一個文本字符串?

可以使用sub()方法來進行查詢和替換,sub方法的格式為:sub(replacement, string[, count=0]) replacement是被替換成的文本 string是需要被替換的文本 count是一個可選參數,指最大被替換的數量

49 Python裡面search()和match()的區別?

match()函數只檢測RE是不是在string的開始位置匹配,search()會掃描整個string查找匹配, 也就是說match()只有在0位置匹配成功的話才有返回,如果不是開始位置匹配成功的話,match()就返回none

50 用Python匹配HTML tag的時候,<.>和<.>有什麼區別?

前者是貪婪匹配,會從頭到尾匹配 xyz,而後者是非貪婪匹配,只匹配到第一個 >。

51 Python裡面如何生成隨機數?

import random random.random() 它會返回一個隨機的0和1之間的浮點數

52 super init

super() lets you avoid referring to the base class explicitly, which can be nice. But the main advantage comes with multiple inheritance, where all sorts of fun stuff can happen. See the standard docs on super if you haven't already.

Note that the syntax changed in Python 3.0: you can just say super().init() instead of super(ChildB, self).init() which IMO is quite a bit nicer. http://stackoverflow.com/questions/576169/understanding-python-super-with-init-methods http://blog.csdn.net/mrlevo520/article/details/51712440

53 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. http://stackoverflow.com/questions/94935/what-is-the-difference-between-range-and-xrange-functions-in-python-2-x


分享到:


相關文章: