Python 開発者は Python のダック タイピングに精通していると思いますが、ウィキペディアでのダック タイピングの正確な定義は「動的タイピングのスタイル」です。このスタイルでは、オブジェクトの有効なセマンティクスは、特定のクラスからの継承や特定のインターフェイスの実装によってではなく、「現在のメソッドとプロパティのセット」によって決まります。そこでこの記事では、Python のダックタイピングについて説明します。
ダックタイピングの基本定義
まず第一に、Python はポリモーフィズムをサポートしていませんし、ポリモーフィズムをサポートする必要もありません。
以下はウィキペディアからのダックタイピングの説明です:
プログラミングにおいて、ダックタイピング(英語: duck testing )は、動的タイピングのスタイルの 1 つです。このスタイルでは、オブジェクトの有効なセマンティクスは、特定のクラスからの継承や特定のインターフェイスの実装によってではなく、現在のメソッドとプロパティのセットによって決まります。この概念の名前は、ジェームズ ウィットコム ライリーによって提案されたアヒル テストに由来しています。 「アヒル テスト」は次のように表現できます:
「アヒルのように歩き、アヒルのように泳ぎ、そしてアヒルのように鳴く鳥を見たとき。 「この鳥はアヒルと言えます。」
アヒルタイピングでは、オブジェクト自体の種類ではなく、それがどのように使用されるかに焦点が当てられます。たとえば、アヒル タイピングを使用しない言語では、アヒル型のオブジェクトを受け取り、その walk メソッドと bark メソッドを呼び出す関数を作成できます。ダック タイピングを使用する言語では、このような関数は任意の型のオブジェクトを受け入れ、その walk メソッドと call メソッドを呼び出すことができます。呼び出す必要があるメソッドが存在しない場合は、実行時エラーが発生します。正しい walk メソッドと call メソッドを持つオブジェクトはすべて関数で受け入れられるという事実が上記のステートメントにつながり、型を決定するこの方法の名前が付けられました。
ダックタイピングは、多くの場合、メソッドや関数のパラメーターの型をテストせず、代わりにドキュメント、明確なコード、および正しい使用法を保証するテストに依存することで恩恵を受けます。静的型付け言語から動的型付け言語に移行するユーザーは、多くの場合、静的 (実行時前) 型チェックを追加しようとします。これにより、ダック タイピングの利点とスケーラビリティが損なわれ、言語の動的な性質が制約されます。
Pythonでの具体的な実装
次のコードは単純なアヒル型です
class duck(): def walk(self): print('I walk like a duck') def swim(self): print('i swim like a duck') class person(): def walk(self): print('this one walk like a duck') def swim(self): print('this man swim like a duck')
アヒル型の場合、オブジェクト自体の型やクラスの継承は気にしません。しかし、このクラスがどのように使用されるか。次のコードを通じてこれらのクラスのメソッドを呼び出すことができます。
def watch_duck(animal): animal.walk() animal.swim() small_duck = duck() watch_duck(small_duck) output >> I walk like a duck i swim like a duck duck_like_man = person() watch_duck(duck_like_man) output >> this one walk like a duck this man swim like a duck class Lame_Foot_Duck(): def swim(self): print('i am lame but i can swim') lame_duck = Lame_Foot_Duck() watch_duck(lame_duck) output >> AttributeError: Lame_Foot_Duck instance has no attribute 'walk'
watch_duck
関数は、このクラスのオブジェクトを受け取り、オブジェクトの型をチェックせず、必要なメソッドがあれば、このオブジェクトのウォーキング メソッドとスイミング メソッドを直接呼び出します。存在しません。エラーを報告してください。 watch_duck
函数接收这个类的对象,然后并没有检查对象的类型,而是直接调用这个对象的走和游的方法,如果所需要的方法不存在就报错。
具体在python中鸭子类型的体现如下面的代码所示
class CollectionClass(): lists = [1,2,3,4] def __getitem__(self, index): return self.lists[index] iter_able_object = CollectionClass() class Another_iterAbleClass(): lists=[1,2,3,4] list_position = -1 def __iter__(self): return self def next(self): #还有更简单的实现,使用生成器或迭代器什么的:) self.list_position += 1 if self.list_position >3: raise StopIteration return self.lists[self.list_position] another_iterable_object=Another_iterAbleClass() print(iter_able_object[1]) print(iter_able_object[1:3]) output>> 2 [2, 3] another_iterable_object[2] output>> Traceback (most recent call last): File "/Users/steinliber/a.py", line 32, in <module> another_iterable_object[2] TypeError: 'Another_iterAbleClass' object does not support indexing print(next(another_iterable_object)) output>> 1 print(next(another_iterable_object)) output>> 2 print(next(iter_able_object)) output>> Traceback (most recent call last): File "/Users/steinliber/a.py", line 29, in <module> print(next(iter_able_object)) TypeError: IterAbleClass object is not an iterator
在python把上述代码的实现方法叫做protocol(协议),这些protocol可以看作是通知型的接口,它规定了调用方使用该功能要调用对象的哪些方法,被调用方要实现哪些方法才能完成这个功能。它和java中的接口区别在于java中的接口功能实现需要通过继承,继承的类必须实现接口中的所有的抽象方法,所以在Java中强调的是类型的概念,而python中的protocol
更多的是通知性的,一个函数规定要实现某个功能需要调用传入对象的哪些方法,所有实现这些方法的类就可以实现这个功能。
具体从上面两个类来说,第一个类实现了__getitem__
方法,那python的解释器就会把它当做一个collection,就可以在这个类的对象上使用切片,获取子项等方法,第二个类实现了__iter__
和next
方法,python就会认为它是一个iterator,就可以在这个类的对象上通过循环来获取各个子项。一个类可以实现它有能力实现的方法,并只能被用于在它有意义的情况下。
这两个类和上面的鸭子类相比较,其实用于切边的[](它其实调用的是python的slice
函数)和用于循环的iter()
就相当于watch_duck
函数,这些函数都接收任意类的对象,并调用实现功能所需要的对象中的方法来实现功能,若该函数中调用的方法对象里面不存在,就报错。
从上面可以看出,python鸭子类型的灵活性在于它关注的是这个所调用的对象是如何被使用的,而没有关注对象类型的本身是什么。所以在python中使用isinstance
来判断传入参数的类型是不提倡的,更pythonic
的方法是直接使用传入的参数,通过try,except
は型の概念を重視します。 >Python のプロトコル
関数は、特定の関数を実装するために受信オブジェクトのどのメソッドを呼び出す必要があるかを指定します。これらのメソッドを実装するすべてのクラスは、この関数を実装できます。 🎜🎜 上記の 2 つのクラスについて具体的に言えば、最初のクラスは __getitem__
メソッドを実装し、Python インタープリターはそれをコレクションとして扱い、このクラスのオブジェクトに対してスライスを使用できます。サブアイテムやその他のメソッドを実装する場合、2 番目のクラスは __iter__
メソッドと next
メソッドを実装します。Python はそれをイテレータとみなして、このクラスのオブジェクトを渡すことができます。それぞれの子供を手に入れるために。クラスは実装可能なメソッドを実装でき、意味のある状況でのみ使用できます。 🎜🎜上記のアヒルクラスと比較すると、これら 2 つのクラスは実際には [] エッジカットに使用され (実際には Python の slice
関数を呼び出します)、iter() はループに使用されます)
これらの関数は、任意のクラスのオブジェクトを受け取り、関数を実装するために必要なオブジェクト内のメソッドを呼び出します。存在する場合、エラーが報告されます。 🎜🎜上記からわかるように、Python ダックタイピングの柔軟性は、オブジェクトの型そのものではなく、呼び出されたオブジェクトがどのように使用されるかに焦点を当てていることです。したがって、Python で受信パラメータの型を決定するために isinstance
を使用することは推奨されません。より python
の方法は、try, 受信パラメータが要件を満たしていない場合の状況を処理するための
を除きます。オブジェクトの型を渡すのではなく、その機能を渡してオブジェクトを使用する必要があります。 🎜概要
上記は Python アヒル型の詳細な紹介です。この記事の内容はまだ非常に詳細なので、Python を学習する際に役立つことを願っています。伝えるためのメッセージ。
Python でのダックタイピングに関する詳細な記事については、PHP 中国語 Web サイトに注目してください。