[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