Python 精选笔试面试习题—类继承、方法对象、包管理、闭包、可变类型作为默认参数、列表引用、sort与sorted、 append 和 extend、深拷贝和浅拷贝
1. 類繼承
如下代碼
class A(object):def show(self):print 'This is calss A'class B(A):def show(self):print 'This is calss B'obj = B()
obj.show()
如何才能調(diào)用類 A 的 show 方法呢?
obj.__class__ = A
obj.show()
__ class __ 方法指向了類對象,只用給他賦值類型 A,然后調(diào)用方法 show,但是用完了記得修改回來。
2. 方法對象
問題:為了讓下面這段代碼運(yùn)行,需要增加哪些代碼?
class A(object):def __init__(self,a,b):self.__a = aself.__b = bdef myprint(self):print 'a=', self.__a, 'b=', self.__ba1=A(10,20)
a1.myprint()a1(80)
答案:為了能讓對象實(shí)例能被直接調(diào)用,需要實(shí)現(xiàn)__ call __ 方法
class A(object):def __init__(self, a, b):self.__a = aself.__b = bdef myprint(self):print 'a=', self.__a, 'b=', self.__bdef __call__(self, num):print 'call:', num + self.__a
3. __ new __ 和 __ init __
下面這段代碼輸出什么?
class B(object):def fn(self):print 'B fn'def __init__(self):print "B INIT"class A(object):def fn(self):print 'A fn'def __new__(cls,a):print "NEW", aif a>10:return super(A, cls).__new__(cls)return B()def __init__(self,a):print "INIT", aa1 = A(5)
a1.fn()
a2=A(20)
a2.fn()
輸出結(jié)果:
NEW 5
B INIT
B fn
NEW 20
INIT 20
A fn
使用__ new __ 方法,可以決定返回那個(gè)對象,也就是創(chuàng)建對象之前,這個(gè)可以用于設(shè)計(jì)模式的單例、工廠模式。__ init __ 是創(chuàng)建對象是調(diào)用的。
4. 默認(rèn)方法
如下代碼:
class A(object):def __init__(self,a,b):self.a1 = aself.b1 = bprint 'init'def mydefault(self):print 'default'a1 = A(10,20)
a1.fn1()
a1.fn2()
a1.fn3()
方法 fn1/fn2/fn3 都沒有定義,怎樣修改代碼,使得沒有定義的方法都調(diào)用 mydefault 函數(shù),上面的代碼應(yīng)該輸出
default
default
default
答案如下:
class A(object):def __init__(self,a,b):self.a1 = aself.b1 = bprint 'init'def mydefault(self):print 'default'def __getattr__(self,name):return self.mydefaulta1 = A(10,20)
a1.fn1()
a1.fn2()
a1.fn3()
方法__ getattr __ 只有當(dāng)沒有定義的方法調(diào)用時(shí),才是調(diào)用他。當(dāng) fn1 方法傳入?yún)?shù)時(shí),我們可以給mydefault 方法增加一個(gè) *args 不定參數(shù)來兼容。
class A(object):def __init__(self,a,b):self.a1 = aself.b1 = bprint 'init'def mydefault(self,*args):print 'default:' + str(args[0])def __getattr__(self,name):print "other fn:",namereturn self.mydefaulta1 = A(10,20)
a1.fn1(33)
a1.fn2('hello')
a1.fn3(10)
輸出結(jié)果如下:
init
other fn: fn1
default:33
other fn: fn2
default:hello
other fn: fn3
default:10
5. 包管理
一個(gè)包里有三個(gè)模塊,mod1.py, mod2.py, mod3.py,但使用 from demopack import * 導(dǎo)入模塊時(shí),如何保證只有 mod1、mod3 被導(dǎo)入了。
答案:增加__ init __.py 文件,并在文件中增加:
__all__ = ['mod1','mod3']
6. 閉包
寫一個(gè)函數(shù),接收整數(shù)參數(shù)n,返回一個(gè)函數(shù),函數(shù)的功能是把函數(shù)的參數(shù)和n相乘并把結(jié)果返回。
In [8]: def fun(n):...: def g(val):...: return n * val...: return g...: ...: ff = fun(7)...: print(ff(9))...:
63
7. 可變類型作為函數(shù)默認(rèn)參數(shù)
下面代碼輸出結(jié)果是什么?
def extendList(val, list=[]):list.append(val)return listlist1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')print "list1 = %s" % list1
print "list2 = %s" % list2
print "list3 = %s" % list3
輸出結(jié)果為:
list1 = [10, 'a']
list2 = [123]
list3 = [10, 'a']
很多人都會(huì)誤認(rèn)為 list1=[10], list3=[‘a(chǎn)’],因?yàn)樗麄円詾槊看?extendList 被調(diào)用時(shí),列表參數(shù)的默認(rèn)值都將被設(shè)置為[].但實(shí)際上的情況是,新的默認(rèn)列表只在函數(shù)被定義的那一刻創(chuàng)建一次。當(dāng) extendList 被沒有指定特定參數(shù)list調(diào)用時(shí),這組list的值隨后將被使用。這是因?yàn)閹в心J(rèn)參數(shù)的表達(dá)式在函數(shù)被定義的時(shí)候被計(jì)算,不是在調(diào)用的時(shí)候被計(jì)算。
因此 list1 和 list3 是在同一個(gè)默認(rèn)列表上進(jìn)行操作(計(jì)算)的。而 list2 是在一個(gè)分離的列表上進(jìn)行操作(計(jì)算)的。(通過傳遞一個(gè)自有的空列表作為列表參數(shù)的數(shù)值)。
extendList 的定義可以作如下修改。盡管,創(chuàng)建一個(gè)新的列表,沒有特定的列表參數(shù)。
下面這段代碼可能能夠產(chǎn)生想要的結(jié)果。
def extendList(val, list=None):if list is None:list = []list.append(val)return list
通過上面的修改,輸出結(jié)果將變成:
list1 = [10]
list2 = [123]
list3 = ['a']
8. 超出成員個(gè)數(shù)的切片
下面代碼輸出的結(jié)果是什么?
list = ['a', 'b', 'c', 'd', 'e']
print list[10:]
輸出結(jié)果為:
[]
例如,嘗試獲取list[10]和之后的成員,會(huì)導(dǎo)致 IndexError。
然而,嘗試獲取列表的切片,開始的index超過了成員個(gè)數(shù)不會(huì)產(chǎn)生 IndexError,而是僅僅返回一個(gè)空列表。
這成為特別讓人惡心的疑難雜癥,因?yàn)檫\(yùn)行的時(shí)候沒有錯(cuò)誤產(chǎn)生,導(dǎo)致bug很難被追蹤到。
9. 列表的引用
有以下代碼
1. list = [ [ ] ] * 5
2. list # output?
3. list[0].append(10)
4. list # output?
5. list[1].append(20)
6. list # output?
7. list.append(30)
8. list # output?
2,4,6,8行的輸出結(jié)果如下:
[[], [], [], [], []]
[[10], [10], [10], [10], [10]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30]
原因如下:
第一行的輸出結(jié)果直覺上很容易理解,例如 list = [ [ ] ] * 5 就是簡單的創(chuàng)造了5個(gè)空列表。
然而,理解表達(dá)式list=[ [ ] ] * 5的關(guān)鍵一點(diǎn)是它不是創(chuàng)造一個(gè)包含五個(gè)獨(dú)立列表的列表,而是它是一個(gè)創(chuàng)建了包含對同一個(gè)列表五次引用的列表。
只有了解了這一點(diǎn),我們才能更好的理解接下來的輸出結(jié)果。
list[0].append(10) 將10附加在第一個(gè)列表上。
但由于所有5個(gè)列表是引用的同一個(gè)列表,所以這個(gè)結(jié)果將是:
[[10], [10], [10], [10], [10]]
同理,list[1].append(20)將20附加在第二個(gè)列表上。但同樣由于5個(gè)列表是引用的同一個(gè)列表,所以輸出結(jié)果現(xiàn)在是:
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]]
作為對比, list.append(30)是將整個(gè)新的元素附加在外列表上,因此產(chǎn)生的結(jié)果是:
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30]
10. L.sort() 與 sorted() 的區(qū)別
L.sort(cmp=None, key=None, reverse=False)sorted(iterable, cmp=None, key=None, reverse=False)
- L.sort():該函數(shù)的三個(gè)參數(shù)和 sorted() 的后三個(gè)參數(shù)含義是一致的,而需要特別注意的是,該函數(shù)只適用于列表,而非任意可以迭代的對象。cmp 是比較函數(shù),接受兩個(gè)對象參數(shù) x 和 y,返回 負(fù)數(shù)(x<y),0(x=y),正數(shù)(x>y)。
- 該函數(shù)第一個(gè)參數(shù) iterable 為任意可以迭代的對象,cmp 是比較函數(shù),通常為lambda函數(shù),key 是用于比較的關(guān)鍵字,reverse表示排序結(jié)果是否反轉(zhuǎn)。
In [10]: aa = [5,4,3,2,1]In [11]: aa.sort()In [12]: aa
Out[12]: [1, 2, 3, 4, 5]
調(diào)用 sort() 之后會(huì)改變原來的表的結(jié)構(gòu)順序。
可以指定關(guān)鍵字排序
student = [['Tom', 'A', 20], ['Jack', 'C', 18], ['Andy', 'B', 11]]
student.sort(key=lambda student: student[2])## 輸出結(jié)果
[['Andy', 'B', 11], ['Jack', 'C', 18], ['Tom', 'A', 20]]
sorted() 可以應(yīng)用于任意的可以迭代的對象,所以應(yīng)用范圍比 L.sort() 廣泛的多,可以應(yīng)用于字符串,元組,列表,字典等可迭代對象。
In [19]: s = "zyx"In [20]: sorted(s)
Out[20]: ['x', 'y', 'z']In [21]: s
Out[21]: 'zyx'
需要注意的是,該函數(shù)會(huì)返回一個(gè)排序后的列表,原有可迭代對象保持不變,這與 L.sort() 函數(shù)不同。然而,這會(huì)浪費(fèi)較大的存儲(chǔ)空間,尤其是數(shù)據(jù)量較大時(shí)。所以,在列表進(jìn)行排序時(shí),需要考慮是否需要保存原列表,如果無需保存原列表,則優(yōu)先使用L.sort() 節(jié)省內(nèi)存空間,提高效率。
- L.sort() 函數(shù)只適用于列表排序,而sorted()函數(shù)適用于任意可以迭代的對象排序。
- L.sort() 函數(shù)排序會(huì)改變原有的待排序列表,而sorted()函數(shù)則不會(huì)改變。所以在使用列表進(jìn)行排序時(shí),需要考慮是否需要保存原列表,如果無需保存原列表,則優(yōu)先使用L.sort() 節(jié)省內(nèi)存空間,提高效率。
- 兩個(gè)函數(shù)通過定義 key 和 cmp 都可以完成排序,但是 key 的效率要遠(yuǎn)遠(yuǎn)高于 cmp,所以要優(yōu)先使用 key 。
11. append 與 extend 方法有什么區(qū)別
append表示把某個(gè)數(shù)據(jù)當(dāng)做新元素整體追加到列表的最后面,它的參數(shù)可以是任意對象。
extend 的參數(shù)必須是一個(gè)可迭代對象,表示把該對象里面的所有元素逐個(gè)地追加到列表的后面。
In [1]: x = [1,2,3]In [2]: y = [4,5]In [3]: x.append(y)In [4]: x
Out[4]: [1, 2, 3, [4, 5]]In [5]: x = [1,2,3]In [6]: x.extend(y)In [7]: x
Out[7]: [1, 2, 3, 4, 5]
12. 深拷貝與淺拷貝
- copy.copy 是淺拷貝,只會(huì)復(fù)制父對象,而不會(huì)復(fù)制對象內(nèi)部的子對象;
- copy.deepcopy 是深拷貝,會(huì)復(fù)制對象及其子對象;
In [1]: import copyIn [2]: a = [1,2,3, ['a', 'b']]In [3]: b = aIn [4]: c = a[:]In [5]: d = copy.copy(a)In [6]: e = copy.deepcopy(a)In [7]: id(a)
Out[7]: 62660040LIn [8]: id(b)
Out[8]: 62660040LIn [9]: id(c)
Out[9]: 62660424LIn [10]: id(d)
Out[10]: 62519944LIn [11]: id(e)
Out[11]: 62520776L
通過 = 賦值,它的 id 是和 a 本身是一樣的;
通過 [:] 切片賦值和 copy.copy() 賦值的 id 是一樣的,它們都是淺拷貝;
通過 copy.deepcopy() 賦值的 id 是和其它都不一樣,它是深拷貝;為啥叫做深拷貝,看下面。
In [12]: a[0] = 'aaa'In [13]: a[3].append('c')In [14]: a
Out[14]: ['aaa', 2, 3, ['a', 'b', 'c']]In [15]: b
Out[15]: ['aaa', 2, 3, ['a', 'b', 'c']]In [16]: c
Out[16]: [1, 2, 3, ['a', 'b', 'c']]In [17]: d
Out[17]: [1, 2, 3, ['a', 'b', 'c']]In [18]: e
Out[18]: [1, 2, 3, ['a', 'b']]
參考:
http://www.bugcode.cn/PythonQuestions.html
https://segmentfault.com/a/1190000006265256
http://www.cnblogs.com/wilber2013/p/5178620.html
http://python.jobbole.com/86525/
http://www.cnblogs.com/wilber2013/p/4645353.html
http://www.cnblogs.com/Vito2008/p/5044251.html
總結(jié)
以上是生活随笔為你收集整理的Python 精选笔试面试习题—类继承、方法对象、包管理、闭包、可变类型作为默认参数、列表引用、sort与sorted、 append 和 extend、深拷贝和浅拷贝的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何读取比机器内存大的文件(内存4G,文
- 下一篇: Python 常见的坑汇总