python及其相關面試
Table of Contents
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*的感覺。
通過id
來看引用a
的記憶體地址可以比較理解:
a = 1 def fun(a): print "func_in",id(a) # func_in 41322472 a = 2 print"re-point",id(a), id(2) # re-point 41322448 41322448 print "func_out",id(a), id(1) # func_out 41322472 41322472 fun(a) print a # 1
注:具體的值在不同電腦上執行時可能不同。
可以看到,在執行完a = 2
之後,a
引用中儲存的值,即記憶體地址發生變化,由原來1
物件的所在的地址變成了2
這個實體物件的記憶體地址。
而第2個例子a
引用儲存的記憶體值就不會發生變化:
a = [] def fun(a): print "func_in",id(a) # func_in 53629256a.append(1) print "func_out",id(a) # func_out 53629256 fun(a) print a # [1]
這裡記住的是型別是屬於物件的,而不是變數。而物件有兩種,“可更改”(mutable)與“不可更改”(immutable)物件。在python中,strings, tuples, 和numbers是不可更改的物件,而 list, dict, set 等則是可以修改的物件。(這就是這個問題的重點)
當一個引用傳遞給函式的時候,函式自動複製一份引用,這個函式裡的引用和外邊的引用沒有半毛關係了.所以第一個例子裡函式把引用指向了一個不可變物件,當函式返回的時候,外面的引用沒半毛感覺.而第二個例子就不一樣了,函式內的引用指向的是可變物件,對它的操作就和定位了指標地址一樣,在記憶體裡進行修改.
2 Python中的元類(metaclass)
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)"%x a=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)
來呼叫.
\ | 例項方法 | 類方法 | 靜態方法 |
---|---|---|---|
a = A() | a.foo(x) | a.class_foo(x) | a.static_foo(x) |
A | 不可用 | A.class_foo(x) | A.static_foo(x) |
更多關於這個問題:
4 類變數和例項變數
類變數:
是可在類的所有例項之間共享的值(也就是說,它們不是單獨分配給每個例項的)。例如下例中,num_of_instance 就是類變數,用於跟蹤存在著多少個Test 的例項。
例項變數:
例項化之後,每個例項單獨擁有的變數。
class Test(object): num_of_instance = 0 def __init__(self, name): self.name = name Test.num_of_instance += 1 if __name__ == '__main__': print Test.num_of_instance # 0 t1 = Test('jack') print Test.num_of_instance # 1 t2 = Test('lucy') print t1.name , t1.num_of_instance # jack 2 print t2.name , t2.num_of_instance # lucy 2
補充的例子
class Person: name="aaa" 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了.
可以看看下面的例子:
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().
a = [1,2,3] b = {'a':1,'b':2,'c':3} c = True print type(a),type(b),type(c) # <type 'list'> <type 'dict'> <type 'bool'> print isinstance(a,list) # True
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.__superprivate Traceback (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內部的名字,用來區別其他使用者自定義的命名,以防衝突,就是例如__init__()
,__del__()
,__call__()
這些特殊方法
_foo
:一種約定,用來指定變數私有.程式設計師用來指定私有變數的一種方式.不能用from module import * 匯入,其他方面和公有一樣訪問;
__foo
:這個有真正的意義:解析器用_classname__foo
來代替這個名字,以區別和其他類相同的命名,它無法直接像公有成員一樣隨便訪問,通過物件名._類名__xxx這樣的方式可以訪問.
8 字串格式化:%和.format
.format在許多方面看起來更便利.對於%
最煩人的是它無法同時傳遞一個變數和元組.你可能會想下面的程式碼不會有什麼問題:
"hi there %s" % name
但是,如果name恰好是(1,2,3),它將會丟擲一個TypeError異常.為了保證它總是正確的,你必須這樣做:
"hi there %s" % (name,) # 提供一個單元素的陣列而不是一個引數
但是有點醜..format就沒有這些問題.你給的第二個問題也是這樣,.format好看多了.
你為什麼不用它?
- 不知道它(在讀這個之前)
- 為了和Python2.5相容(譬如logging庫建議使用
%
(issue #4))
9 迭代器和生成器
這裡有個關於生成器的建立問題面試官有考: 問: 將列表生成式中[]改成() 之後資料結構是否改變? 答案:是,從列表變為生成器
>>> 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 0x0000028F8B774200>
通過列表生成式,可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含百萬元素的列表,不僅是佔用很大的記憶體空間,如:我們只需要訪問前面的幾個元素,後面大部分元素所佔的空間都是浪費的。因此,沒有必要建立完整的列表(節省大量記憶體空間)。在Python中,我們可以採用生成器:邊迴圈,邊計算的機制—>generator
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. apple 1. banana 2. 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 = vegetable apple = 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一聽起來有點懵,同學面阿里的時候就被問懵了...
裝飾器是一個很著名的設計模式,經常被用於有切面需求的場景,較為經典的有插入日誌、效能測試、事務處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量函式中與函式功能本身無關的雷同程式碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的物件新增額外的功能。
12 鴨子型別
“當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。”
我們並不關心物件是什麼型別,到底是不是鴨子,只關心行為。
比如在python中,有很多file-like的東西,比如StringIO,GzipFile,socket。它們有很多相同的方法,我們把它們當作檔案使用。
又比如list.extend()方法中,我們並不關心它的引數是不是list,只要它是可迭代的,所以它的引數可以是list/tuple/dict/字串/生成器等.
鴨子型別在動態語言中經常使用,非常靈活,使得python不想java那樣專門去弄一大堆的設計模式。
13 Python中過載
函式過載主要是為了解決兩個問題。
- 可變引數型別。
- 可變引數個數。
另外,一個基本的設計原則是,僅僅當兩個函式除了引數型別和引數個數不同以外,其功能是完全相同的,此時才使用函式過載,如果兩個函式的功能其實不同,那麼不應當使用過載,而應當使用一個名字不同的函式。
好吧,那麼對於情況 1 ,函式功能相同,但是引數型別不同,python 如何處理?答案是根本不需要處理,因為 python 可以接受任何型別的引數,如果函式的功能相同,那麼不同的引數型別在 python 中很可能是相同的程式碼,沒有必要做成兩個不同函式。
那麼對於情況 2 ,函式功能相同,但引數個數不同,python 如何處理?大家知道,答案就是預設引數。對那些缺少的引數設定為預設引數即可解決問題。因為你假設函式功能相同,那麼那些缺少的引數終歸是需要用的。
好了,鑑於情況 1 跟 情況 2 都有了解決方案,python 自然就不需要函式過載了。
14 新式類和舊式類
這個面試官問了,我說了老半天,不知道他問的真正意圖是什麼.
新式類很早在2.2就出現了,所以舊式類完全是相容的問題,Python3裡的類全部都是新式類.這裡有一個MRO問題可以瞭解下(新式類是廣度優先,舊式類是深度優先),<Python核心程式設計>裡講的也很多.
一箇舊式類的深度優先的例子
class A(): def foo1(self): print "A" class B(A): def foo2(self): pass class C(A): def foo1(self): print "C" class D(B, C): pass d = D() d.foo1() # A
按照經典類的查詢順序從左到右深度優先
的規則,在訪問d.foo1()
的時候,D這個類是沒有的..那麼往上查詢,先找到B,裡面沒有,深度優先,訪問A,找到了foo1(),所以這時候呼叫的是A的foo1(),從而導致C重寫的foo1()被繞過
15 __new__
和__init__
的區別
這個__new__
確實很少見到,先做了解吧.
__new__
是一個靜態方法,而__init__
是一個例項方法.__new__
方法會返回一個建立的例項,而__init__
什麼都不返回.- 只有在
__new__
返回一個cls的例項時後面的__init__
才能被呼叫. - 當建立一個新例項時呼叫
__new__
,初始化一個例項時用__init__
.
ps: __metaclass__
是建立類時起作用.所以我們可以分別使用__metaclass__
,__new__
和__init__
來分別在類建立,例項建立和例項初始化的時候做一些小手腳.
16 單例模式
單例模式是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統中一個類只有一個例項而且該例項易於外界訪問,從而方便對例項個數的控制並節約系統資源。如果希望在系統中某個類的物件只能存在一個,單例模式是最好的解決方案。
__new__()
在__init__()
之前被呼叫,用於生成例項物件。利用這個方法和類的屬性的特點可以實現設計模式的單例模式。單例模式是指建立唯一物件,單例模式設計的類只能例項
這個絕對常考啊.絕對要記住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._instance class 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 ob class MyClass2(Borg): a = 1
3 裝飾器版本
def singleton(cls, *args, **kw): instances相關推薦
python及其相關面試
Table of ContentsPython語言特性1 Python的函式引數傳遞看兩個例子:a = 1 def fun(a): a = 2 fun(a) print a # 1a = [] def fun(a): a.append(1) fun(a) print a # [1]所有的變
python檔案IO及其相關問題
前天用python做專案,將字典存入檔案,以及讀取檔案中的字典。遇到的幾個問題:1.開啟檔案的時候為什麼要用with open('file' , 'r') as f:2.字典在檔案中的讀取與存入方法在網上找到了答案。1.關於 with ... as ...其實它的正確寫法應
[轉]Python中的eval()、exec()及其相關函數
ron 1-1 代碼 project call s函數 錯誤 被調用 loader 剛好前些天有人提到eval()與exec()這兩個函數,所以就翻了下Python的文檔。這裏就來簡單說一下這兩個函數以及與它們相關的幾個函數,如gl
Python程式設計思想(3):數字及其相關運算
Python 提供了三種數值型別:int(整型),float(浮點型)和complex(複數)。 int:通常被稱為整型或者整數,如200、299、10都屬於整型; float:浮點數包含整數和小數部分,如3.1415926,2.71828都屬於浮點數; complex:複數包含實數部分和虛數部分,形如
Python Day7(相關補充)
decode 設置 lib str 信息 rec message 種類 代碼塊 一、其他相關 1.isinstance(obj, cls) 檢查是否obj是否是類 cls 的對象 1 class Foo(object): 2 pass 3 4 obj =
python 集合相關操作
查看 pan logs per ngx 返回值 sdi 字典 any 集合相關操作 集合是一個無序的,不重復的數據組合,它有著兩個主要作用:去重以及關系測試。 去重指的是當把一個列表變成了集合,其中重復的內容就自動的被去掉了 關系測試指的是,測試兩組數據之間的交集、差集、並
python類相關總結(持續更新)
屬於 模塊 error pan 類成員 pro 相關 ror __str__ __init__ 構造函數 __dict__ vars()函數,獲取命名空間裏面的名稱 __str__ str()函數,輸出成員相關信息的內容 __repr__ repl()函數,
Spring Boot入門——JDBCTemplate使用及其相關問題解決
oca accounts abs method const error mas exist release 1、在pom.xml文件中引入相應依賴 <!-- mysql依賴 --> <dependency> <
java相關面試的基礎知識
111、3.4位double類型 ,float = 3.4是不對的,double位雙精度,8個字節64位,float位4個字節單精度32位,這樣向窄出轉換會使精度損失,正確寫成float = 3.4f int 為32位,Long位64位,Short為16位 Long a = 1L; Short b =12、M
python學習_day13___面試模擬真題講解
copy 斐波那契數列 後綴 個數字 .com ict 但是 utf-8 class 一、選擇題(32分) 1、python不支持的數據類型有:(A) A、char B、int C、float D、list 2、x = “foo” y = 2
python集合相關操作
對稱 symmetric date n) 內容 指定 成了 進行 都在 集合相關操作 集合是一個無序的,不重復的數據組合,它有著兩個主要作用:去重以及關系測試。 去重指的是當把一個列表變成了集合,其中重復的內容就自動的被去掉了 關系測試指的是,測試兩組數據之間的交集、差集、
python 列表相關內容
sort 雜項 出現 wan 增加 獲取 列表 .so 判斷 python 列表相關內容 1、增加2、修改3、切片4、清空5、刪除6、雜項 #定義兩個列表name2 = [1,2,3,4]name_list = [‘zhang‘,‘wang‘,‘li‘,‘liu‘,‘yan
記2017年年底,幾次Python後端面試
可惜了 項目 創業公司 由於 接口 現場 記得 搜狗 字符 前記:16年本科畢業,選了大公司的QA,工作一段時間後發現更喜歡做開發,遂走上了轉崗之路,在一家小公司的做了半年開發之後,由於公司原因,遂開始了艱難的投簡歷面試之路。臨近年底,工作機會不是很多,下面記錄了部分面試
python 時間 相關
mon 利用 時間戳 %u 字符串 時間戳轉換 arr 代碼 day http://www.jb51.net/article/47957.htm 不管何時何地,只要我們編程時遇到了跟時間有關的問題,都要想到 datetime 和 time 標準庫模塊,今天我們就用它內部的方
Linux系統,Centos7版本下搭建postfix服務器及其相關配置應用
服務器 .html 監聽端口 生成 工作目錄 new works close 網址 實驗報告 一、 實驗名稱:郵件服務器的搭建和相關使用功能的配置二、 實驗環境與要求:Linux系統 centos7版本搭建郵件服務器實現發信收信基本功能實現發信認證功能搭建好LAMP環境
python及其開發工具的安裝
配置 file 安裝包 還需 water 常用 得到 http 模塊 一、Python的安裝python 支持在多平臺上安裝與運行,我們一般從python的官方網站下載其安裝包,比如Windows下是exe文件,Linux下則是二進制文件。這裏我們安裝的是python3.6
串口及其相關知識
金屬 正常 之間 長度 hal 必須 計時 高電平 mark 本文介紹了串口通訊的基本概念、數據格式、通訊方式、典型的串口通訊標準等內容。 串口通訊,RS232,RS485,停止位,奇校驗,偶校驗 1 串口通訊 串口通訊(Serial Communication),
PYTHON 一些基礎面試題目總結
外部 刪除一個文件 tel 信息 mpi seq break list 請求 PYTHON 一些基礎面試題目總結http://www.bieryun.com/1191.html1. Python是如何進行內存管理的?答:從三個方面來說,一對象的引用計數機制,二垃
kernel信息及其相關命令
time modules usr 配置文件 系統啟動 amt 通過 ... 修改 內核 linux內核是單內核體系設計、但充分借鑒了微內核設計體系的優點,為內核引入模塊化機制 內核組成部分: kernel: 內核核心,
計劃任務及其相關命令
電腦 即使 它的 crontab 開機 優先級 執行 options 周期 Linux 計劃任務的分類: 一次性任何計劃: 未來的某時間點執行一次任務 at:專門用來處理一次性任務計劃 batch :系統自行選擇空閑