python必看面試題(一)
python中 == 和 is 的區別是什麼?
python對象包含三個基本要素:id(身份標識)、type(數據類型) 和 value(值)。
== 比較操作符,is 統一性操作符。
is和==對對象比較判斷的內容不同: == 比較判斷兩個對象的value是否相等,is比較判斷的是兩個對象的id(唯一身份標識)是否相同。
深拷貝和淺拷貝
深拷貝指的是複製內容,重新開闢一塊內存,淺拷貝指的是兩個變量同時指向一個內存ID。
import copy
a = [1, 2, 3, 4, 5]
b = a # 淺拷貝
c = copy.deepcopy(a) # 深拷貝
d = a[:] # 深拷貝
e = copy.copy(a) # 當拷貝對象爲可變類型時,爲深拷貝,爲不可變類型時爲淺拷貝。
私有化和Proprety
對於類中定義的私有屬性,可以通過定義get()和set()方法在類的外部獲取和修改值。但是,在類的外部,程序對類中方法和屬性的調用有所不同,方法的調用格式爲:實例名.方法名(參數),屬性的調用格式爲:實例名.屬性名。方法和屬性的調用格式並不統一,爲了可以使用屬性的方式調用類中的方法,可以在定義方法前面加上@property裝飾器。
class Test(object):
def __init__(self):
self.__age = 100
@property
def num(self):
print("-------getter--------")
return self.__age
@num.setter
def num(self, newage):
print("-------setter--------")
self.__age = newage
t = Test() # 創建實例
t.num = 200 # 修改屬性__num的值
print(t.num) # 獲取屬性__num
python的生成器
生成器一邊循環,一遍計算,佔用內存小,在使用的時候取值,降低CPU和內存空間,提高效率。一般使用for循環取值。
生成器的2種創建方式:
- 把列表生成式的 [ ] 改成 (),就創建了一個generator
- 函數中包含yield關鍵字,這個函數及時一個generator,調用函數就創建了一個生成器(generator)對象。
# (1)把列表生成式的 [ ] 改成 (),就創建了一個generator;
L = [x **2 for x in range(5)] # 列表生成式創建列表
g = (x **2 for x in range(5)) # 創建生成器
# (2)函數中包含yield關鍵字,這個函數及時一個generator,調用函數就創建了一個生成器(generator)對象。
def fib(max): # 定義生成器生成斐波那契數列
n, a, b = 0, 1, 1
while n < max:
yield b
a, b = b, a + b
n += 1
obj = fib(5) # 通過函數調用創建生成器對象。
for i in obj:
print(i)
生成器工作原理 :
- generator能夠迭代的關鍵是它有一個next()方法,工作原理就是通過調用next()方法,直到捕獲一個異常;
- 帶有yield的生成器同樣可以用next()方法調用生成器對象取值,next()有兩種調用方式:t.next() 和 next(t)。推薦使用for循環來取值(每執行一次,取生成器裏面的一個值)。
- yield相當於return返回一個值,並且記住這個返回的位置,下次迭代時,代碼從yield的嚇一跳語句開始執行。
- .send()和next()一樣,都能讓生成器往下一步走(下次遇到yield停),但是send()能傳一個值,可以強行修改上一個yield表達式值。
python迭代器
迭代器提供了一種不依賴索引的迭代取值方式。首先明白迭代器和可迭代對象的概念:
- 可迭代對象:內置有__iter__()方法的對象,都成爲可迭代對象(str, list, tuple, dict, set)。
- 迭代器對象*:內置有__next__()方法的對象,執行該方法可以不依賴索引值;有內置有__iter__()方法的對象,執行迭代器的__iter__()方法得到的是迭代器本身。
因此,迭代器一定是可迭代對象,可迭代對象不一定是迭代器。執行可迭代對象下的__iter__()方法,返回一個迭代器對象,再通過迭代器對象的__next__()方法取值。
python的for循環
for循環本質爲迭代器循環,其工作原理如下 :
- 先調用in後面對象的__iter__()方法,將其編程一個迭代器對象;
- 調用next(迭代器),將得到的返回值賦值給變量;
- 循環往復,直到next(迭代器)拋出異常,for循環自動捕捉異常後結束循環。
帶有else的for循環:當for 循環正常結束時,會執行else裏面的語句;當被break中斷時,不執行else裏面的語句。
# 例如查找列表中是否有3的倍數,沒有的話打印“沒有3的倍數”,有則什麼都不打印
# 通常方法需要定義一個布爾型的變量,如下代碼
a = [1,2,3,4,5]
has_value = False
for value in a:
if value % 3 == 0:
has_value = True
break
if not has_value:
print("沒有3的倍數")
# 上述代碼可以實現功能,但是還有一種更加優雅的方式實現,避免了定義布爾型變量
a = [1,2,3,4,5]
for value in a:
if value % 3 == 0:
break
else:
print('沒有3的倍數')
閉包
一個函數定義在另一個函數內,且 使用到了外部函數的參數,外部函數返回值爲內部函數的函數名,這樣的整個代碼塊稱之爲閉包。當外部函數參數確定時,內部函數參數可以反覆調用。
閉包避免了使用全局變量,此外 ,閉包允許將函數與其所操作的數據關聯起來。一般來說,當對象中只有一個方法時,使用閉包是更好的選擇。
# 定義一個閉包
def adder(x):
def wrapper(y):
return x + y
return wrapper
adder3 = adder(3)
b = adder3(4) # b的值爲7
c = adder3(6) # c的值爲9,此步解釋了當外部參數x不變時,內部函數可以反覆調用
python裝飾器
裝飾器的本質是把原來的函數裝飾成新的函數,並且返回這個函數本身的高階函數(一個函數作爲參數傳遞給另一個函數,或者一個函數的返回值爲另一個函數,滿足其一則爲高階函數)。
以計算函數執行時間爲例,介紹裝飾器的使用。
- 裝飾沒有參數的函數
import time
import functools
def timer(func): # 裝飾器函數
@functools.wraps(func) # 顯示爲被裝飾的函數的函數名
def inner(): # 閉包函數
st = time.time()
func()
print(time.time() - st)
return inner
@timer
def func1(): # 需要計算執行時間的函數
print('in func1')
time.sleep(3)
if __name__ == '__main__':
func1()
- 裝飾帶有參數的函數
import time
import functools
def timer(func):
@functools.wraps(func) # 顯示爲被裝飾的函數的函數名
def inner(*args, **kwargs): # 閉包函數中輸入形參,可以接收全部參數
st = time.time()
func(*args, **kwargs) # 將參數傳遞給被裝飾的函數
print(time.time() - st)
return inner
@timer
def func1(a, b, c): # 需要計算執行時間的函數
print('in func1')
print('a = {}, b = {}, c = {}'.format(a, b, c))
time.sleep(1)
if __name__ == '__main__':
func1(12, 2113, 324)
- 裝飾帶有返回值的函數
import time
import functools
def timer(func):
@functools.wraps(func) # 顯示爲被裝飾的函數的函數名
def inner(a, b, c):
st = time.time()
ret = func(a, b, c) # 通過ret接收返回值
print(time.time() - st)
return ret # 將返回值返回
return inner
@timer
def func1(a, b, c): # 需要計算執行時間的函數
print('in func1')
time.sleep(1)
return True
if __name__ == '__main__':
a = func1()
print(a)
- 通用裝飾器(有參有返回值)
import time
import functools
def timer(func):
@functools.wraps(func) # 顯示爲被裝飾的函數的函數名
def inner(*args, **kwargs):
st = time.time()
ret = func(*args, **kwargs)
print(time.time() - st)
return ret
return inner
@timer
def func1(a, b, c): # 需要計算執行時間的函數
print('in func1')
print('a = {}, b = {}, c = {}'.format(a, b, c))
time.sleep(1)
return a + b + c
if __name__ == '__main__':
a = func1(12, 2113, 324)
print(a)
python內置裝飾器有staticmethod, classmethod, property。
pyhton實例方法,類方法,靜態方法
類對象:將具有相似屬性和方法的對象總結抽象爲類對象,可以定義相似的屬性和方法,不同實例對象去引用類對象的屬性和方法,減少代碼的重複率 。
實例對象:又稱實例化對象,是通過類對象實例化出來的實例
實例方法:第一個參數必須是實例對象(self,可以傳遞類的屬性和方法),只能由實例對象調用
類方法:使用裝飾器@classmethod裝飾,第一個參數必須是當前類對象(cls,傳遞類的屬性和方法,不能傳遞實例的屬性和方法)。功能:將類本身作爲對象進行操作的方法。
靜態方法:使用裝飾器 @staticmethod裝飾,參數隨意(沒有self和cls參數),方法體中不能使用類或市裏的任何屬性和方法。實例對象和類對象都可以調用。功能:主要用來 存放邏輯性代碼,邏輯上屬於類,但是和類 本身沒有關係 ,可以理解爲靜態方法試了獨立的、單純的函數,僅僅託管於某個類的名稱空間中。
class Animal():
__num = 0 # 類屬性(已私有化,通過下面num方法訪問),所有實例對象共有
def __init__(self, name):
self.__name = name # 私有化實例屬性
def __new__(self, *args, **kargs): # 重寫__new__()方法,實現創建對象,__num自增1
Animal.__add_num()
return super(Animal, self).__new__(self)
@classmethod
def __add_num(cls): # 類方法,可以通過"類名.方法名"調用
cls.__num += 1
@classmethod
def print_hello(cls):
print('hello, this is a classmethod!')
@property
def name(self): # 實例方法
return self.__name
@property
def num(self): # 通過裝飾器爲__num提供訪問方式
return Animal.__num
def jump(self): # 實例方法
print("I'm {}, i am jumping!")
@staticmethod
def add_ab(a, b): # 靜態方法,邏輯性代碼,與類本身無關
print(a + b)
if __name__ == '__main__':
a = Animal('dog') # 實例化對象a
b = Animal('cat') # 實例化對象b
Animal.print_hello() # 調用類方法
print(a.num)
print(b.num)