Python の動的な性質とダック タイピングのサポートは、その柔軟性が長い間賞賛されてきました。ただし、コードベースが大きくなり複雑になるにつれて、静的型チェックの利点がますます明らかになります。しかし、ダックタイピングの柔軟性と静的型チェックの安全性をどのように調和させることができるのでしょうか? Python のプロトコル クラスを入力します。
このチュートリアルでは次のことを学びます:
- ダックタイピングとは何か、そしてそれが Python でどのようにサポートされているか
- ダックタイピングの長所と短所
- 抽象基本クラス (ABC) が入力の問題を解決する方法
- プロトコルを使用して両方の長所を活かす方法: 静的型チェックによるダックタイピングの柔軟性
アヒルタイピングを理解する
ダックタイピングは、オブジェクトの型やクラスが、それが定義するメソッドほど重要ではないというプログラミングの概念です。これは、「アヒルのように見え、アヒルのように泳ぎ、アヒルのように鳴く場合、それはおそらくアヒルである」という考えに基づいています。
Python ではダックタイピングが完全にサポートされています。例えば:
クラスアヒル:
def quack(自分自身):
print("クワック!")
クラスの人:
def quack(自分自身):
print(「アヒルの真似してるよ!」)
def make_it_quack(thing): # 注: ここには型ヒントはありません
thing.quack()
アヒル = アヒル()
人 = 人()
make_it_quack(duck) # 出力: クワック!
make_it_quack(person) # 出力: アヒルの真似をしています!
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 パラメータには型ヒントがないことに注意してください。これはダックタイプのコードでは一般的ですが、大規模なコードベースでは問題を引き起こす可能性があります。
アヒルタイピングの長所と短所
ダックタイピングにはいくつかの利点があります:
柔軟性: 特定の型に縛られない、より柔軟なコードが可能になります。-
コードの再利用が簡単: 既存のクラスを変更せずに新しいコンテキストで使用できます。-
動作の強調: オブジェクトが何であるかではなく、オブジェクトが何ができるかに焦点を当てます。-
ただし、いくつかの欠点もあります:
明確さの欠如: オブジェクトがどのメソッドを実装する必要があるかが不明確になる可能性があります。-
実行時エラー: 型関連のエラーは実行時にのみ検出されます。-
IDE のサポートが少ない: IDE は正確なオートコンプリートとエラー チェックを提供するのに苦労しています。-
ABC ソリューション
これらの問題に対処する 1 つのアプローチは、抽象基本クラス (ABC) を使用することです。以下に例を示します:
from abc import ABC、abstractmethod
クラスクワッカー(ABC):
@abstractmethod
def quack(自分自身):
合格
クラスアヒル(クワッカー):
def quack(自分自身):
print("クワック!")
クラス人(クワッカー):
def quack(自分自身):
print(「アヒルの真似してるよ!」)
def make_it_quack(物:クワッカー):
thing.quack()
アヒル = アヒル()
人 = 人()
make_it_quack(アヒル)
make_it_quack(人)
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!
このアプローチは、より優れた型チェックとより明確なインターフェイスを提供しますが、次のような欠点があります。
継承が必要なので、階層が柔軟性に欠ける可能性があります。-
変更できない既存のクラスでは機能しません。-
これは Python の「ダック タイピング」哲学に反します。-
プロトコル: 両方の長所
Python 3.8 では Protocol クラスが導入され、継承を必要とせずにインターフェイスを定義できるようになりました。これを使用する方法は次のとおりです:
インポートプロトコルの入力から
クラスQuacker(プロトコル):
def quack(self):...
クラスアヒル:
def quack(自分自身):
print("クワック!")
クラスの人:
def quack(自分自身):
print(「アヒルの真似してるよ!」)
def make_it_quack(物:クワッカー):
thing.quack()
アヒル = アヒル()
人 = 人()
make_it_quack(アヒル)
make_it_quack(人)
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!
これを詳しく見てみましょう:
期待するインターフェイスを指定する Quacker プロトコルを定義します。-
Duck クラスと Person クラスは何も継承する必要がありません。-
make_it_quack で型ヒントを使用して、Quacker を期待していることを指定できます。-
このアプローチにはいくつかの利点があります:
静的型チェック: IDE と型チェッカーは実行前にエラーをキャッチできます。-
継承は必要ありません: 既存のクラスは、適切なメソッドがある限り機能します。-
明確なインターフェイス: プロトコルは、どのようなメソッドが期待されるかを明確に定義します。-
これは、ドメイン クラス (Circle、Rectangle) をフラットに保ちながら、プロトコルを必要に応じて複雑にする方法 (Shape) を示す、より複雑な例です:
インポート プロトコル、リストの入力から
クラスDrawable(プロトコル):
def 描画(自分自身): ...
サイズ変更可能なクラス(プロトコル):
def サイズ変更(self, 要素: float): ...
クラス形状(描画可能、サイズ変更可能、プロトコル):
合格
def process_shapes(shapes: リスト[形状]):
シェイプ内のシェイプの場合:
形状.描画()
シェイプ.リサイズ(2.0)
# 使用例
クラスサークル:
デフォルト描画(自分自身):
print("円を描く")
defsize(self、factor: float):
print(f"係数 {factor} による円のサイズ変更")
クラス長方形:
デフォルト描画(自分自身):
print("長方形を描く")
defsize(self、factor: float):
print(f"係数 {factor} による四角形のサイズ変更")
# これは描画メソッドとサイズ変更メソッドを持つ任意のクラスで機能します。
# 実際の型や継承に関係なく
形状: List[Shape] = [Circle(), Rectangle()]
process_shapes(形状)
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!
この例では、Circle と Rectangle は Shape または他のクラスから継承しません。これらは必要なメソッド (描画とサイズ変更) を実装するだけです。 process_shapes 関数は、Shape プロトコルのおかげで、これらのメソッドを持つ任意のオブジェクトを操作できます。
まとめ
Python のプロトコルは、ダックタイプのコードに静的型付けを導入する強力な方法を提供します。これらにより、継承を必要とせずに型システムでインターフェイスを指定できるようになり、静的型チェックの利点を加えながらダック タイピングの柔軟性を維持できます。
プロトコルを使用すると、次のことが可能になります:
コードの明確なインターフェイスを定義-
IDE を改善し (静的型チェック)、サポートし、エラーを早期にキャッチします-
ダックタイピングの柔軟性を維持する-
変更できないクラスの型チェックを利用します。-
Python のプロトコルと型ヒントについて詳しく知りたい場合は、型付けモジュールに関する Python の公式ドキュメントを確認するか、mypy などの高度な静的型チェック ツールを調べてください。
コーディングを楽しんでください。そして、あなたのアヒルが常にタイプ セーフで鳴きますように!
ニュースレターなど、私のコンテンツの詳細はこちらからご覧いただけます