Python 的動態特性和對鴨子類型的支持長期以來因其靈活性而受到稱讚。然而,隨著程式碼庫變得越來越大、越來越複雜,靜態類型檢查的好處變得越來越明顯。但是我們如何協調鴨子類型的靈活性和靜態類型檢查的安全性呢?進入Python的Protocol類別。
在本教程中,您將學習:
鴨子類型是一種程式設計概念,其中物件的類型或類別不如它定義的方法重要。它基於這樣的想法:「如果它看起來像鴨子,像鴨子一樣游泳,像鴨子一樣嘎嘎叫,那麼它可能就是一隻鴨子。」
在 Python 中,完全支援鴨子類型。例如:
class Duck: def quack(self): print("Quack!") class Person: def quack(self): print("I'm imitating a duck!") def make_it_quack(thing): # Note: No type hint here thing.quack() duck = Duck() person = Person() make_it_quack(duck) # Output: Quack! make_it_quack(person) # Output: I'm imitating a duck!
在這個例子中,make_it_quack 不關心事物的類型。它只關心這個東西有一個江湖方法。請注意,thing 參數沒有類型提示,這在鴨子類型程式碼中很常見,但可能會導致較大程式碼庫中出現問題。
鴨子打字有幾個優點:
但是,它也有一些缺點:
解決這些問題的一種方法是使用抽象基底類別(ABC)。這是一個例子:
from abc import ABC, abstractmethod class Quacker(ABC): @abstractmethod def quack(self): pass class Duck(Quacker): def quack(self): print("Quack!") class Person(Quacker): def quack(self): print("I'm imitating a duck!") def make_it_quack(thing: Quacker): thing.quack() duck = Duck() person = Person() make_it_quack(duck) make_it_quack(person)
雖然這種方法提供了更好的類型檢查和更清晰的接口,但它也有缺點:
Python 3.8引入了Protocol類,它允許我們定義介面而不需要繼承。以下是我們如何使用它:
from typing import Protocol class Quacker(Protocol): def quack(self):... class Duck: def quack(self): print("Quack!") class Person: def quack(self): print("I'm imitating a duck!") def make_it_quack(thing: Quacker): thing.quack() duck = Duck() person = Person() make_it_quack(duck) make_it_quack(person)
讓我們來分解一下:
這個方法為我們帶來了幾個好處:
這是一個更複雜的範例,展示了協議如何根據需要(形狀)變得複雜,同時保持域類(圓形、矩形)平坦:
from typing import Protocol, List class Drawable(Protocol): def draw(self): ... class Resizable(Protocol): def resize(self, factor: float): ... class Shape(Drawable, Resizable, Protocol): pass def process_shapes(shapes: List[Shape]): for shape in shapes: shape.draw() shape.resize(2.0) # Example usage class Circle: def draw(self): print("Drawing a circle") def resize(self, factor: float): print(f"Resizing circle by factor {factor}") class Rectangle: def draw(self): print("Drawing a rectangle") def resize(self, factor: float): print(f"Resizing rectangle by factor {factor}") # This works with any class that has draw and resize methods, # regardless of its actual type or inheritance shapes: List[Shape] = [Circle(), Rectangle()] process_shapes(shapes)
在此範例中,Circle 和 Rectangle 不繼承自 Shape 或任何其他類別。他們只是實現所需的方法(繪製和調整大小)。由於 Shape 協議,process_shapes 函數可以與任何具有這些方法的物件一起使用。
Python 中的協定提供了一種將靜態類型引入鴨子類型程式碼的強大方法。它們允許我們在類型系統中指定接口,而不需要繼承,保持鴨子類型的靈活性,同時添加靜態類型檢查的好處,
透過使用協議,您可以:
如果您想了解有關 Python 中的協議和類型提示的更多信息,請查看有關打字模組的官方 Python 文檔,或探索 mypy 等高級靜態類型檢查工具。
祝你編碼愉快,願你的鴨子總是因為類型安全而嘎嘎叫!
您可以在這裡找到更多我的內容,包括我的電子報
以上是鴨子類型遇到類型提示:在 Python 中使用協議的詳細內容。更多資訊請關注PHP中文網其他相關文章!