2020 年最全 Python 面试题汇总 (二)
@Author:Runsen
求職季在即,技巧千萬條,硬實力才是關鍵,聽說今年疫情大環境不好,更要好好準備才行。于是Runsen在牛客網,Leetcode,九章算法,不斷地尋找面試真題,共計100題,包括Python基礎,算法,數據庫,SQL,Linux。
大四刷題拼offer系列,不拼不行啊。我先刷下牛客網的Python的題目和知識點,適當的記錄下自己做
錯的題目。
文章目錄
- 21、__new__和__init__的區別
- 22、==和is的區別
- 23、*args and **kwargs
- 24、 閉包
- 25、PEP8規范
- 26、ascii、Unicode、utf-8、gbk的區別
- 27、進制轉換
- 28、可迭代對象迭代器
- 29、迭代器
- 30、獲取命令行參數
- 31、filter、map、reduce
- 32、yield和return的區別
- 33、生成器
- 34、enumerate
- 35、面向對象的三大特性
- 36、super
- 37、魔術方法
- 38、@staticmethod和@classmethod區別
- 39、@property
- 40、內置類屬性
21、__new__和__init__的區別
填空題:在Python中__new__ 和 __init__ 的區別主要表現在
-
__new__:創建對象時調用,會返回當前對象的一個實例
-
__init__:創建完對象后調用,對當前對象的一些實例初始化,無返回值
簡單總結如下:
1,__new__方法是對象的生成方法,__init__方法是對象的初始化方法。
2,對象生成后,才能初始化。故__new__方法在__init__方法之前調用。
3,__new__方法的第一個參數是cls,指的是類本身,__init__的第一個參數是self,指的是__new__方法生成的對象。
4,__new__方法的其余參數會和生成的對象一起繼續傳給__init__方法。
5,__new__方法的返回值通常調用其父類的__new__方法生成。
6,__init__方法不能有返回值。
7,__new__較少使用,可以用它實現單例模式,即一個類只能創建一個實例,有時候通過使用單例模式可以極大減少內存的占用。
22、==和is的區別
題目舉例:說明Python中==和is的區別。
參考解析:is是判讀對象標識符是否一致,而==是判讀兩個對象的內容是否相等!
x is y 相當于 id(x)==id(y)
==是檢查兩個對象的內容是否相等,會調用對象的內部__eq__()。
例如下面這段代碼,只有數值型和字符串型的情況下,a is b才為True,當a和b是tuple,list,dict或set型時,a is b為False。(下面代碼只舉了字符串和list)
>>> a = "Runsen" >>> b = "Runsen" >>> a is b True >>> id(a) == id(b) True >>> a == b True >>> c = [1,2,3] >>> d = [1,2,3] >>> c is d False >>> id(c) == id(d) False >>> c == d True23、*args and **kwargs
請問:在Python函數中*args and **kwargs有上面區別?
*args 代表一個元組(數組參數),**kwargs代表一個字典(字典參數)
# 單星號(*): 將所有參數以元祖(tuple)的形式導入 def signal_start(param1,*param2):print ("param1={}".format(param1))print ("*param2={}".format(param2)) signal_start(1,2,3,4,5) # param1=1 # *param2=(2, 3, 4, 5)# 雙星號(**)將參數以字典的形式導入 def double_start(param1,**param2):print ("param1={}".format(param1))print ("**param2={}".format(param2))double_start(1,a=2,b=3) # param1=1 # **param2={'a': 2, 'b': 3}24、 閉包
問:說說Python中閉包是什么?
答:可以將閉包理解為一種特殊的函數,這種函數由兩個函數的嵌套組成,外函數和內函數。
def 外層函數(參數):def 內層函數():print("內層函數執行", 參數)return 內層函數內層函數的引用 = 外層函數("傳入參數") 內層函數的引用()在一個外函數中定義了一個內函數,內函數里運用了外函數的臨時變量,并且外函數的返回值是內函數的引用。這樣就構成了一個閉包。
下面舉一個具體的閉包函數的實例,代碼如下。
# outer是外部函數 def outer(a):# inner是內函數def inner( b ):#在內函數中 用到了外函數的臨時變量print(a+b)# 外函數的返回值是內函數的引用return inner ret = outer(5) #ret = inner ret(10) #15 ret 存了外函數的返回值,也就是inner函數的引用,這里相當于執行inner函數25、PEP8規范
請列出至少5個PEP8規范
-
每一級縮進使用4個空續縮進對齊。
-
每行代碼的最大長度限制為79個字符
-
在表達式中避免無關的空格
-
代碼更改時,相應的注釋也要隨之更改。
-
命名要規范,通俗易懂
26、ascii、Unicode、utf-8、gbk的區別
- ascii 是最早美國用的標準信息交換碼,把所有的字母的大小寫,各種符號用 二進制來表示,共有256中,加入些拉丁文等字符,1bytes代表一個字符
- Unicode是為了統一世界各國語言的不用,統一用2個bytes代表一個字符,可以表達2^16=65556個,稱為萬國語言,特點:速度快,但浪費空間
- utf-8 為了改變Unicode的這種缺點,規定1個英文字符用1個字節表示,1個中文字符用3個字節表示,特點;節省空間,速度慢,用在硬盤數據傳輸,網絡數據傳輸,相比硬盤和網絡速度,體現不出來的
- gbk 是中文的字符編碼,用2個字節代表一個字符
27、進制轉換
進制轉換以十進制為媒介:十六進制前面加上0x,八進制加上0o,二進制前面加上0b。
| 二進制 | bin(int(x, 8)) | bin(int(x, 10)) | bin(int(x, 16)) | |
| 八進制 | oct(int(x, 2)) | oct(int(x, 10)) | oct(int(x, 16)) | |
| 十進制 | int(x, 2) | int(x, 8) | int(x, 16) | |
| 十六進制 | hex(int(x, 2)) | hex(int(x, 8)) | hex(int(x, 10)) |
28、可迭代對象迭代器
什么是可迭代對象?
可迭代的對象:常見的可以被for循環迭代的一些數據類型都是可迭代的對象,如列表,元組,字典,集合,字符串,生成器,range函數生成的數列等。凡是可作用于for循環的對象都是Iterable可迭代對象類型;
>>> iter(1111) Traceback (most recent call last):File "<stdin>", line 1, in <module> TypeError: 'int' object is not iterable >>> iter("1111") <str_iterator object at 0x000002430E9C2320>這些對象都有一個內置的iter方法,且該方法可以返回一個迭代器對象,當用iter(可迭代對象)調用這個對象時,會返回一個迭代器對象(屬于Iterator類)
29、迭代器
什么是迭代器?
凡是可作用于next( )函數的對象都是迭代器(Iterator類型),它們表示一個惰性計算的序列。
集合數據類型如list、tuple、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。
30、獲取命令行參數
Python代碼如何獲取命令行參數?
python中獲取命令行參數的方法有很多,早先的sys模塊的argv就是其中之一,現在使用比較多的是argparse模塊。
sys.argv[0]是Python腳本文件名,sys.argv[1:]之后的才是傳入到python腳本中的參數。所有通過命令行傳入到腳本中的參數,都是字符串類型!
下面是testargv.py文件中的代碼。
import sysif __name__ == "__main__":print(sys.argv)print(sys.argv[0])sum = 0if len(sys.argv) > 1:for i in sys.argv[1:]:sum += int(i) # 強制類型轉換成int類型print("sum = ", sum)else:print("No Parameter is passed in!")argparse更易于編寫用戶友好的命令行界面。程序定義所需要的參數,argparse將找出如何從sys.argv中解析這些參數。
使用步驟:
- import argparse 首先導入模塊
- parser = argparse.ArgumentParser() 創建一個解析對象
- parser.add_argument() 向該對象中添加你要關注的命令行參數和選項
- parser.parse_args() 進行解析
下面是testargparse.py的代碼。
import argparse# ArgumentParser對象保存了將命令行傳入的參數解析成Python數據類型的所以信息 parser = argparse.ArgumentParser(description='Process some integers.') # 調用add_argument,即可將程序的參數填充進ArgumentParser,規定 # ArgumentParser如何將命令行的字符轉換成objects parser.add_argument('integers', metavar='N', type=int, nargs='+',help='an integer for the accumulator') parser.add_argument('--sum', dest='accumulate', action='store_const',const=sum, default=max,help='sum the integers (default: find the max)') # 調用parse_args() 可以將上述所有的信息保存并開始使用 args = parser.parse_args() print(args.accumulate(args.integers))31、filter、map、reduce
filter() 相當于過濾器的作用
s=[1,2,3,5,6,8,9,10,25,12,30] # 篩選出3的倍數 # 第一個參數為一個返回True或者False的函數,第二個參數為可迭代對象 # 該函數把可迭代對象依次傳入第一個函數,如果為True,則篩選 d=filter(lambda x:True if x % 3 == 0 else False,s) print(list(d)) # [3, 6, 9, 12, 30]map與lambda連用
# 第一個參數為函數,依次將后面的參數傳給第一個函數,并執行函數 # 如果有多個參數則,依次將后面的對應傳給參數 s=map(lambda x,y:x+y,range(10),range(10)) print(list(s)) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] ss=map(lambda x:x*x,range(10)) print(list(ss)) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]reduce與lambda連用,與區別是:map返回的是Map對象,需要用List轉化。
from functools import reduce # 開始的時候將可迭代對象的第一個數和第二個數當成x和y # 然后將第一次函數的執行結果當成x,然后再傳入一個數當成y # 再執行函數 s=reduce(lambda x,y:x+y,range(101)) print(s) # 相當于0+1+2+……+99+100 # 505032、yield和return的區別
你知道yield和return的區別
相同點:都是返回函數執行的結果
不同點:return 在返回結果后結束函數的運行,而yield 則是讓函數變成一個生成器,生成器每次產生一個值(yield語句),函數被凍結,被喚醒后再產生一個值。
例子:求一組數的平方值
# return 實現:def squre(n):ls = [i*i for i in range(n)]return ls for i in squre(5):print(i, end=' ')結果為:0 1 4 9 16
# yield 實現:def squre(n):for i in range(n):yield i*ifor i in squre(5):print(i, end=' ')結果為:0 1 4 9 16
yield和return也很好區別,return就返回值,結束函數,yield只是保存,不會結束函數。想下,在scrapy中,如果將yield改為retrun,那么爬一個就return,也就是爬取一個數據就停止了。
33、生成器
什么是生成器?
在Python中,一邊循環一邊計算的機制,稱為生成器:generator。
因為列表所有數據都在內存中,如果有海量數據的話將會非常耗內存。
創建生成器,只要把一個列表生成式的[]改成(),就創建了一個generator:
>>> L = [x * x for x in range(10)] >>> L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x1022ef630>還有一種方法就是在函數中使用yield關鍵字,那么這個函數就不再是一個普通函數,而是一個generator。調用函數就是創建了一個生成器(generator)對象。
def gen():yield 1# 返回一個對象,這個對象的值是1 def ret():return 1# 返回一個數字1 g = gen() r = ret() print(g,r) print(next(g))#輸出如下 <generator object gen at 0x0000020452E83840> 1 1也是就函數中只要有yield,這個函數就會變成生成器。每次運行到yield的時候,函數會暫停,并且保存當前的運行狀態,返回返回當前的數值,并在下一次執行next方法的時候,又從當前位置繼續往下走。
34、enumerate
enumerate的意思是什么?如何使用。
在Python中,如果要在迭代列表的同時對迭代次數進行計數,可以使用 enumerate關鍵字。下面是沒有使用enumerate實現的遍歷。
Mylist = ['Runsen','為','offer', '而戰'] for i in range(len(Mylist )):print(i, Mylist [i])# 輸出如下 0 Runsen 1 為 2 offer 3 而戰enumerate()提供了強大的功能,例如,當您需要獲取索引列表時,它會派上用場:
for i, j in enumerate(Mylist):print(i, j)# 輸出如下 0 Runsen 1 為 2 offer 3 而戰35、面向對象的三大特性
簡述面向對象的三大特性?
-
繼承:繼承就是繼承的類直接擁有被繼承類的屬性而不需要在自己的類體中重新再寫一遍,其中被繼承的類叫做父類、基類,繼承的類叫做派生類、子類。
-
封裝: 封裝就是把類中的屬性和方法定義為私有的,方法就是在屬性名或方法名前加雙下劃線,而一旦這樣定義了屬性或方法名后,python會自動將其轉換為_類名__屬性名(方法名)的格式,在類的內部調用還是用雙下劃線加屬性名或方法名,在類的外部調用就要用_類名__屬性名(方法名)。父類的私有屬性和方法,子類無法對其進行修改。
-
多態:多態就是不同的對象可以調用相同的方法然后得到不同的結果,有點類似接口類的感覺,在python中處處體現著多態,比如不管你是列表還是字符串還是數字都可以使用+和*。
36、super
簡述super關鍵字?
調用父類(超類)的一個方法,可以使用 super() 函數,super() 函數的一個常見用法是在 init() 方法中確保父類被正確的初始化了,具體的看我寫的示例。
''' @Author: Runsen @微信公眾號: Python之王 @博客: https://blog.csdn.net/weixin_44510615 @Date: 2020/8/29 '''# 創建一個A類 class A(object):def __init__(self):self.x = 0def A(self):print("This A func")class B(A):# 創建 B 繼承 Adef __init__(self):super().__init__()self.y = 1def B(self):print("This B class ")print(self.x, self.y)super().A()if __name__ == '__main__':b = B()# 實例化 Bb.B()# 調用B# 輸出如下 This B class 0 1 This A func但是這里需要注意如果出現了"雙下劃線"的函數中的變量,只有類對象自己能訪問,連子類對象也不能訪問到這個數據。具體看我很早之前寫的博客的示例。
class Father():__money = 1000 #私有變量是繼承不了def __action(self): # 父類的私有方法money = 1000print('調用父類的方法')class Son(Father):def action(self):super()._Father__action()print(money) son=Son() son.action() 調用父類的方法 name 'money' is not defined這里的__action由于是"雙下劃線“,因此money 私有變量是不能用supper繼承不了,也就是不可以訪問父類中的私有屬性方法的變量money。
37、魔術方法
什么是魔術方法?
在Python中的面向對象中有很多魔術方法如:
__init__: 構造函數,在生成對象時調用 __del__: 析構函數,釋放對象時使用 __str__: 使用print(對象)或者str(對象)的時候觸發 __repr__: 在使用repr(對象)的時候觸發,前提是沒有__str__。如果有__str__,則沒有用。 __setitem__ : 按照索引賦值:每當屬性被賦值的時候都會調用該方法:self.__dict__[name] = value __getitem__: 按照索引獲取值:當訪問不存在的屬性時會調用該方法 _delitem__(self,name): 當刪除屬性時調用該方法 __len__: 獲得長度 __cmp__: 比較運算 __call__: 函數調用 __add__: 加運算 __sub__: 減運算 __mul__: 乘運算 __div__: 除運算 __mod__: 求余運算 __pow__: 乘方下面,我只介紹幾個常見的。__init__和__init__在上面有談到,這里不再述說。
1、__str__:用于處理打印實例本身的時候的輸出內容。如果沒有覆寫該函數,則默認輸出一個對象名稱和內存地址。
2、 __del__ : 析構方法,恰好在對象要被刪除之前調用,目的是做一些釋放銷毀工作。
3、__call__:其實就是調用這個對象的 對象名__call__()方法,可以用來改變實例的內部成員的值。
下面看一個具體示例,區別上面的三種魔術方法。
''' @Author: Runsen @微信公眾號: Python之王 @博客: https://blog.csdn.net/weixin_44510615 @Date: 2020/8/29 '''class Student(object):def __init__(self, name, age): # 重寫了__init__方法self.name = nameself.age = agedef __str__(self):return "姓名:%s; 年齡:%d" % (self.name, self.age)def __del__(self):# 當對象被銷毀時,會自動調用這個方法print('__del__ 方法被調用了')def __call__(self, a, b):self.name = aself.age = bprint('__call__ with ({}, {})'.format(self.name, self.age))def say(self):return "Runsen為Offer而戰"s = Student('Runsen', 20) print(s) s("runsen",20) print(s) print(s.say()) del s# 輸出如下 姓名:Runsen; 年齡:20 __call__ with (runsen, 20) 姓名:runsen; 年齡:20 Runsen為Offer而戰 __del__ 方法被調用了38、@staticmethod和@classmethod區別
說下@staticmethod和@classmethod的區別
其實@staticmethod和@classmethod都是用來聲明靜態方法的。只不過一個聲明靜態方法,一個聲明類方法。
-
類方法:使用裝飾器@classmethod。第一個參數為當前類的對象,通常為cls。實例對象和類對象都可以調用類方法。(不用聲明對象就可以調用)
-
靜態方法:使用裝飾器@staticmethod。沒有self和cls參數。方法體中不能使用類或者實例的任何屬性和方法。實例對象和類對象都可以調用。
為了方便大家了解兩者的差別,以下的示例代碼將有助于發現其中的差別:
''' @Author: Runsen @微信公眾號: 潤森筆記 @博客: https://blog.csdn.net/weixin_44510615 @Date: 2020/8/30 ''' class A():@classmethoddef get_name(cls, name):print(cls) # <class '__main__.A'>print('my name is %s' % name)@staticmethoddef get_age(age):print(f'i am %s years old' % age)if __name__ == '__main__':A.get_name('Runsen') # my name is RunsenA.get_age(20) # i am 20 years old@staticmethod 與 @classmethod在Python中稱為 裝飾器,用來修飾函數,相當于添加一個額外的功能,比如不再像普通函數那樣進行實例化。通過使用裝飾器可以讓代碼更加整潔,易讀。用了修飾器之后,也可以進行實例化之后再調用,但是就顯得多此一舉了。
| a = A() | a.get_name() | a.get_name() | a.get_name() |
| A | 不可用 | A.get_name() | A.get_name() |
39、@property
說下你對@property的理解
@property可以將一個方法的調用變成屬性調用。舉例說明:平時我們調用數據屬性和方法,是這樣的
class School():name = "家里蹲大學"def test(self):print("實例方法")@propertydef test_pro(self):print("靜態屬性")if __name__ == "__main__":s = School()print(s.name)s.test()#輸出 # 家里蹲大學# 實例方法如果我們想直接類似于數據屬性一樣的去調用方法
class School():name = "家里蹲大學"def test(self):print("實例方法")@propertydef test_pro(self):print("靜態屬性")if __name__ == "__main__":s = School()print(s.name)# 注意返回的函數 千萬別加()s.test_pro#輸出 # 家里蹲大學# 靜態屬性40、內置類屬性
說下你了解幾個內置類屬性?
__dict__: 類的屬性(包含一個字典,由類的數據屬性組成)
__doc__ :類的文檔字符串
__name__: 類名
__module__: 類定義所在的模塊
__bases__: 類的所有父類構成元素(包含了一個由所有父類組成的元組)
如果你想跟博主建立親密關系,可以關注博主,或者關注博主公眾號“Python之王”,了解一個非本科程序員是如何成長的。
博主ID:潤森,希望大家點贊、評論、收藏
總結
以上是生活随笔為你收集整理的2020 年最全 Python 面试题汇总 (二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 五十六、TodoList的三种写法,祭奠
- 下一篇: 2020 年最全 Python 面试题汇