[Python-ideas] breaking out of module execution
Steven D'Aprano
steve at pearwood.info
Wed Apr 25 02:40:32 CEST 2012
M.-A. Lemburg wrote:
> Antoine Pitrou wrote:
>> I think good practice should lead you to put your initialization code
>> in a dedicated function that you call from your module toplevel. In
>> this case, breaking out of execution is a matter of adding a return
>> statement.
>
> True, but that doesn't prevent import from being run, functions and
> classes from being defined and resources being bound which are not
> going to get used.
Premature micro-optimizations. Just starting the interpreter runs imports,
defines functions and classes, and binds resources which your script may never
use. Lists and dicts are over-allocated, which you may never need. Ints and
strings are boxed. Python is a language that takes Knuth seriously:
"We should forget about small efficiencies, say about 97% of the time:
premature optimization is the root of all evil."
In the majority of cases, a few more small inefficiencies, especially those
that are one-off costs at startup, are not a big deal.
In those cases where it is a big deal, you can place code inside if-blocks,
factor it out into separate modules, or use delayed execution by putting it
inside functions. It may not be quite so pretty, but it gets the job done, and
it requires no new features.
> Think of code like this (let's assume the "break" statement is used
> for stopping module execution):
>
> """
> #
> # MyModule
> #
>
> ### Try using the fast variant
>
> try:
> from MyModule_C_Extension import *
> except ImportError:
> pass
> else:
> # Stop execution of the module code object right here
> break
>
> ### Ah, well, so go ahead with the slow version
>
> import os, sys
> from MyOtherPackage import foo, bar, baz
>
> class MyClass:
> ...
>
> def MyFunc(a,b,c):
> ...
>
> def main():
> ...
>
> if __name__ == '__main__':
> main()
> """
>
> You can solve this by using two separate modules and a top-level
> module to switch between the implementations, but that's cumbersome
> if you have more than just a few of such modules in a package.
You can also solve it by defining the slow version first, as a fall-back, then
replace it with the fast version, if it exists:
import os, sys
from MyOtherPackage import foo, bar, baz
class MyClass: ...
def MyFunc(a,b,c): ...
def main(): ...
try:
from MyModule_C_Extension import *
except ImportError:
pass
if __name__ == '__main__':
main()
The advantage of this is that MyModule_C_Extension doesn't need to supply
everything or nothing. It may only supply (say) MyClass, and MyFunc will
naturally fall back on the pre-defined Python versions.
This is the idiom used by at least the bisect and pickle modules, and I think
it is the Pythonic way.
--
Steven
More information about the Python-ideas
mailing list