Perché main () dovrebbe essere breve?


87

Ho programmato per oltre 9 anni e, secondo i consigli del mio primo insegnante di programmazione, mantengo sempre la mia main()funzione estremamente breve.

All'inizio non avevo idea del perché. Ho solo obbedito senza capire, con grande gioia dei miei professori.

Dopo aver acquisito esperienza, mi sono reso conto che se avessi progettato il mio codice correttamente, avendo una breve main()funzione sarebbe appena successo. Scrivere codice modulare e seguire il principio della singola responsabilità ha permesso al mio codice di essere progettato in "gruppi" e main()non è stato altro che un catalizzatore per far funzionare il programma.

Velocemente fino a poche settimane fa, stavo guardando il codice Souce di Python e ho trovato la main()funzione:

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

...

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

Yay Python. main()Funzione breve == Buon codice.

Gli insegnanti di programmazione avevano ragione.

Volendo guardare più in profondità, ho dato un'occhiata a Py_Main. Nella sua interezza, è definito come segue:

/* 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;
}

Buon Dio Onnipotente ... è abbastanza grande da affondare il Titanic.

Sembra che Python abbia fatto il trucco "Introduzione alla programmazione 101" e abbia semplicemente spostato tutto main()il codice in una diversa funzione che lo chiamava qualcosa di molto simile a "principale".

Ecco la mia domanda: questo codice è terribilmente scritto o ci sono altri motivi per avere una funzione principale breve?

Allo stato attuale, non vedo assolutamente alcuna differenza tra fare questo e semplicemente spostare Py_Main()nuovamente il codice main(). Sbaglio nel pensare questo?


4
non sarebbe meglio per codereviews.stackexchange.com ?
foobar,

38
@Luzhin, no. Non sto chiedendo a nessuno di rivedere il codice sorgente di Python. Questa è una domanda di programmazione.
riwalk

3
TBH, metà del codice è l'elaborazione delle opzioni, e ogni volta che il tuo programma supporta molte opzioni e scrivi un processore personalizzato, questo è quello che finisci per fare ...
Nim

7
@Star No, Programmers.SE è anche per le migliori pratiche, stili di codifica, ecc. In realtà, questo è ciò per cui visito il sito.
Mateen Ulhaq,

4
@Nim, capisco che è quello che sta facendo, ma non c'è ragione per non scrivere come options = ParseOptionFlags(argc,argv)dove optionsè una structche contiene le variabili Py_BytesWarningFlag, Py_DebugFlagecc ...
riwalk

Risposte:


137

Non è possibile esportare mainda una libreria, ma è possibile esportare Py_Main, quindi chiunque utilizzi quella libreria può "chiamare" Python molte volte con argomenti diversi nello stesso programma. A quel punto, pythondiventa solo un altro consumatore della libreria, poco più che un wrapper per la funzione libreria; chiama Py_Mainproprio come tutti gli altri.


1
C'è una buona risposta
riwalk

26
Suppongo che potrebbe essere più preciso dire che non puoi importarlo , @Shoosh. Lo standard C ++ proibisce di chiamarlo dal proprio codice. Inoltre, il suo collegamento è definito dall'implementazione. Inoltre, tornando da mainchiamate efficaci exit, che di solito non si desidera fare una libreria.
Rob Kennedy,

3
@Coder, vedi C ++ 03 §3.6.1 / 5: "Un'istruzione return in mainha l'effetto di lasciare la funzione principale ... e chiamare exitcon il valore restituito come argomento." Vedi anche §18.3 / 8, che spiega che "gli oggetti con durata della memoria statica vengono distrutti" e "tutti i flussi C aperti ... vengono scaricati" quando si chiama exit. C99 ha un linguaggio simile.
Rob Kennedy,

1
@Coder, se le exitfoglie mainsono irrilevanti. Non stiamo discutendo il comportamento di exit. Stiamo discutendo il comportamento di main. E il comportamento di main include il comportamento di exit, qualunque esso sia. Questo è ciò che rende indesiderabile importare e chiamare main(se fare una cosa del genere è persino possibile o permesso).
Rob Kennedy,

3
@Coder, se il ritorno da mainnon ha l'effetto di chiamare exitil tuo compilatore, il tuo compilatore non segue lo standard. Che la norma impone tale comportamento per la mainprova che non v'è qualcosa di speciale. La cosa speciale mainè che il ritorno da essa ha l'effetto di chiamare exit. ( Come lo fa cioè fino al compilatore scrittori Il compilatore può semplicemente inserire il codice nella funzione epilogo che distrugge oggetti statici, chiama. atexitRoutine, vampate di file, e termina il programma - che, ancora una volta, non è qualcosa che si desidera in una libreria .)
Rob Kennedy,

42

Non maindovrebbe essere lungo quanto dovresti evitare che qualsiasi funzione sia troppo lunga. mainè solo un caso speciale di funzione. Le funzioni più lunghe diventano molto difficili da individuare, diminuiscono la manutenibilità e sono generalmente più difficili da lavorare. Mantenendo le funzioni (e main) più brevi, generalmente si migliora la qualità del codice.

Nel tuo esempio non c'è alcun vantaggio nel spostare fuori il codice main.


9
La parola d'oro può essere "riutilizzo". Un lungo mainnon è molto riutilizzabile.
S. Lott,

1
@S - Questa è una parola d'oro. Un altro è OMG !!! ADHD È STATO SENTITO A CAUSARE !!!! o in parole povere: leggibilità.
Edward Strange,

3
main () ha anche alcune restrizioni che non hanno altre funzioni.
Martin York,

1
Anche main () non ha un significato reale. Il tuo codice dovrebbe significare qualcosa per un altro programmatore. Uso main per analizzare gli argomenti e basta - e lo delego anche se è più di qualche riga.
Bill K,

@Bill K: buon punto, l'uso di main () solo per analizzare gli argomenti (e l'avvio del resto del programma) è conforme anche al principio della responsabilità singola.
Giorgio

28

Un motivo per main()abbreviare consiste nel test unitario. main()è l'unica funzione che non può essere testata dall'unità, quindi ha senso estrarre la maggior parte del comportamento in un'altra classe che può essere testata dall'unità. Questo va di pari passo con quello che hai detto

Scrivere codice modulare e seguire il principio della singola responsabilità ha permesso che il mio codice fosse progettato in "gruppi", e main () non serviva altro che un catalizzatore per far funzionare il programma.

Nota: ho avuto l'idea da qui .


Un altro buono. Non ho mai pensato a quell'aspetto.
riwalk

16

Raramente è una buona idea mainessere lunghi; come con qualsiasi funzione (o metodo) se è lungo probabilmente ti mancano opportunità di refactoring.

Nel caso specifico sopra citato, mainè breve perché tutta quella complessità viene presa in considerazione Py_Main; se vuoi che il tuo codice si comporti come una shell python, puoi semplicemente usare quel codice senza troppi scherzi. (Deve essere preso in considerazione in questo modo perché non funziona bene se lo metti mainin una libreria; cose strane accadono se lo fai.)

EDIT:
per chiarire, mainnon può essere in una libreria statica perché non ha un link esplicito ad esso e quindi non sarà collegato correttamente (a meno che non lo si raccolga in un file oggetto con qualcosa a cui si fa riferimento, il che è semplicemente orribile !) Le librerie condivise sono generalmente trattate come simili (di nuovo, per evitare confusione) anche se su molte piattaforme un ulteriore fattore è che una libreria condivisa è solo un eseguibile senza una sezione bootstrap (di cui mainè solo l'ultima e più visibile parte ).


1
In breve, non mettere mainin una biblioteca. O non funzionerà o ti confonderà terribilmente. Ma delegare praticamente tutto il suo lavoro a una funzione che è in una lib, è spesso sensato.
Donal Fellows,

6

Main dovrebbe essere corto per lo stesso motivo per cui qualsiasi funzione dovrebbe essere breve. Il cervello umano ha difficoltà a conservare contemporaneamente grandi quantità di dati non partizionati. Suddividilo in blocchi logici in modo che sia facile da digerire e ragionare per altri sviluppatori (così come te stesso!).

E sì, il tuo esempio è terribile e difficile da leggere, figuriamoci da mantenere.


Sì, ho sempre sospettato che il codice stesso fosse terribile (sebbene la domanda riguardasse il posizionamento del codice, non il codice stesso). Temo che la mia visione di Python sia stata intrinsecamente danneggiata di conseguenza ...
riwalk del

1
@stargazer: non so che il codice stesso sia terribile, solo che non è ben organizzato per il consumo umano. Detto questo, c'è un sacco di codice "brutto" là fuori che funziona bene e funziona alla grande. La bellezza del codice non è tutto, anche se dovremmo sempre fare del nostro meglio per scrivere il codice più pulito possibile.
Ed S.

meh. Per me, sono la stessa cosa. Il codice pulito tende ad essere più stabile.
riwalk

Il codice non è terribile, principalmente ci sono casi di switch e la gestione di più piattaforme. Cosa trovi esattamente terribile?
Francesco,

@Francesco: scusami, "Terribile" dal punto di vista della manutenzione e della leggibilità, non funzionale.
Ed S.

1

Alcune persone godono di oltre 50 funzioni che non fanno altro, ma concludono una chiamata a un'altra funzione. Preferirei preferire la normale funzione principale che fa la logica del programma principale. Ben strutturato ovviamente.

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

Non vedo alcun motivo per cui dovrei avvolgere qualcosa di tutto ciò in un involucro.

È puramente un gusto personale.


1
Perché documenta il codice. Puoi scrivere codice in questo modo senza la necessità (quasi) di scrivere mai commenti. E quando si modifica il codice, la documentazione cambia automaticamente :-).
Oliver Weiler,

1

La sua migliore pratica è quella di mantenere TUTTE le tue funzioni brevi, non solo quelle principali. Tuttavia, "breve" è soggettivo, dipende dalle dimensioni del programma e dalla lingua che si sta utilizzando.


0

Non è necessario mainavere alcuna lunghezza, a parte gli standard di codifica. mainè una funzione come qualsiasi altra, e come tale la sua complessità dovrebbe essere inferiore a 10 (o qualunque cosa dicano gli standard di codifica). Questo è tutto, qualsiasi altra cosa è piuttosto polemica.

modificare

mainnon dovrebbe essere corto. O lungo. Dovrebbe includere le funzionalità necessarie per eseguire in base al progetto e aderire agli standard di codifica.

Per quanto riguarda il codice specifico nella tua domanda - sì, è brutto.

Quanto alla tua seconda domanda: sì, ti sbagli . Spostare tutto quel codice di nuovo in main non ti consente di usarlo come una libreria collegandolo Py_Maindall'esterno.

Adesso sono chiaro?


Non ho chiesto se può essere lungo. Ho chiesto perché non dovrebbe essere lungo.
riwalk

"Complessità inferiore a 10"? Esiste un'unità di misura per questo?
Donal Fellows,

@ Stargazer712 La lunghezza della funzione è generalmente regolata anche dagli standard di codifica. È un problema di leggibilità (e la complessità, di solito le funzioni lunghe sono diramate in modo tale che la complessità sia molto superiore a 20), e come ho detto - mainnon è diversa da qualsiasi altra funzione in questo senso.
littleadv,

@Donal - sì, fai clic sul link.
littleadv,

Dovrò sottovalutare questa gemma. Ti manca completamente l'intento della domanda.
riwalk

0

Ecco un nuovo motivo pragmatico anche a corto di corto di GCC 4.6.1 Log delle modifiche :

Sulla maggior parte delle destinazioni con supporto per le sezioni denominate, le funzioni utilizzate solo all'avvio (costruttori statici e principali ), le funzioni utilizzate solo all'uscita e le funzioni rilevate come fredde vengono inserite in sottosezioni di segmenti di testo separati . Questo estende la funzione -freorder-funzioni ed è controllato dallo stesso interruttore. L'obiettivo è migliorare i tempi di avvio di grandi programmi C ++.

Evidenziazione aggiunta da me.


0

Non dare per scontato che solo perché un po 'di software è buono tutto il codice dietro quel software è buono. Un buon software e un buon codice non sono la stessa cosa e anche se un buon software è supportato da un buon codice, è inevitabile che in un grande progetto ci siano luoghi in cui gli standard scivolano.

È buona norma avere una mainfunzione breve , ma in realtà è solo un caso speciale della regola generale secondo cui è meglio avere funzioni brevi. Le funzioni brevi sono più facili da capire e più facili da eseguire il debug, oltre ad essere più efficaci nel rispettare il tipo di design "monouso" che rende i programmi più espressivi. mainè, forse, un posto più importante per attenersi alla regola poiché chiunque voglia capire il programma deve capire mainmentre angoli più oscuri della base di codice possono essere visitati meno spesso.

Ma la base di codice di Python non sta spingendo il codice verso l'esterno per Py_Maingiocare questa regola, ma perché non puoi esportare mainda una libreria né chiamarlo come una funzione.


-1

Ci sono diverse risposte tecniche sopra, lasciamo da parte.

Un main dovrebbe essere corto perché dovrebbe essere un bootstrap. Il principale dovrebbe creare un'istanza di un piccolo numero di oggetti, spesso uno, che svolgono il lavoro. Come altrove, quegli oggetti dovrebbero essere ben progettati, coerenti, accoppiati liberamente, incapsulati, ...

Mentre potrebbero esserci ragioni tecniche per avere una chiamata principale su una linea, un altro metodo mostruoso, in linea di principio hai ragione. Dal punto di vista dell'ingegneria del software, non è stato ottenuto nulla. Se la scelta è tra una linea principale che chiama un metodo mostro e che il principale stesso è un metodo mostro, quest'ultimo è frazionalmente meno male.


Stai assumendo che "il codice C ++ dovrebbe usare oggetti e solo oggetti". Questo non è vero, C ++ è un linguaggio multiparadigm e non forza tutto in uno stampo OO come in altre lingue.
Ben Voigt,
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.