Wie kann man eine Reihe verketteter Methoden in Python durchbrechen?

137

Ich habe eine Zeile mit dem folgenden Code (keine Schuld an Namenskonventionen, sie gehören nicht mir):

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()

Ich mag es nicht, wie es aussieht (nicht zu lesbar), aber ich habe keine bessere Idee, Zeilen in dieser Situation auf 79 Zeichen zu beschränken. Gibt es eine bessere Möglichkeit, es zu brechen (vorzugsweise ohne Backslashes)?

Juliusz Gonera
quelle

Antworten:

255

Sie könnten zusätzliche Klammern verwenden:

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()
    )
etw
quelle
Mir gefällt es auch am besten. Fügt keinen weiteren Code hinzu und ist ohne Backslashes.
Juliusz Gonera
22
Ich bin mir nicht sicher, was die zusätzliche Einrückung hier rechtfertigt. Ich denke, diese Lösung liest sich genauso gut, wenn die hängenden Linien nur einmal eingerückt sind und der nachfolgende Paren überhaupt nicht.
Carl Meyer
4
Meiner Meinung nach ist eine doppelte Einrückung hier nützlich, da sie sich optisch von einem normalen eingerückten Block unterscheidet. Wenn es von anderem Code umgeben ist, wird dadurch deutlicher, dass es sich um eine umbrochene einzelne Zeile handelt.
etw
1
Beste Antwort in Bezug auf die Verwendung von Parens. Wie in einem Kommentar von Shanimal in einer anderen Antwort erwähnt, ist die Verwendung der impliziten Zeilenfortsetzung über Klammern PEP 8 gegenüber dem Fortsetzungszeichen ``
kevlarr
Ich bevorzuge Backslashes. Klammern sind kein Hinweis für alle Situationen. Beispielsweise funktioniert es nicht mit dem Zuweisungsoperator. Stellen Sie sich vor, Sie möchten Linien in dieser Kette brechen:foo.set_default('bar', {}).set_default('spam', {}).set_default('eggs', {})['lol'] = 'yeah'
Loutre
56

Dies ist ein Fall, in dem ein Zeilenfortsetzungszeichen dem Öffnen von Klammern vorgezogen wird. Die Notwendigkeit dieses Stils wird offensichtlicher, wenn Methodennamen länger werden und Methoden Argumente annehmen:

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 soll mit einem Maß an gesundem Menschenverstand und einem Auge für das Praktische und das Schöne interpretiert werden. Verstößt glücklich gegen eine PEP 8-Richtlinie, die zu hässlichem oder schwer lesbarem Code führt.

Wenn Sie jedoch häufig mit PEP 8 nicht einverstanden sind, kann dies ein Zeichen dafür sein, dass es Lesbarkeitsprobleme gibt, die über Ihre Wahl des Leerzeichens hinausgehen :-)

Raymond Hettinger
quelle
2
+1 bei Backslashes und Ausrichten der verketteten Filter in diesem speziellen Fall. Diese Situation tritt auch in Django auf und ist auf diese Weise am besten lesbar - aber in jeder anderen Situation bin ich der Meinung, dass Phrasen in Klammern überlegen sind (leiden Sie nicht unter dem Problem "Gibt es nach meinem Backslash Leerzeichen?"). Das heißt, die Klammerung der Phrase kann verwendet werden, um den gleichen Effekt zu erzielen - aber Sie befinden sich mitten im Lesen von Python im Lisp-Lesemodus, was mich irritiert.
zxq9
11
Ich sehe nicht, wie diese Lösung besser damit umgehen kann, "wenn Methodennamen länger werden und Methoden Argumente annehmen", als entweder "Wrap in Outer Parens" oder "Zeilenumbruch nach jedem offenen Paren und vor jedem geschlossenen Paren". Lösungen. Tatsächlich ist es bei der Handhabung schlechter, da (zumindest wie hier gezeigt) für jede hängende Leine ein viel tieferer Einzug erforderlich ist.
Carl Meyer
1
Viel zu viel Einzug für die Filteraufrufe. Ein Tab oder 4 Leerzeichen hätten hier gereicht. Auch Ausrichtung des `` ... Wie viele Sekunden haben Sie diese Leertaste gedrückt? Im Allgemeinen bin ich gegen alle Möglichkeiten, die erfordern, dass Sie diesen Leertaste hämmern, als gäbe es kein Morgen.
Zelphir Kaltstahl
2
fwiw, PEP8 lautet: "Die bevorzugte Methode zum Umschließen langer Zeilen ist die Verwendung der impliziten Zeilenfortsetzung von Python in Klammern, Klammern und geschweiften Klammern. Lange Zeilen können durch Umschließen von Ausdrücken in Klammern über mehrere Zeilen unterbrochen werden. Diese sollten der Verwendung eines Backslashs vorgezogen werden für die Fortsetzung der Leitung. " - Python.org Es wird weiter diskutiert, wann Backslashes angebracht sein könnten
Shanimal
Toller Hinweis auf PEP8! Ein ärgerliches Problem bei der Ausrichtung aller .filterAufrufe ist, dass Sie bei einem Wechsel subkeywordzu sub_keywordjetzt die Einrückung jeder einzelnen Zeile korrigieren müssen , nur weil Sie den Variablennamen geändert haben. Nicht gut, wenn Stil tatsächlich die Wartbarkeit behindert ...
Kevlarr
15

Meine persönliche Wahl wäre:

subkeyword = Session.query (
    Subkeyword.subkeyword_id,
    Subkeyword.subkeyword_word,
).Filtern nach(
    subkeyword_company_id = self.e_company_id,
    subkeyword_word = subkeyword_word,
    subkeyword_active = True,
).einer()
pkoch
quelle
1
Ich bin damit einverstanden, wenn mehrere Parameter übergeben werden, aber es sieht hässlich aus, wenn 0 oder 1 Parameter gemeinsam sind. Zum Beispiel: gist.github.com/andybak/b23b6ad9a68c7e1b794d
Andy Baker
1
Ja, dieser Stil hat entartete Fälle (wie jeder Stil). Ich würde nicht alle offenen Parens brechen. Nichts davon macht mich glücklich, aber hier sind einige Fälle: gist.github.com/pkoch/8098c76614765750f769
pkoch
12

Speichern Sie einfach das Zwischenergebnis / Objekt und rufen Sie die nächste Methode auf, z

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()
Ivo van der Wijk
quelle
10
Dies funktioniert gut für so etwas wie eine Abfrage, aber als allgemeines Muster bin ich mir nicht so sicher. Wenn Sie beispielsweise in Beautiful Soup verketten team_members = soup.find(class_='section team').find_all('ul').find_all('li'), entspricht der Rückgabewert jedes .find(...)Aufrufs noch nicht der Bedeutung von team_members.
Taylor Edmiston
1
@ TaylorEdmiston Natürlich können Sie für die Teilergebnisse unterschiedliche Namen haben. So etwas wie section = soup.find(class_='section team')und team_members = section.find_all('ul').find_all('li').
Jeyekomon
4

Laut Python-Sprachreferenz
können Sie einen Backslash verwenden.
Oder einfach brechen. Wenn eine Klammer nicht gepaart ist, behandelt Python dies nicht als Zeile. Und unter solchen Umständen spielt das Einrücken der folgenden Zeilen keine Rolle.

Haozhun
quelle
4

Es ist eine etwas andere Lösung als die von anderen, aber ein Favorit von mir, da es manchmal zu raffinierten Metaprogrammierungen führt.

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()

Dies ist eine gute Technik zum Erstellen von Suchvorgängen. Gehen Sie eine Liste der Bedingungen durch, die Sie aus Ihrem komplexen Abfrageformular abrufen möchten (oder auf Zeichenfolgen basierende Abzüge darüber, wonach der Benutzer sucht), und lösen Sie dann einfach das Wörterbuch in den Filter auf.

Árni St. Sigurðsson
quelle
1

Sie scheinen SQLAlchemy zu verwenden. Wenn dies zutrifft, verwendet die sqlalchemy.orm.query.Query.filter_by()Methode mehrere Schlüsselwortargumente, sodass Sie wie folgt schreiben können:

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()

Aber es wäre besser:

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()
Minhee
quelle
+1 für SQLAlchemy filter_by () Hinweis. Es ist gut für dieses Beispiel, aber ich verwende stattdessen oft filter (), das nur eine Bedingung akzeptiert.
Juliusz Gonera
1

Ich möchte die Argumente um zwei Blöcke und die Anweisung um einen Block wie folgt einrücken:

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)
acgtyrant
quelle