我已经进行了9年以上的编程工作,并且根据我的第一位编程老师的建议,我始终将我的main()
函数保持得非常简短。
起初我不知道为什么。我只是听从而不理解,这让我的教授们非常高兴。
在获得经验之后,我意识到,如果我正确地设计了代码,则具有短main()
函数的情况就会发生。编写模块化代码并遵循单一职责原则,使我的代码可以“分批”设计,并且main()
无非是推动程序运行的催化剂。
快到几周前,我正在查看Python的源代码,然后找到了该main()
函数:
/* Minimal main program -- everything is loaded from the library */
...
int
main(int argc, char **argv)
{
...
return Py_Main(argc, argv);
}
是的python。短main()
函数==良好的代码。
编程老师是对的。
想深入了解一下,我看了一下Py_Main。整个定义如下:
/* 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;
}
全能的好上帝...它足以沉没泰坦尼克号。
似乎Python做了“编程入门101”的技巧,只是将所有main()
的代码移到了一个不同的函数中,该函数与“ main”非常相似。
这是我的问题:这段代码编写得很糟糕,还是有其他原因使main函数简短?
就目前而言,我认为这样做与将代码Py_Main()
移回绝对没有区别main()
。我在想这个吗?
options = ParseOptionFlags(argc,argv)
其中options
是struct
包含变量Py_BytesWarningFlag
,Py_DebugFlag
等等...