Mengapa main () harus pendek?

87

Saya telah pemrograman selama lebih dari 9 tahun, dan menurut saran dari guru pemrograman pertama saya, saya selalu menjaga main()fungsi saya sangat singkat.

Awalnya saya tidak tahu kenapa. Saya hanya patuh tanpa pengertian, sangat menyenangkan para profesor saya.

Setelah mendapatkan pengalaman, saya menyadari bahwa jika saya mendesain kode saya dengan benar, memiliki main()fungsi pendek terjadi saja. Menulis kode termodulasi dan mengikuti prinsip tanggung jawab tunggal memungkinkan kode saya dirancang dalam "tandan", dan main()berfungsi sebagai katalis untuk menjalankan program.

Maju cepat ke beberapa minggu yang lalu, saya melihat kode sumber Python, dan saya menemukan main()fungsinya:

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

...

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

Yay python. main()Fungsi pendek == Kode yang baik.

Memprogram guru benar.

Ingin melihat lebih dalam, saya melihat Py_Main. Secara keseluruhan, didefinisikan sebagai berikut:

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

Ya Tuhan Yang Maha Kuasa ... itu cukup besar untuk menenggelamkan Titanic.

Tampaknya seolah-olah Python melakukan trik "Pengenalan Pemrograman 101" dan baru saja memindahkan semua main()kode ke fungsi yang berbeda, menyebutnya sesuatu yang sangat mirip dengan "utama".

Inilah pertanyaan saya: Apakah kode ini ditulis dengan sangat buruk, atau adakah alasan lain untuk memiliki fungsi utama yang singkat?

Seperti berdiri sekarang, saya sama sekali tidak melihat perbedaan antara melakukan ini dan hanya memindahkan kode Py_Main()kembali ke main(). Apakah saya salah dalam memikirkan ini?

riwalk
sumber
4
bukankah itu lebih baik untuk codereviews.stackexchange.com ?
foobar
38
@Luzhin, tidak. Saya tidak meminta siapa pun untuk meninjau kode sumber Python. Ini pertanyaan pemrograman.
riwalk
3
TBH, separuh kodenya adalah pemrosesan opsi, dan kapan pun program Anda mendukung banyak opsi, dan Anda menulis prosesor khusus, inilah yang akhirnya Anda lakukan ...
Nim
7
@Star Tidak, Programmers.SE juga untuk praktik terbaik, gaya pengkodean, dll. Sebenarnya, itulah tujuan saya mengunjungi situs ini.
Mateen Ulhaq
4
@Nim, saya memahami bahwa adalah apa yang dilakukannya, tetapi tidak ada alasan untuk tidak menulis sebagai options = ParseOptionFlags(argc,argv)mana optionsadalah structyang berisi variabel Py_BytesWarningFlag, Py_DebugFlag, dll ...
riwalk

Jawaban:

137

Anda tidak dapat mengekspor maindari perpustakaan, tetapi Anda dapat mengekspor Py_Main, dan siapa pun yang menggunakan perpustakaan itu dapat "memanggil" Python berkali-kali dengan argumen berbeda dalam program yang sama. Pada saat itu, pythonmenjadi sekadar konsumen perpustakaan, sedikit lebih dari sekadar pembungkus untuk fungsi perpustakaan; itu panggilan Py_Mainsama seperti orang lain.

Rob Kennedy
sumber
1
Ada jawaban yang bagus.
riwalk
26
Saya kira mungkin lebih akurat untuk mengatakan Anda tidak dapat mengimpornya , @Shoosh. Standar C ++ melarang memanggilnya dari kode Anda sendiri. Selain itu, keterkaitannya ditentukan oleh implementasi. Juga, kembali dari mainpanggilan efektif exit, yang biasanya Anda tidak ingin perpustakaan lakukan.
Rob Kennedy
3
@Coder, lihat C ++ 03 §3.6.1 / 5: "Pernyataan kembali mainmemiliki efek meninggalkan fungsi utama ... dan memanggil exitdengan nilai kembali sebagai argumen." Juga lihat §18.3 / 8, yang menjelaskan bahwa "objek dengan durasi penyimpanan statis dihancurkan" dan "semua aliran C yang terbuka ... dibilas" ketika Anda menelepon exit. C99 memiliki bahasa yang mirip.
Rob Kennedy
1
@Coder, apakah exitdaun maintidak relevan. Kami tidak membahas perilaku exit. Kami sedang mendiskusikan perilaku main. Dan perilaku main termasuk perilaku exit, apa pun itu. Itulah yang membuatnya tidak diinginkan untuk mengimpor dan menelepon main(jika melakukan hal seperti itu bahkan mungkin atau diizinkan).
Rob Kennedy
3
@Coder, jika kembali dari maintidak memiliki efek memanggil exitkompiler Anda, maka kompiler Anda tidak mengikuti standar. Bahwa standar mendikte perilaku tersebut untuk mainmembuktikan bahwa ada adalah sesuatu yang istimewa tentang hal itu. Hal khusus tentang itu mainadalah bahwa kembali darinya memiliki efek menelepon exit. ( Cara melakukannya tergantung pada penulis kompiler. Kompilator dapat dengan mudah menyisipkan kode dalam fungsi epilog yang menghancurkan objek statis, memanggil atexitrutin, mem-flush file, dan menghentikan program - yang, sekali lagi, bukan sesuatu yang Anda inginkan di perpustakaan .)
Rob Kennedy
42

Ini bukan berarti bahwa maintidak boleh lama begitu banyak seperti Anda harus menghindari setiap fungsi menjadi terlalu lama. mainhanyalah kasus fungsi khusus. Fungsi yang lebih lama menjadi sangat sulit untuk grok, mengurangi rawatan, dan umumnya lebih sulit untuk dikerjakan. Dengan menjaga fungsi (dan main) lebih pendek, Anda biasanya meningkatkan kualitas kode Anda.

Dalam contoh Anda tidak ada manfaat sama sekali untuk mengeluarkan kode main.

Mark B
sumber
9
Kata emas mungkin "digunakan kembali". Panjang maintidak bisa digunakan kembali.
S.Lott
1
@ S - Itu satu kata emas. Lain adalah OMG !!! ADHD HANYA MENARIK !!!! atau dalam istilah awam: keterbacaan.
Edward Strange
3
main () juga memiliki beberapa batasan yang tidak dimiliki fungsi lain.
Martin York
1
Main () juga tidak memiliki arti sebenarnya. Semua kode Anda harus berarti bagi programmer lain. Saya menggunakan argumen utama untuk mengurai dan itu saja - dan saya bahkan mendelegasikannya jika lebih dari beberapa baris.
Bill K
@ Bill K: Poin bagus, menggunakan main () hanya untuk menguraikan argumen (dan memulai sisa program) juga sesuai dengan prinsip tanggung jawab tunggal.
Giorgio
28

Salah satu alasan untuk membuat main()pendek melibatkan pengujian unit. main()adalah satu fungsi yang tidak bisa diuji unit, jadi masuk akal untuk mengekstrak mayoritas perilaku ke dalam kelas lain yang bisa diuji unit. Ini sejalan dengan apa yang Anda katakan

Menulis kode yang termodulasi dan mengikuti prinsip tanggung jawab tunggal memungkinkan kode saya dirancang dalam "tandan", dan main () berfungsi sebagai katalis untuk menjalankan program.

Catatan: Saya mendapat ide dari sini .

Kesempatan
sumber
Satu lagi yang bagus. Tidak pernah memikirkan aspek itu.
riwalk
16

Jarang sekali ide yang bagus untuk mainmenjadi panjang; seperti halnya fungsi (atau metode) apa pun jika itu lama Anda mungkin kehilangan peluang untuk refactoring.

Dalam kasus khusus yang Anda sebutkan di atas, mainsingkat karena semua kompleksitas diperhitungkan Py_Main; jika Anda ingin kode Anda berperilaku seperti shell python, Anda bisa menggunakan kode itu tanpa banyak mengotak-atik. (Itu harus diperhitungkan seperti itu karena tidak berfungsi dengan baik jika Anda menempatkan maindi perpustakaan; hal-hal aneh terjadi jika Anda melakukannya.)

EDIT:
Untuk memperjelas, maintidak dapat berada di perpustakaan statis karena tidak memiliki tautan eksplisit ke sana dan karenanya tidak akan ditautkan dengan benar (kecuali Anda menempatkannya di file objek dengan sesuatu yang dirujuk, yang hanya mengerikan !) Pustaka bersama biasanya diperlakukan sama (sekali lagi, untuk mencegah kebingungan) meskipun pada banyak platform faktor tambahan adalah bahwa pustaka bersama hanya dapat dieksekusi tanpa bagian bootstrap (yang mainhanya merupakan bagian terakhir dan paling terlihat ).

Donal Fellows
sumber
1
Singkatnya, jangan letakkan maindi perpustakaan. Entah itu tidak akan berhasil atau itu akan membingungkan Anda. Tapi mendelegasikan hampir semua pekerjaannya ke fungsi yang ada di lib, itu sering masuk akal.
Donal Fellows
6

Utama harus pendek karena alasan yang sama bahwa fungsi apa pun harus pendek. Otak manusia mengalami kesulitan menyimpan sejumlah besar data yang tidak dipartisi dalam memori sekaligus. Pecah menjadi potongan-potongan logis sehingga mudah bagi pengembang lain (dan juga Anda sendiri!) Untuk dicerna dan dipikirkan.

Dan ya, teladan Anda sangat buruk dan sulit dibaca, apalagi dipertahankan.

Ed S.
sumber
Ya, saya selalu curiga bahwa kodenya sendiri mengerikan (meskipun pertanyaannya berkaitan dengan penempatan kodenya, bukan kodenya sendiri). Saya khawatir bahwa penglihatan saya tentang Python secara inheren telah rusak sebagai hasilnya ...
riwalk
1
@stargazer: Saya tidak tahu bahwa kode itu sendiri mengerikan, hanya saja tidak diatur dengan baik untuk konsumsi manusia. Yang mengatakan, ada banyak kode "jelek" di luar sana yang bekerja dengan baik dan berkinerja hebat. Keindahan kode bukanlah segalanya, meskipun kita harus selalu berusaha sebaik mungkin untuk menulis kode terbersih yang mungkin.
Ed S.
meh. Bagi saya, mereka satu dan sama. Kode bersih cenderung lebih stabil.
riwalk
Kode ini tidak buruk, terutama ada saklar kasus dan penanganan beberapa platform. Apa tepatnya yang Anda temukan mengerikan?
Francesco
@ Francesco: Maaf, "Mengerikan" dari perspektif pemeliharaan dan keterbacaan, bukan yang fungsional.
Ed S.
1

Beberapa orang menikmati 50+ fungsi yang tidak melakukan apa-apa lagi, tetapi membungkus panggilan ke fungsi lain. Saya lebih suka fungsi utama normal yang melakukan logika program utama. Tentu saja terstruktur dengan baik.

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

Saya tidak melihat alasan mengapa saya harus membungkusnya dengan bungkus.

Ini murni selera pribadi.

Coder
sumber
1
Karena itu mendokumentasikan kodenya. Anda dapat menulis kode dengan cara ini tanpa perlu (hampir) pernah menulis komentar. Dan ketika Anda mengubah kode, dokumentasi berubah secara otomatis :-).
Oliver Weiler
1

Praktik terbaik untuk menjaga SEMUA fungsi Anda tetap singkat, bukan hanya utama. Namun "pendek" itu subjektif, tergantung pada ukuran program Anda dan bahasa yang Anda gunakan.

Mike Miller
sumber
0

Tidak ada persyaratan mainapa pun untuk apa pun, selain standar pengkodean. mainadalah fungsi seperti yang lainnya, dan kompleksitasnya harus di bawah 10 (atau apa pun standar pengkodean Anda katakan). Itu saja, yang lain agak argumentatif.

sunting

mainseharusnya tidak pendek. Atau panjang. Ini harus mencakup fungsionalitas yang diperlukan untuk dilakukan berdasarkan desain Anda, dan mematuhi standar pengkodean.

Mengenai kode spesifik dalam pertanyaan Anda - ya, itu jelek.

Mengenai pertanyaan kedua Anda - ya, Anda salah . Memindahkan semua kode itu kembali ke main tidak memungkinkan Anda menggunakannya modulary sebagai perpustakaan dengan menautkan Py_Maindari luar.

Sekarang apakah saya jelas?

littleadv
sumber
Saya tidak bertanya apakah itu bisa lama. Saya bertanya mengapa tidak lama.
riwalk
"Kompleksitas di bawah 10"? Apakah ada unit pengukuran untuk itu?
Donal Fellows
@ Stargazer712 Panjang fungsi biasanya diatur oleh standar pengkodean juga. Ini masalah keterbacaan (dan kompleksitas, biasanya fungsi panjang bercabang sehingga kompleksitasnya jauh di atas 20), dan seperti yang saya katakan - maintidak ada bedanya dengan fungsi lain dalam hal ini.
littleadv
@ Donal - ya, klik tautannya.
littleadv
Saya akan harus memilih dulu kuncup yang satu ini. Anda benar-benar kehilangan maksud pertanyaan.
riwalk
0

Berikut ini alasan pragmatis baru juga tetap pendek dari GCC 4.6.1 Changelog utama :

Pada sebagian besar target dengan dukungan bagian bernama, fungsi yang hanya digunakan saat startup (konstruktor statis dan utama ), fungsi yang hanya digunakan saat keluar dan fungsi yang terdeteksi dingin ditempatkan ke sub-bagian segmen teks terpisah . Ini memperluas fitur -freorder-fungsi dan dikendalikan oleh saklar yang sama. Tujuannya adalah untuk meningkatkan waktu startup dari program C ++ besar.

Sorotan ditambahkan oleh saya.

Peter G.
sumber
0

Jangan berasumsi bahwa hanya karena sedikit perangkat lunak baik, semua kode di belakang perangkat lunak itu baik. Perangkat lunak yang baik dan kode yang baik bukan hal yang sama dan bahkan di mana perangkat lunak yang baik didukung oleh kode yang baik, tidak dapat dihindari bahwa dalam proyek besar akan ada tempat di mana standar tergelincir.

Ini adalah praktik yang baik untuk memiliki mainfungsi pendek , tetapi itu benar-benar hanya kasus khusus dari aturan umum bahwa lebih baik memiliki fungsi pendek. Fungsi pendek lebih mudah dipahami dan lebih mudah untuk di-debug serta lebih baik berpegang pada jenis desain 'tujuan tunggal' yang membuat program lebih ekspresif. mainmungkin merupakan tempat yang lebih penting untuk tetap berpegang pada aturan karena siapa pun yang ingin memahami program harus memahami mainsementara sudut kode basis yang lebih tidak jelas mungkin lebih jarang dikunjungi.

Tapi, basis kode Python tidak mendorong kode untuk Py_Mainmenjalankan aturan ini, tetapi karena Anda tidak bisa mengekspor maindari perpustakaan atau menyebutnya sebagai fungsi.

Jack Aidley
sumber
-1

Ada beberapa jawaban teknis di atas, mari kita kesampingkan itu.

Main harus pendek karena harus bootstrap. Yang utama harus instantiate sejumlah kecil objek, sering satu, yang melakukan pekerjaan. Seperti di tempat lain, benda-benda itu harus dirancang dengan baik, kohesif, longgar digabungkan, dienkapsulasi, ...

Meskipun mungkin ada alasan teknis untuk memiliki panggilan utama satu-line metode monster lain, pada prinsipnya Anda benar. Dari perspektif rekayasa perangkat lunak, tidak ada yang diperoleh. Jika pilihannya adalah antara satu baris utama yang memanggil metode monster, dan utama itu sendiri adalah metode monster, yang terakhir fraksinya kurang buruk.

Gannet Beton
sumber
Anda mengasumsikan bahwa "kode C ++ harus menggunakan objek dan hanya objek". Itu tidak benar, C ++ adalah bahasa multiparadigma, dan tidak memaksa semuanya menjadi cetakan OO seperti beberapa bahasa lainnya.
Ben Voigt