为什么Python只编译模块而不编译正在运行的脚本?

为什么Python只编译模块而不编译正在运行的脚本?,python,Python,为什么Python编译脚本中使用的库,而不是被调用的脚本本身 比如说, 如果存在main.py和module.py,并且Python是通过执行Python main.py来运行的,则会有一个编译文件module.pyc,但不会有一个用于main的文件。为什么? 编辑 增加赏金。我认为这个问题没有得到正确的回答 如果响应是对main.py目录的潜在磁盘权限,Python为什么要编译模块?它们同样可能(如果不是更可能的话)出现在用户没有写访问权限的位置。Python可以编译main,如果它是可写的,

为什么Python编译脚本中使用的库,而不是被调用的脚本本身

比如说,

如果存在
main.py
module.py
,并且Python是通过执行
Python main.py
来运行的,则会有一个编译文件
module.pyc
,但不会有一个用于main的文件。为什么?

编辑

增加赏金。我认为这个问题没有得到正确的回答

  • 如果响应是对
    main.py
    目录的潜在磁盘权限,Python为什么要编译模块?它们同样可能(如果不是更可能的话)出现在用户没有写访问权限的位置。Python可以编译
    main
    ,如果它是可写的,或者在另一个目录中编译

  • <> LI>

    如果原因是利益将是最小的,考虑脚本将被大量使用的情况(例如在CGI应用程序中)。p>


    因为正在运行的脚本可能不适合生成.pyc文件,例如
    /usr/bin

    从.pyc或.pyo文件读取程序时,其运行速度不会比从.py文件读取程序时快;.pyc或.pyo文件唯一更快的地方是加载它们的速度

    这对于为主脚本生成.pyc文件是不必要的。只应编译可能多次加载的库

    已编辑

    看来你没有明白我的意思。首先,了解编译成
    .pyc
    文件的整个思想是为了使同一个文件在第二次执行时执行得更快。但是,请考虑Python是否编译了正在运行的脚本。解释器将在第一次运行时将字节码写入
    .pyc
    文件,这需要时间。所以它甚至会运行得慢一点。你可能会争辩说,它会运行得更快。嗯,这只是一个选择。另外,正如
    所说的那样:

    显式比隐式好


    如果希望通过使用
    .pyc
    文件来加速,则应手动编译并显式运行
    .pyc
    文件。

    文件在导入时编译。这不是安全问题。很简单,如果导入它,python会保存输出。见Fredrik Lundh关于Effbot的文章

    >>>import main
    # main.pyc is created
    
    运行脚本时,python将使用*.pyc文件。 如果您有其他原因需要预编译脚本,则可以使用该模块

    汇编语言用法 问题的答案编辑
  • 如果响应是对
    main.py
    目录的潜在磁盘权限,Python为什么要编译模块

    模块和脚本的处理方式相同。导入会触发要保存的输出

  • 如果原因是利益将是最小的,考虑脚本将被大量使用的情况(例如在CGI应用程序中)。p> 使用compileall并不能解决这个问题。 除非显式调用,否则python执行的脚本不会使用

    *.pyc
    。这有消极的副作用,这一点在中得到了很好的说明

    给出的CGI应用程序示例应该通过使用以下技术来解决:。如果您希望消除编译脚本的开销,那么您可能也希望消除启动python的开销,更不用说数据库连接开销了

    可以使用轻引导脚本,甚至可以使用python-c“导入脚本”
  • ,但这些脚本的风格有问题


    提供了一些更正和改进此答案的灵感。

    要回答您的问题,请参考Python官方文档中的

    当通过在命令行中指定脚本名称来运行脚本时,该脚本的字节码永远不会写入.pyc或.pyo文件。因此,可以通过将脚本的大部分代码移动到一个模块并使用一个导入该模块的小引导脚本来缩短脚本的启动时间。也可以直接在命令行上命名.pyc或.pyo文件


    似乎没有人想这么说,但我很确定答案很简单:这种行为没有确凿的理由

    到目前为止给出的所有理由基本上都是错误的:

    • 主文件没有什么特别之处。它作为一个模块加载,并像任何其他模块一样显示在
      sys.modules
      中。运行主脚本无非是用模块名
      \uuuuu main\uuuu
      导入它
    • 由于只读目录,无法保存.pyc文件没有问题;Python只是忽略了它,继续前进
    • 缓存脚本的好处与缓存任何模块的好处相同:不会浪费时间在每次运行脚本时重新编译脚本。文档明确承认这一点(“因此,脚本的启动时间可能会缩短…”)
    另一个需要注意的问题是:如果您运行
    python foo.py
    并且foo.pyc存在,则不会使用它。您必须明确地说
    pythonfoo.pyc
    。这是一个非常糟糕的想法:这意味着当.pyc文件不同步时,Python不会自动重新编译该文件(由于.py文件发生了更改),因此在手动重新编译之前,不会使用对.py文件的更改。如果您升级Python,并且.pyc文件格式不再兼容(这是经常发生的),它也会完全失败,并出现运行时错误。通常,这一切都是透明处理的

    您不需要将脚本移动到虚拟模块并设置引导脚本来欺骗Python缓存它。这是一个很老套的解决办法

    我能想出的唯一可能(也是非常不令人信服)的理由是避免你的主目录被一堆.pyc文件弄得乱七八糟。(这不是真正的原因;如果这是一个实际问题,那么.pyc文件应该保存为dotfiles。)当然没有理由不选择这样做

    Python绝对应该是
    python -m compileall .
    
    python -m compileall --help
    option --help not recognized
    usage: python compileall.py [-l] [-f] [-q] [-d destdir] [-x regexp] [directory ...]
    -l: don't recurse down
    -f: force rebuild even if timestamps are up-to-date
    -q: quiet operation
    -d destdir: purported directory name for error messages
       if no directory arguments, -l sys.path is assumed
    -x regexp: skip files matching the regular expression regexp
       the regexp is searched for in the full path of the file
    
    static PyObject *
    load_source_module(char *name, char *pathname, FILE *fp)
    {
        // snip...
    
        if (/* Can we read a .pyc file? */) {
            /* Then use the .pyc file. */
        }
        else {
            co = parse_source_module(pathname, fp);
            if (co == NULL)
                return NULL;
            if (Py_VerboseFlag)
                PySys_WriteStderr("import %s # from %s\n",
                    name, pathname);
            if (cpathname) {
                PyObject *ro = PySys_GetObject("dont_write_bytecode");
                if (ro == NULL || !PyObject_IsTrue(ro))
                    write_compiled_module(co, cpathname, &st);
            }
        }
        m = PyImport_ExecCodeModuleEx(name, (PyObject *)co, pathname);
        Py_DECREF(co);
    
        return m;
    }
    
    static PyCodeObject *
    parse_source_module(const char *pathname, FILE *fp)
    {
        PyCodeObject *co = NULL;
        mod_ty mod;
        PyCompilerFlags flags;
        PyArena *arena = PyArena_New();
        if (arena == NULL)
            return NULL;
    
        flags.cf_flags = 0;
    
        mod = PyParser_ASTFromFile(fp, pathname, Py_file_input, 0, 0, &flags, 
                       NULL, arena);
        if (mod) {
            co = PyAST_Compile(mod, pathname, NULL, arena);
        }
        PyArena_Free(arena);
        return co;
    }
    
    PyObject *
    PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals,
              PyObject *locals, int closeit, PyCompilerFlags *flags)
    {
        PyObject *ret;
        mod_ty mod;
        PyArena *arena = PyArena_New();
        if (arena == NULL)
            return NULL;
    
        mod = PyParser_ASTFromFile(fp, filename, start, 0, 0,
                       flags, NULL, arena);
        if (closeit)
            fclose(fp);
        if (mod == NULL) {
            PyArena_Free(arena);
            return NULL;
        }
        ret = run_mod(mod, filename, globals, locals, flags, arena);
        PyArena_Free(arena);
        return ret;
    }
    
    static PyObject *
    run_mod(mod_ty mod, const char *filename, PyObject *globals, PyObject *locals,
         PyCompilerFlags *flags, PyArena *arena)
    {
        PyCodeObject *co;
        PyObject *v;
        co = PyAST_Compile(mod, filename, flags, arena);
        if (co == NULL)
            return NULL;
        v = PyEval_EvalCode(co, globals, locals);
        Py_DECREF(co);
        return v;
    }
    
    int
    run_script(fp, filename)
        FILE *fp;
        char *filename;
    {
        object *m, *d, *v;
        m = add_module("`__main__`");
        if (m == NULL)
            return -1;
        d = getmoduledict(m);
        v = run_file(fp, filename, file_input, d, d);
        flushline();
        if (v == NULL) {
            print_error();
            return -1;
        }
        DECREF(v);
        return 0;
    }