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種創建方式:

  1. 把列表生成式的 [ ] 改成 (),就創建了一個generator
  2. 函數中包含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)

生成器工作原理 :

  1. generator能夠迭代的關鍵是它有一個next()方法,工作原理就是通過調用next()方法,直到捕獲一個異常;
  2. 帶有yield的生成器同樣可以用next()方法調用生成器對象取值,next()有兩種調用方式:t.next() 和 next(t)。推薦使用for循環來取值(每執行一次,取生成器裏面的一個值)。
  3. yield相當於return返回一個值,並且記住這個返回的位置,下次迭代時,代碼從yield的嚇一跳語句開始執行。
  4. .send()和next()一樣,都能讓生成器往下一步走(下次遇到yield停),但是send()能傳一個值,可以強行修改上一個yield表達式值。

python迭代器

迭代器提供了一種不依賴索引的迭代取值方式。首先明白迭代器和可迭代對象的概念:

  • 可迭代對象:內置有__iter__()方法的對象,都成爲可迭代對象(str, list, tuple, dict, set)。
  • 迭代器對象*:內置有__next__()方法的對象,執行該方法可以不依賴索引值;有內置有__iter__()方法的對象,執行迭代器的__iter__()方法得到的是迭代器本身。

因此,迭代器一定是可迭代對象,可迭代對象不一定是迭代器。執行可迭代對象下的__iter__()方法,返回一個迭代器對象,再通過迭代器對象的__next__()方法取值。

python的for循環

for循環本質爲迭代器循環,其工作原理如下 :

  1. 先調用in後面對象的__iter__()方法,將其編程一個迭代器對象;
  2. 調用next(迭代器),將得到的返回值賦值給變量;
  3. 循環往復,直到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裝飾器

裝飾器的本質是把原來的函數裝飾成新的函數,並且返回這個函數本身的高階函數(一個函數作爲參數傳遞給另一個函數,或者一個函數的返回值爲另一個函數,滿足其一則爲高階函數)。
以計算函數執行時間爲例,介紹裝飾器的使用。

  1. 裝飾沒有參數的函數
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()
  1. 裝飾帶有參數的函數
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)
  1. 裝飾帶有返回值的函數
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)
  1. 通用裝飾器(有參有返回值)
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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章