Search code examples
pythonrubyprogramming-languageslanguage-featureslanguage-design

What are the advantages and disadvantages of the require vs. import methods of loading code?


Ruby uses require, Python uses import. They're substantially different models, and while I'm more used to the require model, I can see a few places where I think I like import more. I'm curious what things people find particularly easy — or more interestingly, harder than they should be — with each of these models.

In particular, if you were writing a new programming language, how would you design a code-loading mechanism? Which "pros" and "cons" would weigh most heavily on your design choice?


Solution

  • The Python import has a major feature in that it ties two things together -- how to find the import and under what namespace to include it.

    This creates very explicit code:

    import xml.sax

    This specifies where to find the code we want to use, by the rules of the Python search path.

    At the same time, all objects that we want to access live under this exact namespace, for example xml.sax.ContentHandler.

    I regard this as an advantage to Ruby's require. require 'xml' might in fact make objects inside the namespace XML or any other namespace available in the module, without this being directly evident from the require line.

    If xml.sax.ContentHandler is too long, you may specify a different name when importing:

    import xml.sax as X
    

    And it is now avalable under X.ContentHandler.

    This way Python requires you to explicitly build the namespace of each module. Python namespaces are thus very "physical", and I'll explain what I mean:

    • By default, only names directly defined in the module are available in its namespace: functions, classes and so.
    • To add to a module's namespace, you explicitly import the names you wish to add, placing them (by reference) "physically" in the current module.

    For example, if we have the small Python package "process" with internal submodules machine and interface, and we wish to present this as one convenient namespace directly under the package name, this is and example of what we could write in the "package definition" file process/__init__.py:

    from process.interface import *
    from process.machine import Machine, HelperMachine
    

    Thus we lift up what would normally be accessible as process.machine.Machine up to process.Machine. And we add all names from process.interface to process namespace, in a very explicit fashion.

    The advantages of Python's import that I wrote about were simply two:

    • Clear what you include when using import
    • Explicit how you modify your own module's namespace (for the program or for others to import)