Warum sollte main () kurz sein?


87

Ich programmiere seit über 9 Jahren und gemäß dem Rat meines ersten Programmierlehrers halte ich meine main()Funktion immer extrem kurz.

Anfangs hatte ich keine Ahnung warum. Ich habe nur ohne Verständnis gehorcht, sehr zur Freude meiner Professoren.

Nachdem ich Erfahrung main()gesammelt hatte , stellte ich fest, dass es bei korrektem Design meines Codes zu einer Art Kurzfunktion kam. Durch das Schreiben von modularisiertem Code und das Befolgen des Grundsatzes der Einzelverantwortung konnte mein Code in "Blöcken" entworfen werden und main()diente lediglich als Katalysator, um das Programm zum Laufen zu bringen.

Vor ein paar Wochen habe ich mir den Quellcode von Python angesehen und die folgende main()Funktion gefunden:

/* Minimal main program -- everything is loaded from the library */

...

int
main(int argc, char **argv)
{
    ...
    return Py_Main(argc, argv);
}

Ja, Python. Kurzfunktion main()== Guter Code.

Programmierlehrer hatten recht.

Um genauer hinzuschauen, habe ich mir Py_Main angesehen. In seiner Gesamtheit ist es wie folgt definiert:

/* Main program */

int
Py_Main(int argc, char **argv)
{
    int c;
    int sts;
    char *command = NULL;
    char *filename = NULL;
    char *module = NULL;
    FILE *fp = stdin;
    char *p;
    int unbuffered = 0;
    int skipfirstline = 0;
    int stdin_is_interactive = 0;
    int help = 0;
    int version = 0;
    int saw_unbuffered_flag = 0;
    PyCompilerFlags cf;

    cf.cf_flags = 0;

    orig_argc = argc;           /* For Py_GetArgcArgv() */
    orig_argv = argv;

#ifdef RISCOS
    Py_RISCOSWimpFlag = 0;
#endif

    PySys_ResetWarnOptions();

    while ((c = _PyOS_GetOpt(argc, argv, PROGRAM_OPTS)) != EOF) {
        if (c == 'c') {
            /* -c is the last option; following arguments
               that look like options are left for the
               command to interpret. */
            command = (char *)malloc(strlen(_PyOS_optarg) + 2);
            if (command == NULL)
                Py_FatalError(
                   "not enough memory to copy -c argument");
            strcpy(command, _PyOS_optarg);
            strcat(command, "\n");
            break;
        }

        if (c == 'm') {
            /* -m is the last option; following arguments
               that look like options are left for the
               module to interpret. */
            module = (char *)malloc(strlen(_PyOS_optarg) + 2);
            if (module == NULL)
                Py_FatalError(
                   "not enough memory to copy -m argument");
            strcpy(module, _PyOS_optarg);
            break;
        }

        switch (c) {
        case 'b':
            Py_BytesWarningFlag++;
            break;

        case 'd':
            Py_DebugFlag++;
            break;

        case '3':
            Py_Py3kWarningFlag++;
            if (!Py_DivisionWarningFlag)
                Py_DivisionWarningFlag = 1;
            break;

        case 'Q':
            if (strcmp(_PyOS_optarg, "old") == 0) {
                Py_DivisionWarningFlag = 0;
                break;
            }
            if (strcmp(_PyOS_optarg, "warn") == 0) {
                Py_DivisionWarningFlag = 1;
                break;
            }
            if (strcmp(_PyOS_optarg, "warnall") == 0) {
                Py_DivisionWarningFlag = 2;
                break;
            }
            if (strcmp(_PyOS_optarg, "new") == 0) {
                /* This only affects __main__ */
                cf.cf_flags |= CO_FUTURE_DIVISION;
                /* And this tells the eval loop to treat
                   BINARY_DIVIDE as BINARY_TRUE_DIVIDE */
                _Py_QnewFlag = 1;
                break;
            }
            fprintf(stderr,
                "-Q option should be `-Qold', "
                "`-Qwarn', `-Qwarnall', or `-Qnew' only\n");
            return usage(2, argv[0]);
            /* NOTREACHED */

        case 'i':
            Py_InspectFlag++;
            Py_InteractiveFlag++;
            break;

        /* case 'J': reserved for Jython */

        case 'O':
            Py_OptimizeFlag++;
            break;

        case 'B':
            Py_DontWriteBytecodeFlag++;
            break;

        case 's':
            Py_NoUserSiteDirectory++;
            break;

        case 'S':
            Py_NoSiteFlag++;
            break;

        case 'E':
            Py_IgnoreEnvironmentFlag++;
            break;

        case 't':
            Py_TabcheckFlag++;
            break;

        case 'u':
            unbuffered++;
            saw_unbuffered_flag = 1;
            break;

        case 'v':
            Py_VerboseFlag++;
            break;

#ifdef RISCOS
        case 'w':
            Py_RISCOSWimpFlag = 1;
            break;
#endif

        case 'x':
            skipfirstline = 1;
            break;

        /* case 'X': reserved for implementation-specific arguments */

        case 'U':
            Py_UnicodeFlag++;
            break;
        case 'h':
        case '?':
            help++;
            break;
        case 'V':
            version++;
            break;

        case 'W':
            PySys_AddWarnOption(_PyOS_optarg);
            break;

        /* This space reserved for other options */

        default:
            return usage(2, argv[0]);
            /*NOTREACHED*/

        }
    }

    if (help)
        return usage(0, argv[0]);

    if (version) {
        fprintf(stderr, "Python %s\n", PY_VERSION);
        return 0;
    }

    if (Py_Py3kWarningFlag && !Py_TabcheckFlag)
        /* -3 implies -t (but not -tt) */
        Py_TabcheckFlag = 1;

    if (!Py_InspectFlag &&
        (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
        Py_InspectFlag = 1;
    if (!saw_unbuffered_flag &&
        (p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0')
        unbuffered = 1;

    if (!Py_NoUserSiteDirectory &&
        (p = Py_GETENV("PYTHONNOUSERSITE")) && *p != '\0')
        Py_NoUserSiteDirectory = 1;

    if ((p = Py_GETENV("PYTHONWARNINGS")) && *p != '\0') {
        char *buf, *warning;

        buf = (char *)malloc(strlen(p) + 1);
        if (buf == NULL)
            Py_FatalError(
               "not enough memory to copy PYTHONWARNINGS");
        strcpy(buf, p);
        for (warning = strtok(buf, ",");
             warning != NULL;
             warning = strtok(NULL, ","))
            PySys_AddWarnOption(warning);
        free(buf);
    }

    if (command == NULL && module == NULL && _PyOS_optind < argc &&
        strcmp(argv[_PyOS_optind], "-") != 0)
    {
#ifdef __VMS
        filename = decc$translate_vms(argv[_PyOS_optind]);
        if (filename == (char *)0 || filename == (char *)-1)
            filename = argv[_PyOS_optind];

#else
        filename = argv[_PyOS_optind];
#endif
    }

    stdin_is_interactive = Py_FdIsInteractive(stdin, (char *)0);

    if (unbuffered) {
#if defined(MS_WINDOWS) || defined(__CYGWIN__)
        _setmode(fileno(stdin), O_BINARY);
        _setmode(fileno(stdout), O_BINARY);
#endif
#ifdef HAVE_SETVBUF
        setvbuf(stdin,  (char *)NULL, _IONBF, BUFSIZ);
        setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ);
        setvbuf(stderr, (char *)NULL, _IONBF, BUFSIZ);
#else /* !HAVE_SETVBUF */
        setbuf(stdin,  (char *)NULL);
        setbuf(stdout, (char *)NULL);
        setbuf(stderr, (char *)NULL);
#endif /* !HAVE_SETVBUF */
    }
    else if (Py_InteractiveFlag) {
#ifdef MS_WINDOWS
        /* Doesn't have to have line-buffered -- use unbuffered */
        /* Any set[v]buf(stdin, ...) screws up Tkinter :-( */
        setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ);
#else /* !MS_WINDOWS */
#ifdef HAVE_SETVBUF
        setvbuf(stdin,  (char *)NULL, _IOLBF, BUFSIZ);
        setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ);
#endif /* HAVE_SETVBUF */
#endif /* !MS_WINDOWS */
        /* Leave stderr alone - it should be unbuffered anyway. */
    }
#ifdef __VMS
    else {
        setvbuf (stdout, (char *)NULL, _IOLBF, BUFSIZ);
    }
#endif /* __VMS */

#ifdef __APPLE__
    /* On MacOS X, when the Python interpreter is embedded in an
       application bundle, it gets executed by a bootstrapping script
       that does os.execve() with an argv[0] that's different from the
       actual Python executable. This is needed to keep the Finder happy,
       or rather, to work around Apple's overly strict requirements of
       the process name. However, we still need a usable sys.executable,
       so the actual executable path is passed in an environment variable.
       See Lib/plat-mac/bundlebuiler.py for details about the bootstrap
       script. */
    if ((p = Py_GETENV("PYTHONEXECUTABLE")) && *p != '\0')
        Py_SetProgramName(p);
    else
        Py_SetProgramName(argv[0]);
#else
    Py_SetProgramName(argv[0]);
#endif
    Py_Initialize();

    if (Py_VerboseFlag ||
        (command == NULL && filename == NULL && module == NULL && stdin_is_interactive)) {
        fprintf(stderr, "Python %s on %s\n",
            Py_GetVersion(), Py_GetPlatform());
        if (!Py_NoSiteFlag)
            fprintf(stderr, "%s\n", COPYRIGHT);
    }

    if (command != NULL) {
        /* Backup _PyOS_optind and force sys.argv[0] = '-c' */
        _PyOS_optind--;
        argv[_PyOS_optind] = "-c";
    }

    if (module != NULL) {
        /* Backup _PyOS_optind and force sys.argv[0] = '-c'
           so that PySys_SetArgv correctly sets sys.path[0] to ''
           rather than looking for a file called "-m". See
           tracker issue #8202 for details. */
        _PyOS_optind--;
        argv[_PyOS_optind] = "-c";
    }

    PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind);

    if ((Py_InspectFlag || (command == NULL && filename == NULL && module == NULL)) &&
        isatty(fileno(stdin))) {
        PyObject *v;
        v = PyImport_ImportModule("readline");
        if (v == NULL)
            PyErr_Clear();
        else
            Py_DECREF(v);
    }

    if (command) {
        sts = PyRun_SimpleStringFlags(command, &cf) != 0;
        free(command);
    } else if (module) {
        sts = RunModule(module, 1);
        free(module);
    }
    else {

        if (filename == NULL && stdin_is_interactive) {
            Py_InspectFlag = 0; /* do exit on SystemExit */
            RunStartupFile(&cf);
        }
        /* XXX */

        sts = -1;               /* keep track of whether we've already run __main__ */

        if (filename != NULL) {
            sts = RunMainFromImporter(filename);
        }

        if (sts==-1 && filename!=NULL) {
            if ((fp = fopen(filename, "r")) == NULL) {
                fprintf(stderr, "%s: can't open file '%s': [Errno %d] %s\n",
                    argv[0], filename, errno, strerror(errno));

                return 2;
            }
            else if (skipfirstline) {
                int ch;
                /* Push back first newline so line numbers
                   remain the same */
                while ((ch = getc(fp)) != EOF) {
                    if (ch == '\n') {
                        (void)ungetc(ch, fp);
                        break;
                    }
                }
            }
            {
                /* XXX: does this work on Win/Win64? (see posix_fstat) */
                struct stat sb;
                if (fstat(fileno(fp), &sb) == 0 &&
                    S_ISDIR(sb.st_mode)) {
                    fprintf(stderr, "%s: '%s' is a directory, cannot continue\n", argv[0], filename);
                    fclose(fp);
                    return 1;
                }
            }
        }

        if (sts==-1) {
            /* call pending calls like signal handlers (SIGINT) */
            if (Py_MakePendingCalls() == -1) {
                PyErr_Print();
                sts = 1;
            } else {
                sts = PyRun_AnyFileExFlags(
                    fp,
                    filename == NULL ? "<stdin>" : filename,
                    filename != NULL, &cf) != 0;
            }
        }

    }

    /* Check this environment variable at the end, to give programs the
     * opportunity to set it from Python.
     */
    if (!Py_InspectFlag &&
        (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
    {
        Py_InspectFlag = 1;
    }

    if (Py_InspectFlag && stdin_is_interactive &&
        (filename != NULL || command != NULL || module != NULL)) {
        Py_InspectFlag = 0;
        /* XXX */
        sts = PyRun_AnyFileFlags(stdin, "<stdin>", &cf) != 0;
    }

    Py_Finalize();
#ifdef RISCOS
    if (Py_RISCOSWimpFlag)
        fprintf(stderr, "\x0cq\x0c"); /* make frontend quit */
#endif

#ifdef __INSURE__
    /* Insure++ is a memory analysis tool that aids in discovering
     * memory leaks and other memory problems.  On Python exit, the
     * interned string dictionary is flagged as being in use at exit
     * (which it is).  Under normal circumstances, this is fine because
     * the memory will be automatically reclaimed by the system.  Under
     * memory debugging, it's a huge source of useless noise, so we
     * trade off slower shutdown for less distraction in the memory
     * reports.  -baw
     */
    _Py_ReleaseInternedStrings();
#endif /* __INSURE__ */

    return sts;
}

Guter Gott, Allmächtiger ... es ist groß genug, um die Titanic zu versenken.

Es scheint, als hätte Python den Trick "Intro to Programming 101" ausgeführt und den gesamten main()Code auf eine andere Funktion verschoben , die "main" sehr ähnlich ist.

Ist hier meine Frage: Ist dieser Code schrecklich geschrieben, oder gibt es andere Gründe, eine kurze Hauptfunktion zu haben?

Im Moment sehe ich absolut keinen Unterschied, ob ich das mache oder den Code einfach Py_Main()wieder hineinschiebe main(). Liege ich falsch, wenn ich das denke?


4
Wäre das nicht besser für codereviews.stackexchange.com ?
foobar

38
@ Luzhin, nein. Ich bitte niemanden, Pythons Quellcode zu überprüfen. Das ist Programmierfrage.
Riwalk

3
TBH, der halbe Code ist die Verarbeitung von Optionen, und immer wenn Ihr Programm viele Optionen unterstützt und Sie einen benutzerdefinierten Prozessor schreiben, ist dies das, was Sie am Ende tun ...
Nim

7
@Star Nein, Programmierer.SE ist auch für Best Practices, Codierungsstile usw. gedacht. Genau dafür besuche ich die Site.
Mateen Ulhaq

4
@Nim verstehe ich, dass ist , was es tut, aber es gibt keinen Grund , es nicht so zu schreiben , options = ParseOptionFlags(argc,argv)wo optionseine struct, die die Variablen enthält Py_BytesWarningFlag, Py_DebugFlagusw ...
riwalk

Antworten:


137

Sie können nicht mainaus einer Bibliothek exportieren , aber Sie können exportieren Py_Main, und dann kann jeder, der diese Bibliothek verwendet, Python mit verschiedenen Argumenten im selben Programm mehrmals "aufrufen". An diesem Punkt pythonwird nur ein weiterer Verbraucher der Bibliothek, kaum mehr als ein Wrapper für die Bibliotheksfunktion; es ruft Py_Maingenau wie alle anderen.


1
Es gibt eine gute Antwort.
Riwalk

26
Ich nehme an, es ist genauer zu sagen, dass Sie es nicht importieren können, @Shoosh. Der C ++ - Standard verbietet es, ihn aus Ihrem eigenen Code aufzurufen. Außerdem ist die Verknüpfung durch die Implementierung definiert. Zurückkehren von maineffektiven Aufrufen exit, die normalerweise von einer Bibliothek nicht ausgeführt werden sollen.
Rob Kennedy

3
@Coder, siehe C ++ 03 §3.6.1 / 5: "Eine return-Anweisung in mainbewirkt, dass die Hauptfunktion verlassen wird ... und exitder Rückgabewert als Argument verwendet wird." Siehe auch §18.3 / 8, in dem erklärt wird, dass "Objekte mit statischer Speicherdauer zerstört werden" und "alle offenen C-Streams ... gelöscht werden", wenn Sie anrufen exit. C99 hat eine ähnliche Sprache.
Rob Kennedy

1
@Coder, ob exitBlätter mainsind irrelevant. Wir diskutieren nicht das Verhalten von exit. Wir diskutieren das Verhalten von main. Und das Verhalten von main schließt das Verhalten von ein exit, was auch immer das sein mag. Das macht es unerwünscht zu importieren und anzurufen main(wenn so etwas überhaupt möglich oder erlaubt ist).
Rob Kennedy

3
@Coder Wenn die Rückkehr von mainnicht den Effekt hat, dass exitIhr Compiler aufgerufen wird, folgt Ihr Compiler nicht dem Standard. Dass der Standard ein solches Verhalten diktiert für mainbeweist , dass es ist etwas Besonderes. Das Besondere daran mainist, dass die Rückkehr von dort den Effekt hat, anzurufen exit. ( Wie das geht, liegt bei den Compiler-Autoren. Der Compiler könnte einfach Code in den Funktionsepilog einfügen, der statische Objekte zerstört, atexitRoutinen aufruft , Dateien löscht und das Programm beendet - was wiederum nicht in einer Bibliothek gewünscht ist .)
Rob Kennedy

42

Es ist nicht so, dass maines nicht so lange dauern sollte, sondern Sie sollten vermeiden, dass eine Funktion zu lang ist. mainist nur ein spezieller Funktionsfall. Längere Funktionen sind sehr schwer zu fassen, beeinträchtigen die Wartbarkeit und sind im Allgemeinen schwieriger zu bearbeiten. Wenn Sie die Funktionen (und main) kürzer halten, verbessern Sie im Allgemeinen die Qualität Ihres Codes.

In Ihrem Beispiel hat es überhaupt keinen Vorteil, den Code zu entfernen main.


9
Das goldene Wort könnte "Wiederverwendung" sein. Eine lange mainist nicht sehr wiederverwendbar.
S.Lott

1
@S - Das ist ein goldenes Wort. Ein anderer ist OMG !!! ADHS GERADE EINGESETZT !!!! oder für Laien: Lesbarkeit.
Edward Strange

3
main () hat auch einige Einschränkungen, die andere Funktionen nicht haben.
Martin York

1
Auch main () hat keine wirkliche Bedeutung. Ihr Code sollte für einen anderen Programmierer von Bedeutung sein. Ich verwende main, um Argumente zu analysieren, und das ist es - und ich delegiere das sogar, wenn es mehr als ein paar Zeilen sind.
Bill K

@Bill K: Ein guter Punkt, wenn Sie main () nur zum Parsen von Argumenten verwenden (und den Rest des Programms starten), entspricht dies auch dem Prinzip der einmaligen Verantwortung.
Giorgio

28

Ein Grund für eine main()Verkürzung ist das Testen von Einheiten. main()Da diese Funktion nicht Unit-getestet werden kann, ist es sinnvoll, den größten Teil des Verhaltens in eine andere Klasse zu extrahieren, die Unit-getestet werden kann. Das stimmt mit dem überein, was Sie gesagt haben

Durch das Schreiben von modularisiertem Code und das Befolgen des Grundsatzes der Einzelverantwortung konnte mein Code in "Bündeln" entworfen werden, und main () diente lediglich als Katalysator, um das Programm zum Laufen zu bringen.

Hinweis: Ich habe die Idee von hier .


Noch eine gute. Ich habe nie an diesen Aspekt gedacht.
Riwalk

16

Es ist selten eine gute Idee main, lange zu sein. Wie bei jeder Funktion (oder Methode), bei der es sich um eine lange Zeit handelt, verpassen Sie wahrscheinlich Möglichkeiten zur Umgestaltung.

In dem speziellen Fall, den Sie oben erwähnt haben, mainist es kurz, weil all diese Komplexität berücksichtigt wird Py_Main. Wenn Sie möchten, dass sich Ihr Code wie eine Python-Shell verhält, können Sie diesen Code einfach verwenden, ohne viel herumzuspielen. (Es muss so berücksichtigt werden, weil es nicht gut funktioniert, wenn Sie maineine Bibliothek anlegen . Seltsame Dinge passieren, wenn Sie dies tun.)

BEARBEITEN:
Zur Verdeutlichung: mainKann nicht in einer statischen Bibliothek vorhanden sein, da keine explizite Verknüpfung vorhanden ist und daher nicht korrekt eingebunden wird (es sei denn, Sie ordnen sie in einer Objektdatei mit etwas an, auf das verwiesen wird, was einfach schrecklich ist !) Gemeinsam genutzte Bibliotheken werden normalerweise als ähnlich behandelt (um Verwirrung zu vermeiden). Auf vielen Plattformen ist ein zusätzlicher Faktor, dass eine gemeinsam genutzte Bibliothek nur eine ausführbare Datei ohne einen Bootstrap-Abschnitt ist (von denen mainnur der letzte und sichtbarste Teil ist) ).


1
Kurz gesagt, nicht mainin eine Bibliothek stecken. Entweder wird es nicht funktionieren oder es wird Sie schrecklich verwirren. Aber es ist oft sinnvoll, praktisch alle Arbeiten an eine Funktion zu delegieren, die sich in einer Bibliothek befindet.
Donal Fellows

6

Die Hauptleitung sollte aus dem gleichen Grund kurz sein, aus dem auch jede Funktion kurz sein sollte. Dem menschlichen Gehirn fällt es schwer, große Mengen nicht partitionierter Daten gleichzeitig im Speicher zu halten. Teilen Sie es in logische Teile auf, damit andere Entwickler (und auch Sie selbst!) Es leicht verstehen und überlegen können.

Und ja, Ihr Beispiel ist schrecklich und schwer zu lesen, geschweige denn zu pflegen.


Ja, ich hatte immer den Verdacht, dass der Code selbst schrecklich war (obwohl sich die Frage mit der Platzierung des Codes befasste, nicht mit dem Code selbst). Ich fürchte, dass meine Vision von Python inhärent beschädigt wurde ...
riwalk

1
@stargazer: Ich weiß nicht, dass der Code selbst schrecklich ist, nur dass er für den menschlichen Verzehr nicht gut organisiert ist. Das heißt, es gibt viele "hässliche" Codes, die gut funktionieren und eine hervorragende Leistung erbringen. Code-Schönheit ist nicht alles, obwohl wir immer unser Bestes geben sollten, um den saubersten Code zu schreiben.
Ed S.

meh. Für mich sind sie ein und dasselbe. Sauberer Code ist in der Regel stabiler.
Riwalk

Der Code ist nicht schrecklich, hauptsächlich gibt es Switch Cases und die Handhabung mehrerer Plattformen. Was genau findest du schrecklich?
Francesco

@Francesco: Tut mir leid, "Terrible" aus Sicht der Wartung und Lesbarkeit, keine funktionierende.
Ed S.

1

Einige Leute genießen mehr als 50 Funktionen, die nichts anderes tun, als einen Aufruf einer anderen Funktion zu beenden. Ich würde eher eine normale Hauptfunktion vorziehen, die die Hauptprogrammlogik ausführt. Natürlich gut strukturiert.

int main()
{
CheckInstanceCountAndRegister();
InitGlobals();
ProcessCmdParams();
DoInitialization();
ProgramMainLoopOrSomething();
DeInit();
ClearGlobals();
UnregisterInstance();
return 0; //ToMainCRTStartup which cleans heap, etc.
}

Ich sehe keinen Grund, warum ich irgendetwas davon in einen Umschlag packen sollte.

Es ist nur ein persönlicher Geschmack.


1
Weil es den Code dokumentiert. Sie können auf diese Weise Code schreiben, ohne (fast) jemals Kommentare schreiben zu müssen. Und wenn Sie den Code ändern, ändert sich die Dokumentation automatisch :-).
Oliver Weiler

1

Es wird empfohlen, ALLE Funktionen kurz zu halten, nicht nur die wichtigsten. "Short" ist jedoch subjektiv, es hängt von der Größe Ihres Programms und der Sprache ab, die Sie verwenden.


0

Es ist nicht erforderlich main, dass Sie eine andere Länge als die Codierungsstandards haben. mainist eine Funktion wie jede andere, und als solche sollte ihre Komplexität unter 10 liegen (oder was auch immer Ihre Codierungsstandards aussagen). Das war's auch schon, alles andere ist eher umstritten.

bearbeiten

mainsollte nicht kurz sein. Oder lange. Es sollte die Funktionen enthalten, die für die Ausführung auf der Grundlage Ihres Designs erforderlich sind, und die Kodierungsstandards einhalten.

Bezüglich des spezifischen Codes in Ihrer Frage - ja, es ist hässlich.

Bezüglich Ihrer zweiten Frage - ja, Sie liegen falsch . Wenn Sie den gesamten Code zurück in main verschieben, können Sie ihn nicht als Bibliothek verwenden, indem Sie Py_Mainvon außen verlinken .

Jetzt bin ich klar?


Ich habe nicht gefragt, ob es lange dauern kann . Ich fragte, warum es nicht lange dauern sollte .
Riwalk

"Komplexität unter 10"? Gibt es dafür eine Maßeinheit?
Donal Fellows

@ Stargazer712 Die Funktionslänge wird normalerweise auch durch die Kodierungsstandards geregelt. Es ist ein Problem der Lesbarkeit (und der Komplexität, normalerweise sind lange Funktionen so verzweigt, dass die Komplexität weit über 20 liegt) und unterscheidet sich, wie gesagt, mainin dieser Hinsicht nicht von jeder anderen Funktion.
Littleadv

@Donal - Ja, klicken Sie auf den Link.
Littleadv

Ich werde diese eine Knospe ablehnen müssen. Sie verpassen völlig die Absicht der Frage.
Riwalk

0

Hier ist ein neuer pragmatischer Grund, um die Hauptsache aus dem GCC 4.6.1-Changelog kurz zu halten :

Auf den meisten Zielen mit Unterstützung für benannte Abschnitte werden Funktionen, die nur beim Start verwendet werden (statische Konstruktoren und main ), Funktionen, die nur beim Beenden verwendet werden, und Funktionen, die als kalt erkannt wurden, in separate Textsegment-Unterabschnitte gestellt . Dies erweitert die Funktion -freorder-functions und wird über denselben Schalter gesteuert. Ziel ist es, die Startzeit großer C ++ - Programme zu verbessern.

Hervorhebung von mir hinzugefügt.


0

Gehen Sie nicht davon aus, dass der gesamte Code hinter dieser Software gut ist, nur weil ein bisschen Software gut ist. Gute Software und guter Code sind nicht dasselbe, und selbst wenn gute Software von gutem Code unterstützt wird, ist es unvermeidlich, dass es in einem großen Projekt Stellen gibt, an denen Standards ins Wanken geraten.

Es ist eine gute Praxis, eine Kurzfunktion zu haben main, aber das ist eigentlich nur ein Sonderfall der allgemeinen Regel, dass es besser ist, Kurzfunktionen zu haben. Kurze Funktionen sind leichter zu verstehen und leichter zu debuggen sowie besser in der Lage, sich an das Design für einen einzigen Zweck zu halten, das Programme ausdrucksvoller macht. mainist vielleicht ein wichtigerer Ort, um sich an die Regel zu halten, da jeder, der das Programm verstehen will, verstehen muss, mainwährend undurchsichtigere Ecken der Codebasis möglicherweise weniger häufig besucht werden.

Die Python-Codebasis schiebt den Code jedoch nicht nach außen, Py_Mainum diese Regel abzuspielen, sondern weil Sie ihn weder mainaus einer Bibliothek exportieren noch als Funktion aufrufen können.


-1

Es gibt mehrere technische Antworten oben, lassen Sie das beiseite.

Ein Main sollte kurz sein, da es ein Bootstrap sein sollte. Das Hauptfenster sollte eine kleine Anzahl von Objekten instanziieren, oft eines, die die Arbeit erledigen. Wie überall sollten diese Objekte gut gestaltet, zusammenhängend, lose gekoppelt, eingekapselt, ... sein.

Es kann zwar technische Gründe geben, eine einzeilige Hauptmethode als eine andere Monstermethode zu bezeichnen, aber im Prinzip haben Sie recht. Aus Sicht der Softwareentwicklung wurde nichts erreicht. Wenn die Wahl zwischen einer einzeiligen Hauptmethode, die eine Monstermethode aufruft, und der Hauptmethode selbst, die eine Monstermethode ist, ist die letztere weniger schlecht.


Sie gehen davon aus, dass "C ++ - Code Objekte und nur Objekte verwenden sollte". Das ist nicht wahr, C ++ ist eine multiparadigmische Sprache und zwingt nicht wie einige andere Sprachen alles in eine OO-Form.
Ben Voigt
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.