Qui parlo principalmente di Python, ma suppongo che ciò valga probabilmente per la maggior parte delle lingue. Se ho un oggetto mutabile, è una cattiva idea far sì che un'operazione sul posto restituisca anche l'oggetto? Sembra che la maggior parte degli esempi modifichi l'oggetto e restituisca None
. Ad esempio, list.sort
.L'esecuzione di operazioni sul posto restituisce una cattiva idea all'oggetto?
risposta
Sì, è una cattiva idea. Il motivo è che se le operazioni sul posto e quelle non sul posto hanno un output apparentemente identico, i programmatori spesso mischiano operazioni sul posto e operazioni non sul posto (List.sort()
rispetto a sorted()
) e ciò risulta difficile - Rileva errori.
Le operazioni sul posto che si ripristinano possono consentire di eseguire il "concatenamento dei metodi", tuttavia, questa è una cattiva pratica perché è possibile seppellire le funzioni con effetti collaterali nel mezzo di una catena per errore.
Per evitare errori come questo, catene metodo dovrebbe avere un solo metodo con effetti collaterali, e che la funzione dovrebbe essere alla fine della catena. Le funzioni precedenti alla catena dovrebbero trasformare l'input senza effetti collaterali (ad esempio, navigare in un albero, tagliare una stringa, ecc.). Se le operazioni sul posto restituiscono loro stessi, un programmatore è obbligato a utilizzarlo accidentalmente al posto di una funzione alternativa che restituisce una copia e quindi non ha effetti collaterali (di nuovo, List.sort()
rispetto a sorted()
) che può causare un errore difficile da debug.
Questa è la ragione per cui le funzioni della libreria standard di Python restituiscono sempre una copia o restituiscono None
e modificano gli oggetti sul posto, ma non modificano mai gli oggetti sul posto e restituiscono anche loro stessi. Altre librerie Python come Django seguono questa pratica (vedi this very similar question su Django).
Concordato come regola generale, ma penso che ci siano eccezioni in alcuni casi specifici, che non sono così rari. Eg1: Quando la semantica del metodo è chiaramente una operazione sul posto, come '.empty()' di jQuery. Eg2: Quando l'API è così comunemente usata che tutti lo conoscono dal primo avvio e non ha alcuna versione che restituisce una copia, come '.append()' di jQuery –
Solo perché il nome del metodo è un verbo presente, non significa che le persone troveranno ovvio che l'operazione agisce sul posto. Mi ci è voluto molto tempo dopo aver imparato Python prima di ricordare sempre che 'list.sort' ha funzionato sul posto, anche se il nome suona come dovrebbe. – asmeurer
Non c'è ancora confusione nel terminare una catena con un'operazione sul posto? 'a.sort()' e 'a [: 2] .sort()' faranno cose completamente diverse (immagino che sarebbe diverso se tu usassi qualcosa come un 'array' numpy, che usa le viste). Forse il punto è che 'sort' che restituisce' None' ti protegge dal pensare che 'a [: 2] .sort()' faccia qualcosa di utile? – asmeurer
Suppongo che dipenda dal caso d'uso. Non vedo perché il ritorno di un oggetto da un'operazione sul posto sarebbe dannoso, a parte il fatto che forse non userete il risultato, ma questo non è davvero un problema se non siete super-meticolosi riguardo al funzionalismo puro. Mi piace il pattern di call-chain, come jQuery, quindi lo apprezzo quando le funzioni restituiscono l'oggetto su cui hanno agito, nel caso volessi usarlo ulteriormente.
Il ritorno dell'oggetto modificato dal metodo che lo ha modificato può avere alcuni vantaggi, ma non è consigliato in Python. Restituire self
dopo un'operazione di modifica consentirà di eseguire method chaining sull'oggetto, che è un modo conveniente di eseguire diversi metodi sullo stesso oggetto, è un linguaggio molto comune nella programmazione orientata agli oggetti. A sua volta, il concatenamento dei metodi consente un'implementazione diretta di fluent interfaces. Inoltre, consente di esprimere con maggiore facilità alcuni idiomi di programmazione funzionale.
per citarne alcuni esempi: in Python, la libreria Moka utilizza il metodo concatenamento. In Java, la classe StringBuilder
consente più invocazioni append()
sullo stesso oggetto. In JavaScript, JQuery utilizza ampiamente il concatenamento dei metodi. Smalltalk porta questa idea al livello successivo: per impostazione predefinita, tutti i metodi restituiscono self
se non diversamente specificato (quindi incoraggiando il concatenamento del metodo) - contrasto con Python, che restituisce None
per impostazione predefinita.
L'uso di questo idioma non è comune in Python, perché Python si attiene allo Command/Query Separation Principle, che stabilisce che "ogni metodo deve essere un comando che esegue un'azione o una query che restituisce dati al chiamante, ma non entrambi".
Tutto considerato, se sia una buona o una cattiva idea restituire self
alla fine è una questione di programmazione della cultura e della convenzione, mescolata con il gusto personale. Come accennato in precedenza, alcuni linguaggi di programmazione lo incoraggiano (come Smalltalk) mentre altri lo scoraggiano (come Python). Ogni punto di vista ha vantaggi e svantaggi, aperto a discussioni accese. Se sei un Pythonist del libro, meglio evitare di restituire self
: tieni presente che a volte può essere utile interrompere questa regola.
Grazie per la risposta eccellente, in particolare il collegamento al comando/Principio di separazione delle query, che aiuta Io allego un'etichetta per alcuni compromessi di progettazione che ho rimuginato ultimamente. – FMc
Penso che sia tutto basato sulla coerenza. Python è piuttosto coerente sui metodi su oggetti mutabili che sono operazioni sul posto. Finché si è coerenti, non dovrebbe esserci un problema con le operazioni sul posto che restituiscono un oggetto o un riferimento a un oggetto. –
Ma perché è così in primo luogo? – asmeurer
Non sono sicuro al 100%, ma la maggior parte delle volte non è necessario eseguire un'operazione sul posto per restituire un oggetto. Non stai creando un nuovo oggetto che deve essere assegnato, dopo tutto. Inoltre, ci sono analoghi a ciascuna operazione sul posto per rendere esplicito il fatto che stai restituendo qualcosa per eseguire ulteriori operazioni su di esso. (ad esempio 'list.sort' vs.' sorted (list) ',' list.reverse' vs. 'reverse (lista)') –