Questo codice segue la digitazione anatra?

5

The principle of duck typing says that you shouldn't care what type of object you have - just whether or not you can do the required action with your object. For this reason the isinstance keyword is frowned upon. - -Definition

Nel sotto snippet (funzione) group_tweets_by_state , in seguito alla definizione della digitazione anatra, l'azione setdefault viene eseguita sull'oggetto tweets_by_state aggiungendo l'oggetto tweet

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

La mia comprensione è che la funzione hasattr(tweet, 'setdefault') è il tipo di controllo tweet per essere di <class 'dict'> tipo nello stile di digitazione anatra, prima append.

La mia comprensione è corretta? La funzione group_tweets_by_state segue la digitazione anatra?

    
posta overexchange 26.06.2015 - 11:53
fonte

2 risposte

11

Un test come hassattr(tweet, 'setdefault') per assicurarsi che tweet sia un dizionario non è buono, poiché ovviamente non garantisce che tweet fornisca tutti i metodi / le proprietà di un dizionario. Quindi, dato che tweet.setdefault non è l'unico metodo chiamato da find_closest_state (che a mio avviso è improbabile), questo test non è abbastanza rigoroso. D'altra parte, un test come isinstance(tweet, dict) è troppo severo, perché impedisce l'uso di altre strutture simili a un dizionario, che è esattamente l'idea della digitazione anatra.

Nel tuo esempio il requisito non è proprio che tweet sia un dizionario, il requisito è che find_closest_state possa elaborare il tweet, indipendentemente dai metodi che chiama da un tweet, indipendentemente dal tipo reale. La seguente soluzione gestirà questo in modo generico, senza la necessità di sapere esattamente quali metodi all'interno di find_closest_state vengono utilizzati:

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

Il codice verifica un AttributeError perché questa è l'eccezione che ottieni quando find_closest_state chiama un metodo non fornito da tweet . Controlla anche un TypeError , perché è quello che ottieni quando chiami tweet["abc"] su un non dizionario. Potrebbe essere necessario aggiungere alcune altre eccezioni, a seconda di come find_closest_state è implementato internamente, ma non dovresti aggiungere alcun vincolo artificiale.

Ed è così che dovrebbe essere applicata la digitazione anatra - non facendo assunzioni sul tipo di oggetto passato, solo testando se è possibile o meno eseguire l'azione richiesta (qui: chiama find_closest_state senza ottenere uno dei precedenti eccezioni).

    
risposta data 26.06.2015 - 13:32
fonte
4

In primo luogo, voglio dire che di gran lunga non tutti sono d'accordo sul fatto che la digitazione delle anatre è una buona cosa, per non parlare del fatto che è una specie di principio sacro da seguire. Spesso la digitazione anatra porta a un codice soggetto a errori, difficile da eseguire il debug, sebbene possa essere molto flessibile nel modo in cui è possibile utilizzarlo.

La tipizzazione di anatra prende solo un oggetto e fa con l'oggetto ciò che vuole fare. se si aspetta un oggetto file aperto che desidera chiamare .write() , allora lo fa. Se ciò genera un'eccezione perché l'oggetto non ha quel metodo, allora hai passato in una non-anatra. Colpa tua. Se passi in un tipo di oggetto completamente diverso che funziona anche perché ha un .write() , questo è il vantaggio della digitazione anatra.

Quindi il tuo codice userebbe "duck typing" se si presuppone semplicemente che tweets sia una lista di dizionari (come dice il commento), e non ha alcun problema a controllarlo.

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
    
risposta data 26.06.2015 - 12:16
fonte