Python 基礎面試第三彈

2023-08-31 06:00:53

1. 獲取當前目錄下所有檔名

import os

def get_all_files(directory):
    file_list = []
# os.walk返回一個生成器,每次迭代時返回當前目錄路徑、子目錄列表和檔案列表 for root, dirs, files in os.walk(directory): for file in files: file_list.append(os.path.join(root, file)) return file_list # 獲取當前目錄下的所有檔名 current_directory = os.getcwd() files = get_all_files(current_directory) # 列印所有檔名 for file in files: print(file)

 

2. Python中生成器和迭代器區別

迭代器(Iterator)是一種實現了迭代協定的物件,它必須提供一個__iter__()方法和一個__next__()方法。通過呼叫__iter__()方法,迭代器可以返回自身,並且通過呼叫__next__()方法,迭代器可以依次返回下一個元素,直到沒有更多元素時丟擲StopIteration異常。迭代器是一種惰性計算的方式,每次只在需要時生成一個元素,從而節省記憶體空間。可以使用iter()函數將可迭代物件轉換為迭代器。

生成器(Generator)是一種特殊的迭代器,它使用了更為簡潔的語法來定義迭代器。生成器可以通過函數中的yield關鍵字來實現,當函數執行到yield語句時,會暫停執行並返回一個值,儲存當前狀態,下次呼叫時從上次暫停的位置繼續執行。生成器函數可以像普通函數一樣接收引數,並且可以包含迴圈、條件語句等邏輯。生成器是一種非常方便和高效的迭代器實現方式。

下面是生成器和迭代器的區別總結:

  1. 語法:生成器使用yield關鍵字來定義,而迭代器需要實現__iter__()__next__()方法。
  2. 實現:生成器可以使用函數來定義,而迭代器可以由類來實現。
  3. 狀態儲存:生成器在yield語句處暫停執行並儲存當前狀態,下次呼叫時從上次暫停的位置繼續執行;迭代器通過內部的狀態和指標來儲存迭代的位置。
  4. 簡潔性:生成器的語法更加簡潔,可以使用普通的函數定義和控制流語句;迭代器需要實現多個特殊方法,程式碼相對較多。
  5. 惰性計算:生成器是惰性計算的,每次只在需要時生成一個元素;迭代器也可以實現惰性計算,但需要手動控制。

總之,生成器是一種特殊的迭代器,它提供了更簡潔和方便的語法。生成器可以通過函數中的yield語句來實現迭代過程,並且可以像普通函數一樣編寫邏輯。迭代器是一種更通用的概念,可以通過類來實現,需要顯式地定義__iter__()__next__()方法。無論是生成器還是迭代器,它們都能夠實現按需生成和處理大量資料的能力,提高了程式碼的效率和可讀性。

 一個小栗子:

當我們需要遍歷一個很大的資料集時,生成器可以幫助我們按需生成資料,而不是一次性載入整個資料集到記憶體中。

下面是一個簡單的例子,我們使用生成器來按需生成斐波那契數列的前n個元素:

def fibonacci_generator(n):
    a, b = 0, 1
    count = 0
    while count < n:
        yield a
        a, b = b, a + b
        count += 1

# 使用生成器按需生成斐波那契數列的前10個元素
fibonacci = fibonacci_generator(10)

# 逐個列印生成的元素
for num in fibonacci:
    print(num)

  

  在上述程式碼中,我們定義了一個生成器函數fibonacci_generator,它使用了yield語句來生成斐波那契數列的元素。每次呼叫生成器的__next__()方法時,它會執行到yield語句處,

返回當前的斐波那契數並暫停執行,儲存當前狀態。然後,下次呼叫生成器的__next__()方法時,它會從上次暫停的位置繼續執行,生成下一個斐波那契數。這樣,我們可以通過迭代生成器

來按需獲取斐波那契數列的元素。當我們遍歷生成器物件時,它會依次生成斐波那契數列的元素並列印出來。由於生成器是按需生成資料的,它只在需要時生成一個元素,而不是一次性生成整

個數列。這樣可以節省記憶體空間,特別是當斐波那契數列很大時。總結起來,生成器可以看作是一種特殊的函數,它能夠按需生成資料,節省記憶體空間,並且提供了一種簡潔和方便的方式來

實現迭代器。通過使用生成器,我們可以避免一次性載入大量資料到記憶體中,而是在需要時逐個生成資料,從而提高程式碼的效率和可延伸性。

3. 什麼是可迭代物件,其原理又是什麼

  可迭代物件(Iterable)是指可以被迭代遍歷的物件。在許多程式語言中,迭代是指按照一定的順序逐個存取集合中的元素的過程。在Python中,可迭代物件是指實現了迭代器協定(Iterator Protocol)的物件。

迭代器協定包含兩個方法:

  1. __iter__()方法:該方法返回一個迭代器物件。迭代器物件用於實現具體的迭代邏輯,並且必須包含__next__()方法。

  2. __next__()方法:該方法返回迭代器中的下一個元素。如果沒有元素可供返回,它應該引發StopIteration異常。

當我們使用可迭代物件進行迭代時,實際上是通過迭代器物件來完成的。迭代器物件負責追蹤當前的迭代狀態,並提供下一個元素。迭代器物件會在每次迭代時呼叫__next__()方法,並返回下一個元素,直到遍歷完所有元素或者引發StopIteration異常為止。

Python中許多內建的資料型別和容器都是可迭代物件,例如列表(List)、元組(Tuple)、字典(Dictionary)、集合(Set)等。此外,我們也可以通過自定義類來實現可迭代物件,只需在類中定義__iter__()方法,並在該方法中返回一個迭代器物件即可。

以下是一個範例,展示瞭如何使用可迭代物件和迭代器物件進行迭代:

# 建立一個可迭代物件
my_list = [1, 2, 3, 4, 5]

# 獲取迭代器物件
my_iter = iter(my_list)

# 使用迭代器物件進行迭代
try:
    while True:
        item = next(my_iter)
        print(item)
except StopIteration:
    pass

  在上述範例中,我們通過呼叫iter()函數獲取了my_list的迭代器物件my_iter,然後使用next()函數從迭代器物件中獲取下一個元素並列印,直到遍歷完所有元素或引發

StopIteration異常為止。可迭代物件的原理是基於迭代器協定的實現,通過迭代器物件的__next__()方法來提供序列中的下一個元素。這種機制使得我們可以方便地對集合中的元素

進行逐個存取和處理,提供了一種統一的迭代介面

自己實現可迭代物件小栗子

 
class MyIterable:
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        self.index = 0
        return self

    def __next__(self):
        if self.index < len(self.data):
            item = self.data[self.index]
            self.index += 1
            return item
        else:
            raise StopIteration

# 建立一個可迭代物件範例
my_iterable = MyIterable([1, 2, 3, 4, 5])

# 使用迭代器進行迭代
for item in my_iterable:
    print(item)

4. Python2 和 Python3主要的區別有哪些:

Python 2.x 和 Python 3.x 之間的一些主要區別:

  1. 列印函數:在 Python 2.x 中,列印語句是一個關鍵字,使用類似於 print "Hello" 的語法。而在 Python 3.x 中,print 被改造為一個內建函數,需要使用括號,例如 print("Hello")

  2. 整數除法:在 Python 2.x 中,整數除法的結果會被截斷為整數,例如 5 / 2 的結果是 2。而在 Python 3.x 中,整數除法的結果將保留小數部分,得到浮點數結果,例如 5 / 2 的結果是 2.5。如果想要執行截斷整數除法,可以使用 // 運運算元。

  3. Unicode 字串:在 Python 2.x 中,字串型別分為普通字串和 Unicode 字串(以 u 字首表示),這導致字串處理的一些混亂和困惑。而在 Python 3.x 中,所有字串都是 Unicode 字串,普通字串是以位元組表示的,需要使用 b 字首表示。

  4. xrange 替代 range:在 Python 2.x 中,range 函數返回的是一個列表,如果需要生成大範圍的整數序列,會佔用大量記憶體。而在 Python 3.x 中,range 函數的實現類似於 Python 2.x 中的 xrange,返回一個迭代器物件,節省了記憶體。

  5. 異常語法:在 Python 2.x 中,捕獲異常時使用的語法是 except Exception, e,將異常物件儲存在變數 e 中。而在 Python 3.x 中,使用 except Exception as e 的語法,將異常物件儲存在變數 e 中。

  6. input 函數:在 Python 2.x 中,input 函數會將使用者輸入的內容作為 Python 程式碼進行解析,存在安全風險。而在 Python 3.x 中,input 函數始終將使用者輸入的內容作為字串返回,不進行解析。

除了上述主要區別之外,Python 3.x 還進行了一些其他改進,包括改進的類定義語法、更好的模組管理和匯入機制、更一致的例外處理和錯誤機制等。然而,這也導致了 Python 2.x 程式碼無法直接在 Python 3.x 中執行,需要進行一些修改和調整。

 

5. 說說Python中多執行緒和多程序

  1. 多執行緒(Multithreading):

    • 多執行緒是指在一個程序內建立多個執行緒,每個執行緒獨立執行不同的任務。多執行緒共用程序的記憶體空間,因此執行緒之間可以方便地共用資料。
    • 在 Python 中,可以使用 threading 模組來建立和管理執行緒。通過建立 Thread 類的範例,指定要執行的函數或方法,並呼叫 start() 方法,可以啟動一個執行緒。
    • Python 的多執行緒由於全域性直譯器鎖(Global Interpreter Lock,GIL)的存在,同一時刻只允許一個執行緒執行 Python 位元組碼。這意味著多執行緒並不能充分利用多核處理器,並行效能受限。多執行緒適用於 I/O 密集型任務,如網路請求、檔案讀寫等,但對於 CPU 密集型任務,多執行緒並不能提升效能。
  2. 多程序(Multiprocessing):

    • 多程序是指建立多個獨立的程序,每個程序都有自己的記憶體空間和系統資源。多個程序之間相互獨立,可以並行執行不同的任務。每個程序都有自己的 Python 直譯器,因此可以充分利用多核處理器,提高並行效能。
    • 在 Python 中,可以使用 multiprocessing 模組來建立和管理程序。通過建立 Process 類的範例,指定要執行的函數或方法,並呼叫 start() 方法,可以啟動一個程序。
    • 多程序可以通過程序間通訊(Inter-Process Communication,IPC)來實現程序之間的資料共用。Python 提供了多種 IPC 機制,如佇列(Queue)、管道(Pipe)和共用記憶體等。

總結:

  • 多執行緒適合處理 I/O 密集型任務,可以提高程式的響應能力和效率。
  • 多程序適合處理 CPU 密集型任務,可以充分利用多核處理器提高並行效能。
  • 在 Python 中,多執行緒受到 GIL 的限制,多程序可以繞過 GIL,實現真正的並行執行。
  • 使用多執行緒或多程序時需要注意執行緒安全和程序安全的問題,避免資料競爭和共用資源的衝突。

6. Python中GIL鎖:

  全域性直譯器鎖(Global Interpreter Lock,簡稱 GIL)是在 CPython 直譯器中存在的一個特性。它是一種機制,用於保證同一時刻只有一個執行緒執行 Python 位元組碼。雖然 GIL 的存在確保了 CPython 直譯器的執行緒安全性,但也對多執行緒並行執行帶來了一些限制。

以下是對 GIL 的一些詳細解釋和理解:

  1. GIL 的作用:

    • GIL 的主要作用是保護 CPython 直譯器內部的資料結構免受並行存取的影響,確保執行緒安全。
    • CPython 使用參照計數(Reference Counting)作為記憶體管理的主要機制。GIL 保證了在修改參照計數時的原子性,避免了競態條件(Race Condition)和記憶體漏失問題。
    • GIL 還可以簡化 CPython 直譯器的實現,使其更加簡單高效。
  2. GIL 的影響:

    • 由於 GIL 的存在,同一時刻只有一個執行緒可以執行 Python 位元組碼,其他執行緒被阻塞。這意味著多執行緒並不能充分利用多核處理器,無法實現真正的並行執行。
    • 對於 CPU 密集型任務,由於 GIL 的限制,多執行緒並不能提升效能。實際上,由於執行緒切換的開銷,可能導致多執行緒執行速度比單執行緒還要慢。
    • GIL 對於 I/O 密集型任務的影響相對較小,因為執行緒在進行 I/O 操作時會主動釋放 GIL,允許其他執行緒執行。因此,多執行緒在處理 I/O 操作時仍然可以提供一定的效能優勢。
  3. 解決 GIL 的方法:

    • 採用多程序:由於每個程序都有獨立的 Python 直譯器和 GIL,多程序可以充分利用多核處理器,實現並行執行。
    • 使用擴充套件模組:某些擴充套件模組,如 NumPy、Pandas 等,使用 C/C++ 編寫,可以釋放 GIL,允許多執行緒並行執行。
    • 使用多執行緒庫:一些第三方庫,如 multiprocessing 模組、concurrent.futures 模組等,提供了替代方案,使得在某些情況下可以繞過 GIL 的限制。

需要注意的是,GIL 只存在於 CPython 直譯器中,而其他實現(如 Jython、IronPython)可能沒有 GIL。此外,對於許多型別的應用程式,如 I/O 密集型、並行處理不頻繁的應用程式,GIL

的影響較小,可以繼續使用多執行緒來實現並行。然而,對於 CPU 密集型任務和需要充分利用多核處理器的應用程式,考慮使用多程序或其他解決方案來規避 GIL。