Python 中不易懂的小知識點

大话性能發表於2024-04-10

下面是關於 Python 中容易讓人困惑或難以理解的一些小知識點的分模組解釋,每個模組都包含幾個相關的主題。

這些知識點需要對 Python 程式設計有一定的瞭解。

模組一:迭代器和生成器

迭代器(Iterator)和生成器(Generator)是 Python 中常用的用於處理可迭代物件的工具。

迭代器是一個物件,它實現了迭代協議,即透過__iter__()__next__()方法來使物件具有可迭代性。迭代器可以一個接一個地返回元素,直到沒有更多元素可返回為止。當你使用for迴圈來遍歷一個可迭代物件時,實際上是使用了該物件的迭代器。

下面是一個迭代器的示例:

class MyIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.data):
            raise StopIteration
        value = self.data[self.index]
        self.index += 1
        return value

my_list = [1, 2, 3, 4, 5]
my_iter = MyIterator(my_list)

for item in my_iter:
    print(item)

在這個示例中,我們定義了一個MyIterator類,它實現了迭代器協議。__init__方法用於初始化迭代器,__iter__方法返回迭代器本身,__next__方法用於返回下一個元素。

我們建立一個my_list列表,並將其傳遞給MyIterator類來建立一個迭代器物件my_iter。然後,我們使用for迴圈來遍歷迭代器,逐個列印出元素。

生成器是一種特殊的函式,它使用yield關鍵字來生成一個值,並且可以在迭代過程中暫停和恢復狀態。生成器函式當被呼叫時返回一個生成器物件,可以透過next()函式來逐步獲取生成器的值。在每次呼叫yield時,生成器會返回一個值,然後將其狀態暫停,直到下一次呼叫。

下面是一個生成器的示例:

def my_generator(data):
    for item in data:
        yield item
        
my_list = [1, 2, 3, 4, 5]
gen = my_generator(my_list)

for item in gen:
    print(item)

在這個示例中,我們定義了一個名為my_generator的生成器函式。在函式內部,使用for迴圈遍歷data列表,並透過yield關鍵字逐個生成值。

我們建立一個名為my_list的列表,並將其傳遞給my_generator生成器函式來建立一個生成器物件gen。然後,我們使用for迴圈來遍歷生成器並逐個列印出生成的值。

生成器的好處是它可以按需生成值,而不需要事先計算和儲存所有的值,這使得生成器在處理大量資料或無限序列時非常有效。

總結起來,迭代器和生成器是 Python 中方便處理可迭代物件的工具。迭代器使用__iter__()__next__()方法實現可迭代協議,而生成器使用yield關鍵字來暫停和恢復狀態,並按需生成值。兩者都能將處理可迭代物件的邏輯封裝成可複用的程式碼塊,提供更高效和靈活的迭代方式。

模組二:閉包和裝飾器

閉包是指在一個函式內部定義的函式,並且它可以訪問外部函式的區域性變數。閉包可以捕獲和保留外部函式的狀態,並可以在外部函式執行完成後繼續訪問和修改這些狀態。

def outer_function(x):
    def inner_function(y):
        return x + y
    return inner_function

closure = outer_function(10)
print(closure(5))  # 輸出 15

在上述例子中,outer_function 是一個外部函式,它接受一個引數 x,並定義了一個內部函式 inner_function。內部函式可以訪問外部函式的區域性變數 x。當呼叫 outer_function(10) 後,它返回了一個閉包 closure。我們可以使用 closure(5) 來呼叫閉包,並傳入引數 y。閉包會將外部函式的 x 加上 y 並返回結果。

裝飾器是一種用於裝飾其他函式的函式,它可以在不修改被裝飾函式原始碼的情況下,為被裝飾函式新增額外的功能。

def decorator_function(func):
    def wrapper(*args, **kwargs):
        print("函式執行前")
        result = func(*args, **kwargs)
        print("函式執行後")
        return result
    return wrapper

@decorator_function
def my_function():
    print("這是被裝飾的函式")

my_function()

在上面的例子中,我們定義了一個裝飾器函式 decorator_function,它接受一個函式 func 作為引數,並定義了一個內部函式 wrapper。在 wrapper 函式內部,我們可以在函式執行前和執行後進行一些額外的操作。在裝飾器函式中,我們透過呼叫被裝飾的函式,我們透過呼叫被裝飾的函式 func(*args, **kwargs) 來執行原始函式,並將其結果返回。

使用 @decorator_function 語法,我們將裝飾器應用於 my_function,使得 my_function 成為被裝飾的函式。當呼叫 my_function 時,裝飾器會自動新增額外的功能,即在函式執行前和執行後列印相關資訊。

閉包和裝飾器是 Python 中非常有用的程式設計概念,它們可以幫助我們編寫更加靈活和可複用的程式碼。

模組三:元類和超程式設計

元類(Metaclass)是用於建立類的類。在 Python 中,type 是預設的元類,也是所有類的元類。元類允許我們在執行時建立和修改類物件的行為。

超程式設計(Metaprogramming)是一種程式設計技術,透過編寫程式碼來操作和建立其他程式碼。超程式設計在 Python 中主要透過元類來實現。

元類和超程式設計是 Python 中高階的概念,它們允許我們在執行時建立和操作類。下面是一個使用元類和超程式設計的示例:

class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        # 動態為所有屬性新增字首 "my_"
        prefixed_attrs = {}
        for attr_name, attr_value in attrs.items():
            if not attr_name.startswith("__"):
                prefixed_attrs["my_" + attr_name] = attr_value

        # 建立新的類物件
        new_cls = super().__new__(cls, name, bases, prefixed_attrs)
        return new_cls

class MyClass(metaclass=MyMeta):
    my_variable = 10

    def my_method(self):
        return "Hello, World!"

obj = MyClass()
print(obj.my_variable)  # 輸出 10
print(obj.my_method())  # 輸出 "Hello, World!"

在上述示例中,我們定義了一個元類 MyMeta。元類是一個特殊的類,用於建立其他類。我們在元類中過載了 __new__ 方法,它在建立新的類物件時被呼叫。

__new__ 方法中,我們遍歷所定義的類的所有屬性,並將它們的名稱前加上 "my_" 字首,然後將新的屬性新增到新建立的類物件中。

MyClass 是使用 MyMeta 元類建立的類。當我們定義類時,透過 metaclass 引數將元類應用於該類。在這個例子中,我們定義了一個例項變數 my_variable = 10 和一個例項方法 my_method。由於使用了元類,這些屬性和方法會被自動加上 "my_" 字首,並在建立類物件時進行修改。

最後,我們建立了 MyClass 的一個例項 obj,並使用 obj.my_variableobj.my_method() 來訪問修改後的屬性和方法。

這個例子展示瞭如何使用元類和超程式設計來動態地修改類物件的屬性和方法。超程式設計是一種強大的功能,它允許我們在執行時對類進行定製和修改,為我們提供了更大的靈活性和控制力。

模組四:記憶體管理和垃圾回收

在 Python 中,記憶體管理和垃圾回收是自動處理的,開發者無需明確地進行記憶體分配和釋放操作。Python 的垃圾回收機制會自動檢測並回收不再使用的物件。

下面是一個示例,演示了 Python 的垃圾回收機制:

import sys

class MyClass:
    def __init__(self, name):
        self.name = name

def create_objects():
    # 建立兩個物件,並讓變數a和b引用它們
    a = MyClass("Object A")
    b = MyClass("Object B")

    # 將a變數設定為None,不再引用物件
    a = None

    # 手動呼叫垃圾回收
    gc.collect()

    # 輸出當前系統中的物件數目
    print(sys.getrefcount(b))

create_objects()

在這個示例中,我們定義了一個MyClass類,它有一個例項變數namecreate_objects函式會建立兩個MyClass物件,並將它們分別賦值給變數ab。接著,我們將a變數設定為 None,解除對物件的引用。

在呼叫gc.collect()手動觸發垃圾回收之後,我們使用sys.getrefcount()函式獲取變數b的引用計數。引用計數表示有多少個變數引用了同一個物件。在這裡,由於我們取消了a對物件的引用,只有變數b引用了物件。因此,sys.getrefcount(b)返回的值為 2(在函式內部和函式外部各有一個引用)。

這個示例展示了 Python 中的垃圾回收機制的工作方式。當物件不再被引用時,垃圾回收機制會自動將其回收,釋放物件所佔用的記憶體空間。

需要注意的是,Python 的垃圾回收機制主要基於引用計數(reference counting)和迴圈垃圾收集(cycle detection and garbage collection)。引用計數用於跟蹤物件的引用數量,當引用計數為 0 時,物件被認為是不再被引用的,可以被回收。迴圈垃圾收集用於檢測並處理由迴圈引用引起的無法訪問的物件,透過標記 - 清除演算法進行回收。

總之,在 Python 中,你無需手動管理記憶體,垃圾回收機制會自動處理不再使用的物件。

這些分模組的知識點涵蓋了 Python 中一些較為深入和複雜的主題。希望它們能夠幫助你更好地理解和應用 Python 程式語言。

更多內容可以學習個人主頁專欄內容《測試工程師 Python 工具開發實戰》書籍《大話效能測試 JMeter 實戰》書籍

相關文章