perché sono necessarie funzioni vuote


9

Ho iniziato ad imparare Python e mi chiedo perché in un linguaggio di programmazione siano necessarie funzioni vuote

ad es. in Python:

def empty_func():
    pass

anche negli script di shell sono disponibili funzioni vuote.

Comprensioni e domande:

  1. Perché il linguaggio di programmazione aveva bisogno di funzioni vuote? È solo per giocare con il linguaggio di programmazione o nient'altro che conta davvero?

  2. Se questo ha uno scopo, qualcuno può descrivere il caso d'uso o dare un esempio reale dell'uso di funzioni vuote?

  3. Oppure esiste una tradizione di linguaggi di programmazione che consente funzioni vuote?


EDIT (cose che ho ottenuto leggendo le tue risposte):

  • Per disegnare algoritmi o con funzioni astratte
  • Per l'invio di moduli senza alcuna azione deve essere eseguita
  • Segnaposto per alcune operazioni obbligatorie

1
possono essere utilizzati come STUB se l'esecuzione del programma prevede di trovarli / utilizzarli, tuttavia non si desidera modificare nulla tramite essi.
G.Rassovsky,

Risposte:


5

Nelle lingue di shell della famiglia Bourne, il :comando, che non fa nulla, viene generalmente utilizzato in due situazioni:

  • Segnaposto per quando qualcosa si aspetta un comando obbligatorio, ad es

    while some_condtion
    do :
    done

    poiché dorichiede almeno un comando.

  • Eliminare gli argomenti, ma eseguire effetti collaterali all'interno dell'elenco degli argomenti, ad es

    : ${myvar=foo}

Sono sicuro che ci sono probabilmente altre applicazioni che gli esperti di shell saprebbero :)

In Python (e in altre lingue) è usato meno frequentemente. Può fungere da argomento per una funzione di ordine superiore quando in realtà non si desidera fare nulla. Ad esempio, supponiamo di avere una funzione che invia un modulo e consente di chiamare una funzione in modo asincrono al termine dell'invio:

def submit(callback=empty_func):
    ...

In questo modo, se callbacknon viene fornito, l'invio continuerà comunque, ma non verranno eseguite ulteriori azioni. Se avessi usato Nonecome valore predefinito, avresti dovuto verificare esplicitamente se il callback fosse None, aggiungendo disordine al codice.


1

Dipende da dove ti trovi nel ciclo di sviluppo, ma a volte quando schizzi un algoritmo, vuoi fare astrazione su blocchi complessi senza implementarli immediatamente.

def full_algo():
  init_stuff()
  process_stuff()
  ...

Sai come init_stufffunzionerà, è piuttosto semplice nella tua testa ma non ne hai davvero bisogno subito, quindi lo dichiari come una funzione vuota. Permetterà al tuo codice di essere compilato ed eseguito senza preoccuparsi dei dettagli cruenti.

Un altro uso per le applicazioni rilasciate è quando si utilizza l'ereditarietà. Supponiamo di avere una grande classe che definisce il comportamento del codice specifico della piattaforma. Potresti finire con una logica simile a questa:

init_filesystem();
access_files();
release_filesystem();

Questo codice funzionerà su molte piattaforme, ma alcune piattaforme potrebbero non richiedere l'inizializzazione del filesystem. Quindi la tua eredità sarà simile a questa (virtuale con = 0 in C ++ significa solo che le classi derivate DEVONO implementare quei metodi):

class FileSystem{
  virtual void init_filesystem() = 0;
  virtual void access_files() = 0;
  virtual void release_filesystem() = 0;
};

Quindi un'implementazione particolare di questa classe (interfaccia) potrebbe non fare nulla per alcuni di questi metodi. In alternativa, la classe base potrebbe dichiarare metodi vuoti per init / release invece di dichiararli virtuali.

Finalmente (e vergognosamente), a volte mantieni un'applicazione molto vecchia. Temi che l'eliminazione dei metodi rompa le cose. Ciò accade quando si ha un'eredità complessa che non è stata compresa correttamente o quando si hanno molti puntatori a funzioni (callback). Basta eliminare il codice al loro interno in modo che vengano chiamati comunque senza interrompere nulla.


0

Rufflewind ha fatto un buon lavoro coprendo quando potresti usare una funzione vuota in un programma completato. Nella mia esperienza, tende ad essere usato più spesso in programmi incompiuti, quindi puoi pianificare ciò che stai per scrivere e farlo compilare fino a quando non riesci a implementarlo effettivamente. In altre parole, di solito è solo un segnaposto.

È necessario nel caso di Python perché usa l'indentazione per contrassegnare i blocchi, a differenza dei linguaggi di tipo C che usano le parentesi graffe {}. Questo significa che se non lo avessi fatto pass, il parser non avrebbe saputo dire se intendevi lasciarlo vuoto o se lo avessi dimenticato. Includere passrende il parser molto più semplice e ti dà una parola comoda da cercare quando cerchi blocchi non implementati.


1
Per il codice incompiuto, il lancio NotImplementedErrorè una soluzione più adeguata poiché "Gli errori non devono mai passare silenziosamente" e chiamare una funzione non implementata in un programma live è un errore.
ivan_pozdeev,

Dipende. Per il codice spedito, sì. Ma se si tratta di un codice che ti aspetti di implementare in un'ora e lo stai lasciando senza applicazione mentre lavori ai test unitari, semplicemente non lasciare alcuna implementazione potrebbe essere un'opzione migliore. Spesso il mio flusso di lavoro è 1) scrivere il metodo stub con pass 2) scrivere unit test che verifica il valore di ritorno 3) verificare il test fallisce (poiché il metodo restituisce undefined) 4) implementare il metodo 5) verificare il test ora passa
Gort the Robot

0

Sebbene non sia qualcosa che ti aspetteresti di fare in Python, ci sono volte nel mondo dei driver di dispositivo in cui devi fornire una funzione vuota, per gestire un evento che (a) sai che non accadrà o ( b) non preoccuparti, anche se lo fa.

Lo vedi anche in C con i callback. Occasionalmente, vedrai il codice che ASSUME di aver fornito un callback e prova a chiamarlo, ignorando il rischio di un puntatore null. Tutto quello che puoi fare in quel caso è fornire una routine di richiamata vuota. (Sì, ho in mente un fornitore specifico.)


Ho usato passmolto come segnaposto durante la scrittura di codice, in particolare nelle classi, quando voglio qualcosa di eseguibile prima che tutto sia completo. È davvero l'equivalente di {}Python, che Python non può fare in quanto non usa parentesi graffe. In questo senso, tutte le lingue di cui sono a conoscenza lo consentono, è solo che solo una coppia come pythonrichiede una parola chiave. Anche nei giorni dell'assemblea che abbiamo avuto NOP.
Gort the Robot

@StevenBurnap, di solito quando faccio qualcosa del genere, includo l'equivalente locale di printf (">>> routine XXX chiamato \ n");
John R. Strohm,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.