公眾號上看到的python面試題10坑
問題1:請問如何修改以下Python程式碼,使下面的程式碼呼叫類A的show方法?
class A(object):
def show(self):
print('base show')
class B(A):
def show(self):
print('derived show')
obj = B()
obj.show()
答:題目問的是呼叫類A的show方法,不單單只是顯示A中的show的內容,所以要通過class方法指定到A的類物件即可:
obj.__class__ = A
obj.show()
問題2:請問如何修改以下Python程式碼,使得程式碼能夠執行?
class A(object):
def __init__(self, a, b):
self.__a = a
self.__b = b
def myprint(self):
print('a=', self.__a, 'b=', self.__b)
a1 = A(10, 20)
a1.myprint()
a1(80)
答:該程式執行報了一個錯誤
Traceback (most recent call last): ('a=', 10, 'b=', 20) File "C:/Users/Lee/PycharmProjects/print/exeses.py", line 11, in <module> a1(80) TypeError: 'A' object is not callable
這個錯誤的大致意思是將A類當成了一個方法來進行呼叫是不可行的。為了讓物件例項能被直接呼叫,需要實現call方法。
#-*- coding:utf-8 -*- class A(object): def __init__(self, a, b): self.__a = a self.__b = b def myprint(self): print('a=', self.__a, 'b=', self.__b) def __call__(self, num):#num傳入 print('call:', num + self.__a)#隨意讓num做一些事 a1 = A(10, 20) a1.myprint() a1(80)
結果
('a=', 10, 'b=', 20)
('call:', 90)
問題3:下面這段程式碼的輸出是什麼?
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', a)
if a > 10:
return super(A, cls).__new__(cls)
return B()
def __init__(self, a):
print('INIT', a)
a1 = A(5)
a1.fn()
a2 = A(20)
a2.fn()
答:該題的關鍵點在於__new__方法。__init__其實不是例項化一個類的時候第一個被呼叫 的方法。當使用 a1 = A(5) 這樣的表示式來例項化一個類時,最先被呼叫的方法 其實是 __new__ 方法。所以很自然的,第一行輸出為NEW 5.接著判斷a是否大於10,此時a = 5,所以執行return B(),進入B類中執行;所以第二行和第三行是B類例項的呼叫B INIT, B fn。隨後新建一個a2的類例項,將a = 20傳入A類,先執行__new__,所以第四行是NEW 20,此時a大於了10,執行return super(A, cls).__new__(cls)
大致的意思是得到當前類的例項(如果要得到當前類的例項,應當在當前類中的__new__()方法語句中呼叫當前類的父類 的__new__()方法)
所以還是例項還是在A中,第五行輸出INIT 20,接著呼叫A類中的fn方法,輸出A fn。最終結果
('NEW', 5)
B INIT
B fn
('NEW', 20)
('INIT', 20)
A fn
問題4:下面這段程式碼輸出什麼?
ls = [1, 2, 3, 4]
list1 = [i for i in ls if i > 2]
print(list1)
list2 = [i * 2 for i in ls if i > 2]
print(list2)
dic1 = {x: x ** 2 for x in (2, 4, 6)}
print(dic1)
set1 = {x for x in 'hello world' if x not in 'low level'}
print(set1)
答:該題考查列表解析和字典還有集合的生成方法。輸出如下:
[3, 4]
[6, 8]
{2: 4, 4: 16, 6: 36}
set(['h', 'r', 'd'])
問題5:下面這段程式碼輸出什麼?
num = 9
def f1():
num = 20
def f2():
print(num)
f2()
f1()
f2()
答:該題考查全域性變數和區域性變數的知識。先定義一個num = 9的全域性變數,接著在f1中定義了num = 20.由於區域性變數只能在當前定義的函式或者類中被呼叫,而全域性變數可以在任何函式或者類中被呼叫,前提是沒有被同名的區域性變數所覆蓋。所以該題的答案很明顯了,呼叫f2,print num 應該是全域性變數的num = 9,f1中的num f2函式無法呼叫;接著呼叫f1,沒有print,所以無輸出;最後再一次呼叫f2,f1中的num = 20 並不能直接改變全域性變數的值,所以輸出9.要想在區域性變數中修改全域性變數的值,需要使用global
num = 9
def f1():
global num
num = 20
def f2():
print(num)
f2()
f1()
f2()
這樣num就被修改為了20
9
20
問題6:如何使用一行程式碼交換兩個變數的值?
a = 8
b = 9
答:
(a, b) = (b, a)
問題7:如何新增程式碼,使得沒有定義的方法都呼叫mydefault方法?
class A(object):
def __init__(self, a, b):
self.a1 = a
self.b1 = b
print('init')
def mydefault(self):
print('default')
a1 = A(10, 20)
a1.fn1()
a1.fn2()
a1.fn3()
答:可以使用__getattr__方法解決這個問題,__getattr__函式的作用: 如果屬性查詢(attribute lookup)在例項以及對應的類中(通過__dict__)失敗, 那麼會呼叫到類的__getattr__函式, 如果沒有定義這個函式,那麼丟擲AttributeError異常。由此可見,__getattr__一定是作用於屬性查詢的最後一步,兜底。class A(object):
def __init__(self, a, b):
self.a1 = a
self.b1 = b
print('init')
def mydefault(self, *args):
print('default')
def __getattr__(self, item):
return self.mydefault()#指向mydefault方法
a1 = A(10, 20)
a1.fn1()
a1.fn2()
a1.fn3()
這樣就就不會觸發異常了。還可以給mydefault方法增加一個*args不定引數來相容。問題8:一個包裡有三個模組,mod1,py, mod2.py, mod3.py, 但使用from demopack import * 匯入模組時,如何保證只有mod1, mod3被匯入了?
答:在包中init.py檔案,並在檔案中增加:
__all__ = ['mod1', 'mod3']
python模組中的__all__w屬性,可用於模組匯入時限制,此時被匯入模組若定義了__all__屬性,則只有__all__內指定的屬性、方法、類可被匯入。
若沒定義,則匯入模組內的所有公有屬性,方法和類 。
問題9:寫一個函式,接收整數引數n,返回一個函式,函式返回n和引數的積。
def f1(n):
def f2(x):
return n * x
return f2
num = f1(3)
print(num(9))
問題10:請問下面的程式碼有什麼隱患?
def strtest1(num):
str = 'first'
for i in range(num):
str += 'x'
return str
答:由於變數str是個不可變物件,每次迭代,Python都會生成新的str物件來儲存新的字串,num越大,建立的str物件越多,記憶體消耗越大。