Definieren Sie Funktionen mit zu vielen Argumenten, um den PEP8-Standard einzuhalten


75

Ich habe eine Funktion mit einer langen Liste von Argumenten definiert. Die Gesamtzahl der Zeichen in der Definition liegt über 80 und entspricht nicht PEP8.

def my_function(argument_one, argument_two, argument_three, argument_four, argument_five):

Was kann der beste Ansatz sein, um horizontales Scrollen zu vermeiden?


1
Und wie kann man eine Funktionsaufrufanweisung am besten schreiben? :)
Nabin

Antworten:


109

Ein Beispiel finden Sie in PEP 8:

class Rectangle(Blob):

    def __init__(self, width, height,
                 color='black', emphasis=None, highlight=0):

Das ist also die offizielle Antwort. Persönlich verabscheue ich diesen Ansatz, bei dem Fortsetzungslinien führende Leerzeichen haben, die keiner echten Einrückungsstufe entsprechen. Mein Ansatz wäre:

class Rectangle(Blob):

    def __init__(
        self, width, height,
        color='black', emphasis=None, highlight=0
    ):

. . . oder lassen Sie die Zeile einfach über 80 Zeichen laufen.


9
Ich war tatsächlich verwirrt, zwischen diesen beiden zu wählen. Zum einen haben durchgehende Linien beliebige Leerzeichen, was ich nicht bevorzuge. Der zweite sieht verwirrend aus, um eine Funktion zu sein. Ich würde die offizielle Antwort von PEP8 vorziehen.
Sudip Kafle

3
Ich finde die Methode in Ihrem zweiten Beispiel schmutzig. Die Erklärung in der ersten fällt mir problemlos auf, während ich bei der zweiten genauer hinschauen muss, um sie zu finden. Besonders wenn es 20 Methoden mit jeweils 2-3 Zeilen in derselben Klasse gibt.
Błażej Michalik

5
Hier geht es nicht darum, mehr oder weniger schmutzig zu sein, sondern darum, dass die Methode im zweiten Beispiel tatsächlich praktischer ist. Es ermöglicht dem Editor, den Methodenkörper ordnungsgemäß zu falten und gleichzeitig die Parameter der Methode anzuzeigen. Die offizielle, wie der Autor vorschlägt, entspricht keiner Einrückungsstufe.
BPL

1
Möglicherweise etwas abseits des Themas, aber weiß jemand, ob es eine Möglichkeit gibt, flake8 zum Scheitern zu zwingen, wenn es die erste Lösung findet? Ich mag es wirklich nicht, obwohl es Standard-PEP8 ist und bevorzuge die zweite Lösung viel mehr.
Ariel

1
Wenn Sie können, setzen Sie die "E128" pep8-Regel in Ihrem Linter auf die schwarze Liste. Wenn dies nicht besser funktioniert, schreiben Sie Ihre eigenen benutzerdefinierten Regeln @Ariel
Salyangoz

20

Für Python-Code, der Typanmerkungen verwendet , empfehle ich Folgendes:

def some_func(
    foo: str,
    bar: str = 'default_string',
    qux: Optional[str] = None,
    qui: Optional[int] = None,
) -> List[str]:
    """
    This is an example function.
    """
    print(foo)
    ...

Wenn Sie yapf verwenden , können Sie diese Optionen verwenden in .style.yapf:

[style]
dedent_closing_brackets = true
split_arguments_when_comma_terminated = true

Ich würde dies unabhängig davon tun, ob Sie Anmerkungen verwenden. Es ist sowohl wartbarer als auch lesbarer.
DylanYoung

HINWEIS: Ich konnte YAPF nicht dazu bringen, sich diesem Stil anzupassen. Ich bin zu dem Schluss gekommen, dass dies ein grundlegendes Problem bei der Gestaltung von YAPF ist: das Verpacken von Behältern. Das Verpacken von Behältern ist weitgehend das Gegenteil von dem, was Sie zum Formatieren von Code tun möchten.
DylanYoung

13

Persönlich habe ich mir auch die gleiche Lösung ausgedacht wie @BrenBarns zweiter Stil. Ich mag seine Art, die Einrückung von Funktionsparametern UND deren Implementierung richtig darzustellen, obwohl dieses "unglückliche Gesicht" für einige andere Menschen etwas ungewöhnlich ist.

Heutzutage gibt PEP8 speziell ein Beispiel für einen solchen Fall. Vielleicht wird der Mainstream diesen Stil anpassen:

# More indentation included to distinguish this from the rest.
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

11
def my_function(argument_one, argument_two, argument_three, 
                argument_four, argument_five):

10

1. Was ich empfehlen würde

Obwohl es die Funktion für mehr als ein Argument ausführlicher macht , denke ich, dass dies am besten ist - das folgende Beispiel stammt von Python-:

def my_function(
    argument_one, 
    argument_two, 
    argument_three,
    argument_four, 
    argument_five,
):
    ...

2. Warum?

  1. Wenn Sie jedes Argument in einer Zeile haben, ist es sehr einfach, git diffs zu verwenden , da das Ändern einer Variablen nur diese Änderung anzeigt. Wenn Sie mehr als ein Argument in jeder Zeile haben, wird es später visuell ärgerlicher.
    • Beachten Sie, dass das nachgestellte Komma nach dem letzten Argument das spätere Hinzufügen oder Entfernen eines Arguments erleichtert, während es gleichzeitig der Konvention nach dem Komma von PEP 8 entspricht und git diffspäter ein besseres ergibt .
  2. Einer der Gründe, warum ich das Paradigma "Argumente mit der öffnenden Klammer ausrichten" wirklich nicht mag, ist, dass es keinen leicht zu wartenden Code liefert .
    • Kevlin Henney erklärt es ist eine schlechte Praxis in seinem ITT 2016 - Sieben Unwirksame Coding Verhalten vieler Programmierer Vorlesung (um 17.08 Uhr) .
    • Der Hauptgrund dafür ist, dass Sie, wenn Sie den Namen der Funktion ändern (oder wenn er zu lang ist), den Abstand in allen Zeilen der Argumente neu bearbeiten müssen , was nicht der Fall ist alles skalierbar, obwohl es ( manchmal ) ( subjektiv ) hübscher sein kann.
    • Der andere Grund, der eng mit dem unmittelbar oben genannten verwandt ist, ist die Metaprogrammierung . Wenn die Codebasis zu groß wird, müssen Sie möglicherweise Änderungen an der Codedatei selbst programmieren. Dies kann zur Hölle werden, wenn der Abstand zwischen den Argumenten für jede Funktion unterschiedlich ist.
  3. Die meisten Editoren öffnen, sobald die Klammern geöffnet sind und Sie drücken enter, eine neue Zeile mit einem Tabulator und schieben die schließende Klammer ohne Tab in die nächste Zeile. Das Formatieren des Codes auf diese Weise ist also sehr schnell und standardisiert. Zum Beispiel ist diese Formatierung in JavaScriptund sehr verbreitet Dart.
  4. Wenn Sie der Meinung sind, dass jedes Argument zu unhandlich ist, weil Ihre Funktion möglicherweise viele Argumente enthält, würde ich sagen, dass Sie aufgrund seltener Ausnahmen die einfache Formatierung Ihres Codes beeinträchtigen.
    • Gesetzgebung nicht durch Ausnahmen .
    • Wenn Ihre Funktion viele Argumente enthält, machen Sie wahrscheinlich etwas falsch. Teilen Sie es in weitere (Unter-) Funktionen und (Unter-) Klassen auf.

3. Wie auch immer ...

Eine gute Konvention ist besser als eine schlechte, aber es ist viel wichtiger, eine durchzusetzen, als unnötig wählerisch zu sein .

Wenn Sie sich für die Verwendung eines Standards entschieden haben, teilen Sie Ihre Entscheidung Ihren Kollegen mit und verwenden Sie einen automatisierten Formatierer. Zum Beispiel ist Prettier eine beliebte Wahl für JavaScriptin VS Code; und die DartSprache hat eine weltweit standardisiert: dartfmt- um sie konsequent durchzusetzen und den Bedarf an manueller Bearbeitung zu verringern.


4

Ich finde mich so ziemlich interessant:

def my_function(
        argument_one, argument_two, argument_three,
        argument_four, argument_five
):
    ...

Es ermöglicht das Falten von Code, um die Funktionssignaturen ganz einfach zu enthüllen. Betrachten Sie beispielsweise das folgende Snippet:

def my_function(
        argument_one, argument_two, argument_three,
        argument_four, argument_five
):
    s1 = 1
    s2 = 2
    if s1 + s2:
        s3 = 3


def my_other_function(argument_one, argument_two, argument_three):
    s1 = 1
    s2 = 2
    if s1 + s2:
        s3 = 3

Auf diese Weise können Sie die gesamte Datei mit einem Code falten und alle Funktionen / Signaturen gleichzeitig anzeigen, dh:

Geben Sie hier die Bildbeschreibung ein


3

Ich persönlich mag es, die Parameter einzeln pro Zeile anzuordnen, beginnend mit den offenen Klammern und unter Beibehaltung dieses Einzugs. flake8scheint auch damit zufrieden zu sein.

def guess_device_type(device_name: str,
                      username: str=app.config['KEY_TACACS_USER'],
                      password: str=app.config['KEY_TACACS_PASS'],
                      command: str='show version') -> str:
    """Get a device_type string for netmiko"""

Es gibt einige technische Probleme mit diesem Stil. 1) Um ein Modul voller Funktionen zu analysieren, muss sich das Auge ständig verschieben, wo es horizontal scannt (es sei denn, alle Ihre Funktionsnamen sind gleich lang;)). 2) Durch Hinzufügen eines Arguments nach commandwerden falsche Diff-Linien erzeugt. 3) Das Eingeben des Funktionsblocks ist ein Dedent (dies ist nicht intuitiv, da in Python das Einrücken normalerweise einen Block beginnt).
DylanYoung

1
@ DylanYoung, gute Punkte! In diesen Tagen, verwende ich blackmeinen Code Autoformat.
Ben

Ich würde Schwarz auch in jedem neuen Code verwenden. +1 Unsere Probleme betreffen hauptsächlich die Migration von altem Code, lol. Eines Tages ...
DylanYoung
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.