Tại sao main () phải ngắn?


87

Tôi đã lập trình được hơn 9 năm và theo lời khuyên của giáo viên lập trình đầu tiên của tôi, tôi luôn giữ cho main()chức năng của mình cực kỳ ngắn.

Lúc đầu tôi không biết tại sao. Tôi chỉ vâng lời mà không hiểu, rất nhiều cho niềm vui của các giáo sư của tôi.

Sau khi có được kinh nghiệm, tôi nhận ra rằng nếu tôi thiết kế mã của mình một cách chính xác, có một main()hàm ngắn chỉ sắp xếp xảy ra. Viết mã được mô đun hóa và tuân theo nguyên tắc trách nhiệm duy nhất cho phép mã của tôi được thiết kế theo "bó" và được main()dùng như một chất xúc tác để chương trình chạy.

Nhanh chóng chuyển đến một vài tuần trước, tôi đã xem mã souce của Python và tôi đã tìm thấy main()hàm:

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

...

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

Con trăn Yay. main()Hàm ngắn == Mã tốt.

Giáo viên lập trình đã đúng.

Muốn nhìn sâu hơn, tôi đã xem Py_Main. Trong toàn bộ, nó được định nghĩa như sau:

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

Lạy Chúa toàn năng ... nó đủ lớn để đánh chìm tàu ​​Titanic.

Có vẻ như Python đã thực hiện thủ thuật "Giới thiệu lập trình 101" và chỉ cần chuyển tất cả main()mã sang một chức năng khác gọi nó là một cái gì đó rất giống với "chính".

Đây là câu hỏi của tôi: Mã này được viết một cách khủng khiếp, hoặc có những lý do khác để có một chức năng chính ngắn?

Vì nó đứng ngay bây giờ, tôi thấy hoàn toàn không có sự khác biệt giữa làm điều này và chỉ cần di chuyển mã Py_Main()trở lại main(). Tôi có sai khi nghĩ điều này không?


4
điều đó sẽ tốt hơn cho codereview.stackexchange.com ?
foobar

38
@Luzhin, không. Tôi không yêu cầu ai xem lại mã nguồn của Python. Đây là câu hỏi lập trình.
riwalk

3
TBH, một nửa mã là xử lý tùy chọn và bất cứ khi nào chương trình của bạn hỗ trợ rất nhiều tùy chọn và bạn viết một bộ xử lý tùy chỉnh, đây là những gì bạn sẽ làm ...
Nim

7
@ Star Không, Lập trình viên.SE cũng dành cho các thực tiễn tốt nhất, phong cách mã hóa, v.v. Trên thực tế, đó là những gì tôi truy cập trang web này.
Mateen Ulhaq

4
@Nim, tôi hiểu đó là những gì nó đang làm, nhưng không có lý do để không viết nó như là options = ParseOptionFlags(argc,argv)nơi optionsstructcó chứa các biến Py_BytesWarningFlag, Py_DebugFlag, vv ...
riwalk

Câu trả lời:


137

Bạn không thể xuất khẩu maintừ một thư viện, nhưng bạn có thể xuất Py_Main, và sau đó bất cứ ai sử dụng thư viện có thể "gọi" Python nhiều lần với lý lẽ khác nhau trong cùng một chương trình. Tại thời điểm đó, pythontrở thành một người tiêu dùng khác của thư viện, ít hơn một trình bao bọc cho chức năng thư viện; Nó gọi Py_Maingiống như những người khác.


1
Có một câu trả lời hay.
riwalk

26
Tôi cho rằng có thể chính xác hơn để nói rằng bạn không thể nhập nó, @Shoosh. Tiêu chuẩn C ++ cấm gọi nó từ mã của riêng bạn. Bên cạnh đó, liên kết của nó được xác định thực hiện. Ngoài ra, trở về từ maincác cuộc gọi hiệu quả exit, điều mà bạn thường không muốn thư viện thực hiện.
Rob Kennedy

3
@Coder, xem C ++ 03 §3.6.1 / 5: "Một câu lệnh return in maincó tác dụng rời khỏi hàm main chính và gọi exitvới giá trị trả về làm đối số." Đồng thời xem §18.3 / 8, giải thích rằng "các đối tượng có thời lượng lưu trữ tĩnh bị hủy" và "tất cả các luồng C đang mở được xóa" khi bạn gọi exit. C99 có ngôn ngữ tương tự.
Rob Kennedy

1
@Coder, cho dù exitmainlà không liên quan. Chúng tôi sẽ không thảo luận về hành vi của exit. Chúng ta đang thảo luận về hành vi của main. Và hành vi main bao gồm hành vi của exit, bất cứ điều gì có thể. Đó là điều khiến cho việc nhập và gọi không mong muốn main(nếu làm như vậy thậm chí có thể hoặc được phép).
Rob Kennedy

3
@Coder, nếu trở về từ mainkhông có tác dụng gọi exittrình biên dịch của bạn, thì trình biên dịch của bạn không tuân theo tiêu chuẩn. Đó là tiêu chuẩn mệnh lệnh hành vi như vậy cho mainchứng minh rằng có một cái gì đó đặc biệt về nó. Điều đặc biệt mainlà việc trở về từ nó có tác dụng gọi điện exit. ( Làm thế nào nó có nghĩa là lên đến trình biên dịch nhà văn Trình biên dịch chỉ đơn giản là có thể chèn mã trong lời bạt chức năng phá hủy các đối tượng tĩnh, cuộc gọi. atexitThói quen, xả file, và kết thúc chương trình - mà, một lần nữa, không phải là một cái gì đó mà bạn muốn trong thư viện .)
Rob Kennedy

42

Không mainnên kéo dài quá nhiều vì bạn nên tránh mọi chức năng quá dài. mainchỉ là một trường hợp đặc biệt của chức năng. Các chức năng dài hơn rất khó để tìm kiếm, giảm khả năng bảo trì và thường khó làm việc hơn. Bằng cách giữ các hàm (và main) ngắn hơn, bạn thường cải thiện chất lượng mã của mình.

Trong ví dụ của bạn không có lợi ích gì khi chuyển mã ra khỏi main.


9
Từ vàng có thể là "tái sử dụng". Một thời gian dài mainkhông thể tái sử dụng.
S.Lott

1
@S - Đó là một từ vàng. Một cái khác là OMG !!! ADHD CHỈ CẦN VÀO !!!! hoặc theo thuật ngữ cư sĩ: mức độ dễ đọc.
Edward Strange

3
main () cũng có một số hạn chế mà các hàm khác không có.
Martin York

1
Ngoài ra main () không có ý nghĩa thực sự. Mã của bạn nên có ý nghĩa gì đó với một lập trình viên khác. Tôi sử dụng chính để phân tích các đối số và đó là nó - và tôi thậm chí ủy thác rằng nếu nó nhiều hơn một vài dòng.
Bill K

@Bill K: Điểm hay, chỉ sử dụng hàm main () để phân tích các đối số (và bắt đầu phần còn lại của chương trình) cũng tuân thủ nguyên tắc trách nhiệm đơn.
Giorgio

28

Một lý do để thực hiện main()ngắn liên quan đến thử nghiệm đơn vị. main()là một chức năng không thể được kiểm tra đơn vị, do đó, việc trích xuất phần lớn hành vi vào một lớp khác có thể được kiểm tra đơn vị là hợp lý. Điều này đi cùng với những gì bạn nói

Viết mã được mô đun hóa và tuân theo nguyên tắc trách nhiệm duy nhất cho phép mã của tôi được thiết kế theo "bó" và main () được dùng như một chất xúc tác để chương trình chạy.

Lưu ý: Tôi có ý tưởng từ đây .


Một số tốt. Không bao giờ nghĩ về khía cạnh đó.
riwalk

16

Nó hiếm khi là một ý tưởng tốt mainđể được lâu dài; như với bất kỳ chức năng (hoặc phương thức) nào nếu lâu bạn có thể bỏ lỡ cơ hội tái cấu trúc.

Trong trường hợp cụ thể mà bạn đề cập ở trên, mainlà ngắn vì tất cả sự phức tạp đó được thể hiện thành Py_Main; nếu bạn muốn mã của mình hoạt động giống như vỏ trăn, bạn chỉ có thể sử dụng mã đó mà không phải loay hoay nhiều. (Nó phải được bao thanh toán như vậy bởi vì nó không hoạt động tốt nếu bạn đặt maintrong thư viện; những điều kỳ lạ sẽ xảy ra nếu bạn làm như vậy.)

EDIT:
Để làm rõ, mainkhông thể ở trong một thư viện tĩnh vì nó không có liên kết rõ ràng với nó và vì vậy sẽ không được liên kết chính xác (trừ khi bạn đặt nó trong một tệp đối tượng với một cái gì đó được nhắc đến, điều này thật kinh khủng !) Thư viện dùng chung thường được coi là tương tự (một lần nữa, để tránh nhầm lẫn) mặc dù trên nhiều nền tảng, một yếu tố bổ sung là thư viện dùng chung chỉ là một tệp thực thi mà không có phần bootstrap (trong đó mainchỉ là phần cuối cùng và dễ thấy nhất ).


1
Nói tóm lại, đừng đặt maintrong thư viện. Nó sẽ không hoạt động hoặc nó sẽ làm bạn bối rối khủng khiếp. Nhưng ủy thác hầu như tất cả các công việc của mình để một chức năng mà trong một lib, đó là thường hợp lý.
Donal Fellows

6

Chính nên ngắn vì cùng một lý do là bất kỳ chức năng nào cũng phải ngắn. Bộ não con người có một thời gian khó khăn để giữ một lượng lớn dữ liệu không liên kết trong bộ nhớ cùng một lúc. Chia nó thành các phần hợp lý để các nhà phát triển khác (cũng như chính bạn!) Dễ dàng tiêu hóa và suy luận về nó.

Và vâng, ví dụ của bạn là khủng khiếp và khó đọc, hãy để một mình duy trì.


Có, tôi luôn nghi ngờ rằng bản thân mã là khủng khiếp (mặc dù câu hỏi liên quan đến vị trí của mã, chứ không phải bản thân mã). Tôi sợ rằng tầm nhìn của tôi về Python đã bị phá hủy do đó ...
riwalk

1
@stargazer: Tôi không biết rằng bản thân mã này rất tệ, chỉ là nó không được tổ chức tốt cho con người. Điều đó nói rằng, có rất nhiều mã "xấu xí" ngoài kia hoạt động tốt và hoạt động tuyệt vời. Vẻ đẹp mã không phải là tất cả, mặc dù chúng ta nên luôn cố gắng hết sức để viết mã sạch nhất có thể.
Ed S.

ồ Đối với tôi, họ là một và giống nhau. Mã sạch có xu hướng ổn định hơn.
riwalk

Mã không khủng khiếp, chủ yếu là các trường hợp chuyển đổi và xử lý nhiều nền tảng. Chính xác những gì bạn tìm thấy khủng khiếp?
Francesco

@Francesco: Xin lỗi, "Khủng khiếp" từ góc độ bảo trì và dễ đọc, không phải là chức năng.
Ed S.

1

Một số người thích hơn 50 chức năng mà không làm gì khác, nhưng bao bọc một cuộc gọi đến chức năng khác. Tôi thà thích chức năng chính bình thường mà làm logic chương trình chính. Tất nhiên cấu trúc tốt.

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

Tôi không thấy bất kỳ lý do tại sao tôi nên bọc bất cứ thứ gì trong đó.

Đó hoàn toàn là một sở thích cá nhân.


1
Bởi vì nó là tài liệu. Bạn có thể viết mã theo cách này mà không cần (gần như) viết bình luận. Và khi bạn thay đổi mã, tài liệu sẽ tự động thay đổi :-).
Oliver Weiler

1

Thực hành tốt nhất của nó để giữ TẤT CẢ các chức năng của bạn ngắn, không chỉ chính. Tuy nhiên "ngắn" là chủ quan, nó phụ thuộc vào kích thước chương trình của bạn và ngôn ngữ bạn đang sử dụng.


0

Không có yêu cầu mainphải có bất kỳ độ dài nào, ngoài các tiêu chuẩn mã hóa. mainlà một chức năng như bất kỳ chức năng nào khác, và như vậy độ phức tạp của nó phải dưới 10 (hoặc bất cứ tiêu chuẩn mã hóa nào của bạn nói). Đó là nó, bất cứ điều gì khác là khá tranh luận.

biên tập

mainkhông nên ngắn Hoặc dài. Nó nên bao gồm các chức năng cần thiết để thực hiện dựa trên thiết kế của bạn và tuân thủ các tiêu chuẩn mã hóa.

Đối với mã cụ thể trong câu hỏi của bạn - vâng, nó thật xấu xí.

Như câu hỏi thứ hai của bạn - vâng, bạn đã sai . Di chuyển tất cả mã đó trở lại chính không cho phép bạn sử dụng nó theo kiểu thư viện bằng cách liên kết Py_Maintừ bên ngoài.

Bây giờ tôi đã rõ chưa?


Tôi không hỏi liệu nó thể dài được không. Tôi hỏi tại sao không nên dài.
riwalk

Độ phức tạp dưới 10 điểm? Có một đơn vị đo lường cho điều đó?
Donal Fellows

@ Stargazer712 Chiều dài chức năng thường được quy định bởi các tiêu chuẩn mã hóa. Đó là một vấn đề dễ đọc (và độ phức tạp, thường là các hàm dài được phân nhánh sao cho độ phức tạp cao hơn 20), và như tôi đã nói - mainkhông khác gì so với bất kỳ chức năng nào khác về vấn đề này.
littleadv

@Donal - có, nhấp vào liên kết.
littleadv

Tôi sẽ phải hạ bệ cái này. Bạn đang hoàn toàn thiếu mục đích của câu hỏi.
riwalk

0

Đây là một lý do thực dụng mới, quá ngắn so với GCC 4.6.1 Changelog :

Trên hầu hết các mục tiêu có hỗ trợ phần được đặt tên, các hàm chỉ được sử dụng khi khởi động (hàm tạo tĩnh và chính ), các hàm chỉ được sử dụng khi thoát và các hàm được phát hiện lạnh được đặt vào các phần phụ của đoạn văn bản riêng biệt . Điều này mở rộng tính năng -freorder-chức năng và được điều khiển bởi cùng một công tắc. Mục tiêu là cải thiện thời gian khởi động của các chương trình C ++ lớn.

Làm nổi bật thêm bởi tôi.


0

Đừng cho rằng chỉ vì một chút phần mềm là tốt, tất cả các mã đằng sau phần mềm đó đều tốt. Phần mềm tốt và mã tốt không giống nhau và ngay cả khi phần mềm tốt được hỗ trợ bởi mã tốt, không thể tránh khỏi trong một dự án lớn sẽ có những nơi tiêu chuẩn trượt.

Đó là một thực hành tốt để có một mainchức năng ngắn , nhưng đó thực sự chỉ là một trường hợp đặc biệt của quy tắc chung rằng tốt hơn là có các chức năng ngắn. Các chức năng ngắn dễ hiểu và dễ gỡ lỗi hơn cũng như tốt hơn trong việc gắn bó với kiểu thiết kế 'mục đích duy nhất' giúp các chương trình trở nên biểu cảm hơn. maincó lẽ là một nơi quan trọng hơn để tuân thủ quy tắc vì bất kỳ ai muốn hiểu chương trình đều phải hiểu maintrong khi các góc khó hiểu hơn của codebase có thể được truy cập ít thường xuyên hơn.

Tuy nhiên, cơ sở mã Python không đẩy mã ra để Py_Mainchơi quy tắc này mà vì bạn không thể xuất maintừ thư viện cũng như không gọi nó là hàm.


-1

Có một số câu trả lời kỹ thuật ở trên, hãy để nó qua một bên.

Một main nên ngắn vì nó phải là bootstrap. Chính nên khởi tạo một số lượng nhỏ các đối tượng, thường là một đối tượng, thực hiện công việc. Giống như bất cứ nơi nào khác, những vật thể đó phải được thiết kế tốt, gắn kết, ghép lỏng lẻo, đóng gói, ...

Mặc dù có thể có những lý do kỹ thuật để có một phương thức chính một dòng gọi một phương thức quái vật khác, nhưng về nguyên tắc, bạn đã đúng. Từ quan điểm kỹ thuật phần mềm, không có gì đạt được. Nếu sự lựa chọn là giữa một dòng chính gọi một phương thức quái vật, và chính nó là một phương thức quái vật, thì cái sau sẽ kém hơn một chút.


Bạn đang giả sử rằng "mã C ++ nên sử dụng các đối tượng và chỉ các đối tượng". Điều đó không đúng, C ++ là một ngôn ngữ đa âm và không ép buộc mọi thứ vào khuôn OO như một số ngôn ngữ khác.
Ben Voigt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.