Come spezzare una linea di metodi concatenati in Python?


139

Ho una riga del seguente codice (non dare la colpa alle convenzioni di denominazione, non sono mie):

subkeyword = Session.query(
    Subkeyword.subkeyword_id, Subkeyword.subkeyword_word
).filter_by(
    subkeyword_company_id=self.e_company_id
).filter_by(
    subkeyword_word=subkeyword_word
).filter_by(
    subkeyword_active=True
).one()

Non mi piace come appare (non troppo leggibile) ma non ho idea migliore di limitare le righe a 79 caratteri in questa situazione. Esiste un modo migliore per romperlo (preferibilmente senza barre rovesciate)?

Risposte:


257

È possibile utilizzare parentesi aggiuntiva:

subkeyword = (
        Session.query(Subkeyword.subkeyword_id, Subkeyword.subkeyword_word)
        .filter_by(subkeyword_company_id=self.e_company_id)
        .filter_by(subkeyword_word=subkeyword_word)
        .filter_by(subkeyword_active=True)
        .one()
    )

Mi piace anche di più. Non aggiunge altro codice ed è senza barre rovesciate.
Juliusz Gonera,

22
Non sono sicuro di cosa giustifichi il rientro aggiuntivo qui; Penso che questa soluzione legga altrettanto bene con le linee sospese rientrate una sola volta e il paren finale non è affatto.
Carl Meyer,

4
A mio avviso, la doppia rientranza è utile qui perché è visivamente distinta da un normale blocco rientrato. Quando è circondato da altro codice, ciò rende più ovvio che si tratta di una singola riga chiusa.
qc

1
La migliore risposta, in termini di utilizzo delle parentesi. Come menzionato in un commento di Shanimal in un'altra risposta, l'uso della continuazione di riga implicita tra parentesi è in realtà il PEP 8 preferito rispetto al carattere di continuazione ``
kevlarr,

Preferisco le barre rovesciate. La parentesi non è un suggerimento per tutte le situazioni. Ad esempio, non funziona con l'operatore di assegnazione. Immagina di voler rompere le linee in questa catena:foo.set_default('bar', {}).set_default('spam', {}).set_default('eggs', {})['lol'] = 'yeah'
loutre

56

Questo è un caso in cui si preferisce un carattere di continuazione della linea per aprire le parentesi. La necessità di questo stile diventa più evidente man mano che i nomi dei metodi si allungano e i metodi iniziano a prendere argomenti:

subkeyword = Session.query(Subkeyword.subkeyword_id, Subkeyword.subkeyword_word) \
                    .filter_by(subkeyword_company_id=self.e_company_id)          \
                    .filter_by(subkeyword_word=subkeyword_word)                  \
                    .filter_by(subkeyword_active=True)                           \
                    .one()

PEP 8 intende essere interpretato con una certa dose di buon senso e un occhio sia per il pratico che per il bello. Violare felicemente qualsiasi linea guida PEP 8 che si traduca in codice brutto o difficile da leggere.

Detto questo, se ti trovi spesso in contrasto con PEP 8, potrebbe essere un segno che ci sono problemi di leggibilità che trascendono la tua scelta di spazi bianchi :-)


2
+1 su barre rovesciate e allineamento dei filtri concatenati in questo caso particolare. Questa situazione si presenta anche in Django ed è più leggibile in questo modo - ma in qualsiasi altra situazione mi sembra che le frasi tra parentesi siano superiori (non soffrire del problema "c'è spazio dopo la mia barra rovesciata?"). Detto questo, la parentesi della frase può essere utilizzata per ottenere lo stesso effetto, ma ti mette in modalità di lettura Lisp nel mezzo della lettura di Python, che trovo stonante.
zxq9

11
Non vedo come questa soluzione sia più in grado di far fronte "man mano che i nomi dei metodi si allungano e quando i metodi iniziano a prendere argomenti" rispetto a "avvolgere in parentesi esterne" o "interruzione di riga dopo ogni parentesi aperta e prima di ogni parentesi chiusa" soluzioni. In effetti è peggio nel gestirlo, poiché (almeno come mostrato qui) richiede un rientro molto più profondo per ogni linea sospesa.
Carl Meyer,

1
Troppo rientro per le chiamate del filtro. Una scheda o 4 spazi sarebbero stati sufficienti qui. Anche l'allineamento di `` ... Quanti secondi hai tenuto premuto il tasto spazio? Generalmente sono contrario a tutti, il che richiede che tu martelli quella chiave spaziale come se non ci fosse un domani.
Zelphir Kaltstahl,

2
fwiw, PEP8 recita "Il modo preferito di avvolgere le linee lunghe è usare la continuazione di linea implicita di Python tra parentesi, parentesi quadre e parentesi graffe. Le linee lunghe possono essere spezzate su più linee avvolgendo espressioni tra parentesi. Queste dovrebbero essere usate preferibilmente usando una barra rovesciata per la continuazione della linea ". - Python.org Continua a discutere quando le barre rovesciate possono essere appropriate
Shanimal,

Ottimo riferimento a PEP8! Un problema fastidioso qui con l'allineamento di tutte le .filterchiamate è che se si subkeywordpassa a sub_keyword, ora è necessario correggere il rientro di ogni singola linea solo perché è stato modificato il nome della variabile. Non va bene quando lo stile ostacola effettivamente la manutenibilità ...
kevlarr,

15

La mia scelta personale sarebbe:

subkeyword = Session.query (
    Subkeyword.subkeyword_id,
    Subkeyword.subkeyword_word,
) .Filter_by (
    subkeyword_company_id = self.e_company_id,
    subkeyword_word = subkeyword_word,
    subkeyword_active = True,
).uno()

1
Sono d'accordo se ci sono diversi parametri passati ma sembra brutto quando 0 o 1 parametri sono comuni. Ad esempio: gist.github.com/andybak/b23b6ad9a68c7e1b794d
Andy Baker

1
Sì, quello stile ha casi degeneri (come qualsiasi stile). Non vorrei spezzare tutte le parentesi aperte. Niente di tutto ciò mi rende
pkoch

12

Basta memorizzare il risultato / oggetto intermedio e invocare il metodo successivo su di esso, ad es

q = Session.query(Subkeyword.subkeyword_id, Subkeyword.subkeyword_word)
q = q.filter_by(subkeyword_company_id=self.e_company_id)
q = q.filter_by(subkeyword_word=subkeyword_word)
q = q.filter_by(subkeyword_active=True)
subkeyword = q.one()

10
Funziona bene per qualcosa come una query ma, come schema generale, non ne sono così sicuro. Ad esempio, quando si concatena in Beautiful Soup come team_members = soup.find(class_='section team').find_all('ul').find_all('li'), il valore restituito da ogni .find(...)chiamata non corrisponde ancora al significato di team_members.
Taylor Edmiston,

1
@TaylorEdmiston Naturalmente puoi avere nomi diversi per i risultati parziali. Qualcosa di simile section = soup.find(class_='section team')e team_members = section.find_all('ul').find_all('li').
Jeyekomon l'

4

Secondo Python Language Reference
È possibile utilizzare una barra rovesciata.
O semplicemente romperlo. Se una parentesi non è accoppiata, Python non la tratterà come una linea. E in tale circostanza, il rientro delle seguenti righe non ha importanza.


4

È una soluzione un po 'diversa rispetto a quella fornita da altri, ma è una delle mie preferite poiché a volte porta a un'elegante metaprogramma.

base = [Subkeyword.subkeyword_id, Subkeyword_word]
search = {
    'subkeyword_company_id':self.e_company_id,
    'subkeyword_word':subkeyword_word,
    'subkeyword_active':True,
    }
subkeyword = Session.query(*base).filter_by(**search).one()

Questa è una bella tecnica per costruire ricerche. Passa attraverso un elenco di condizionali da estrarre dal tuo modulo di query complesso (o deduzioni basate su stringhe su ciò che l'utente sta cercando), quindi fai esplodere il dizionario nel filtro.


1

Sembra che tu usi SQLAlchemy, se è vero, il sqlalchemy.orm.query.Query.filter_by()metodo accetta più argomenti di parole chiave, quindi potresti scrivere come:

subkeyword = Session.query(Subkeyword.subkeyword_id,
                           Subkeyword.subkeyword_word) \
                    .filter_by(subkeyword_company_id=self.e_company_id,
                               subkeyword_word=subkeyword_word,
                               subkeyword_active=True) \
                    .one()

Ma sarebbe meglio:

subkeyword = Session.query(Subkeyword.subkeyword_id,
                           Subkeyword.subkeyword_word)
subkeyword = subkeyword.filter_by(subkeyword_company_id=self.e_company_id,
                                  subkeyword_word=subkeyword_word,
                                  subkeyword_active=True)
subkeuword = subkeyword.one()

+1 per il suggerimento SQLAlchemy filter_by (). Va bene per questo esempio, ma uso spesso filter () invece che accetta solo 1 condizione.
Juliusz Gonera,

1

Mi piace indentare gli argomenti di due blocchi e l'affermazione di un blocco, come questi:

for image_pathname in image_directory.iterdir():
    image = cv2.imread(str(image_pathname))
    input_image = np.resize(
            image, (height, width, 3)
        ).transpose((2,0,1)).reshape(1, 3, height, width)
    net.forward_all(data=input_image)
    segmentation_index = net.blobs[
            'argmax'
        ].data.squeeze().transpose(1,2,0).astype(np.uint8)
    segmentation = np.empty(segmentation_index.shape, dtype=np.uint8)
    cv2.LUT(segmentation_index, label_colours, segmentation)
    prediction_pathname = prediction_directory / image_pathname.name
    cv2.imwrite(str(prediction_pathname), segmentation)
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.