web-dev-qa-db-ja.com

このコードはダックタイピングに従っていますか?

ダックタイピングの原則は、あなたが持っているオブジェクトのタイプを気にする必要はないことを示しています-あなたが必要なアクションをあなたのobject。このため、isinstanceキーワードは推奨されません。 -- 定義

以下のスニペット(関数)group_tweets_by_stateでは、ダックタイピングの定義に従って、オブジェクトsetdefaultを追加することにより、アクション_Tweetがオブジェクトtweets_by_stateに対して実行されます。

def group_tweets_by_state(tweets):
    tweets_by_state = {}
    USA_states_center_position = {n: find_center(s) for n, s in us_states.items()}
    for Tweet in tweets:                     # tweets are list of dictionaries 
        if hassattr(Tweet, 'setdefault'):    # Tweet is a dictionary 
            state_name_key = find_closest_state(Tweet, USA_states_center_position)
            tweets_by_state.setdefault(state_name_key, []).append(Tweet)
    return tweets_by_state

私の理解は、関数hasattr(Tweet, 'setdefault')は型チェックTweetで、アヒルのタイピングスタイルで<class 'dict'>タイプであること、before追加。

私の理解は正しいですか?関数group_tweets_by_stateはダックタイピングに従っていますか?

5
overexchange

Tweetがディクショナリのすべてのメソッド/プロパティを提供することを保証しないため、Tweetがディクショナリであることを確認するhassattr(Tweet, 'setdefault')のようなテストは良いテストではありません。したがって、_Tweet.setdefault_が_find_closest_state_によって呼び出される唯一のメソッドではない限り(これはありそうもないことです)、このテストは十分に厳密ではありません。一方、isinstance(Tweet, dict)のようなテストは、他の辞書のような構造の使用を禁止しているため、厳密すぎます。これは、正確にダックタイピングの概念です。

あなたの例では、要件は実際にTweetがディクショナリではないこと、要件は_find_closest_state_がツイートを処理できることであり、実際のタイプとは関係なく、ツイートから呼び出されます。次のソリューションは、_find_closest_state_内のどのメソッドが使用されているかを正確に知る必要なく、一般的な方法でこれを処理します。

_def group_tweets_by_state(tweets):
    tweets_by_state = {}
    USA_states_center_position = {n: find_center(s) for n, s in us_states.items()}
    for Tweet in tweets:              # a  Tweet should behave like a dictionary
        try: 
            state_name_key = find_closest_state(Tweet, USA_states_center_position)
            tweets_by_state.setdefault(state_name_key, []).append(Tweet)
        except (AttributeError, TypeError):
            pass
    return tweets_by_state
_

コードはAttributeErrorをチェックします。これは、_find_closest_state_がTweetで提供されていないメソッドを呼び出したときに取得する例外であるためです。また、TypeErrorもチェックします。これは、辞書以外で_Tweet["abc"]_を呼び出したときに得られるものだからです。 _find_closest_state_の内部実装方法によっては、他の例外を追加する必要がある場合がありますが、人工的な制約を追加しないでください。

そして、それがダックタイピングを実際に適用する方法です-必要なアクションを実行できるかどうかをテストすることによってのみ、渡されたオブジェクトのタイプについて仮定を行わないことによって(ここでは、上記の例外の1つを取得せずに_find_closest_state_を呼び出します) )。

11
Doc Brown

まず、ダックタイピングが良いことだと誰もが認めているわけではないことは言うまでもありません。ダックタイピングは、コードのデバッグが困難なエラーを引き起こしがちですが、使用方法は非常に柔軟です。

ダックのタイピングはオブジェクトを受け取り、そのオブジェクトに対して実行したいことを行います。 .write()をオンにしたいオープンファイルオブジェクトが必要な場合は、それを実行します。オブジェクトにそのメソッドがないために例外がスローされる場合は、非ダックを渡しました。あなたのせい。 .write()があるために機能するまったく異なる種類のオブジェクトを渡した場合、それがダックタイピングの利点です。

そのため、コードは、コメントが言うようにtweetsがディクショナリのリストであると単純に想定し、それをチェックするために問題に行かない場合、「ダックタイピング」を使用します。

def group_tweets_by_state(tweets):
    tweets_by_state = {}
    USA_states_center_position = {n: find_center(s) for n, s in us_states.items()}
    for Tweet in tweets:                     # tweets are list of dictionaries 
        state_name_key = find_closest_state(Tweet, USA_states_center_position)
        tweets_by_state.setdefault(state_name_key, []).append(Tweet)
    return tweets_by_state
4
RemcoGerlich