What is the difference between
python -m path.to.file
and
python path/to/file.py
when executing python files ?
What is the difference between
python -m path.to.file
and
python path/to/file.py
when executing python files ?
The difference is where the script is looked up. Operating systems tend to have a set of paths where executable programs are found (e.g., PATH
) and a different set of directories where dynamic libraries are found (e.g., LIB
). The idea is that many programs can use the same libraries regardless of where the executable is.
Its the same thing with python. You execute a top level script always called "__main__"
and python will find the packages and modules you import in various places such its site-packages
directory. The general idea is that you install packages/modules into these special directories and top level scripts into a directory on the PATH
. The "shebang" (#!/usr/bin/env python3
) at the top of a file tells unix-like systems how to execute scripts and the file association tells Windows how to do it.
But there is a third way to do things. You can use python's module lookup logic to find a module but then execute it as a top level script. Now all of your programs are in python's special directories and you don't have to muck about with getting scripts into the system PATH
.
python path/to/file.py
will look up a script in the local file system using the relative or absolute path and run it as a script. Notice that you need to know the path.python -m path.to.file
will look up the module in the python namespace and run it as a script. You don't need to know the path. This works with any installed module and you don't have to know where it happens to be installed at the moment. The module could be in a zip file for all you know.The most important difference is that python -m path.to.file
uses the import system to locate the module path.to.file
, whereas python path/to/file.py
does not need to resolve the location at all because it was provided directly as a filename (script).
So, the best way to understand the difference is to concoct a situation where they are significantly different. In the case of python -m
usage, wherever the module "file" is eventually found depends on what's in sys.path
, and we can make it resolve here or there by modifying the order of traversal.
$ mkdir -p /tmp/path/to
$ mkdir -p /tmp/other/path/to
$ echo "print('hello')" > /tmp/path/to/file.py
$ echo "print('world')" > /tmp/other/path/to/file.py
$ PYTHONPATH=/tmp:/tmp/other python3 -m path.to.file
hello
$ PYTHONPATH=/tmp/other:/tmp python3 -m path.to.file
world
On the other hand, where python path/to/file.py
finds the code is unconditional: path/to/file.py
is executed directly and it doesn't matter what's on sys.path
.
There is another thing which is important to know about and beware of: the first element of sys.path
may be different depending on whether the interpreter was invoked with a script python path/to/file.py
or with python -m
.
sys.path
will be the directory which contains the script (e.g. /tmp/path/to
).-m
, the first element of sys.path
will be the current working directory.Therefore, where import statements resolve, or if they can be resolved at all, may be different depending on whether -m
was provided or not. This is particularly important in running a test suite (e.g. "pytest" vs "python -m pytest"), where you generally want to be sure you are testing against an installed version of the code, rather than whatever state the current working directory is in.
Other minor differences:
python -m path.to.file
may use cached bytecode file.pyc
if one exists already, and it will create one if it's non-existing or stale. python path/to/file.py
can not use a cache, and neither will it create a file.pyc
.python -m path.to.file
will have the global variable __package__
set to the string "path.to"
, since it is loading file
as a submodule of a package. python path/to/file.py
will have __package__
set to None
in the global namespace, since it's executed directly as a script and Python is unaware of any package structure in this case.Just quoting from documentation:
It will allow modules to be located using the Python module namespace for execution as scripts.
So basically you can get away with specifying the full path (provided a corresponding python file exists for you to be referenced) and it would be executed directly. In case a module name is provided, then it will be searched as per the sys.path
(provided they can be imported with import <module>
because otherwise you won't find it even using module namespace).
-m
module expects a module name. So in certain cases like built in modules or precompiled modules you can't use it but in other cases it will be searched in the sys.path
locations and will be executed as if we are running it with __main__
module.
Note also that package names are also allowed. (check the documentation for more details).
Here is the link and check the timeit
example.