Why import only at module level?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Paul Rubin

    Why import only at module level?

    That's what the Python style guides advise. They don't seem to like

    def frob(x):
    import re
    if re.search('sdf[234]xyz', x): ...

    instead preferring that you pollute your module's global namespace
    with the names of all your imports. What's the point of that? It
    gets worse when you want to do something like

    def dispatch(obj):
    from types import *
    if type(obj) == ListType: obj_list(obj)
    elif type(obj) == FunctionType: obj_fcn(obj)
    ...

    here you actually get a syntax warning that "from ... import *" is not
    ALLOWED except at the module level. So the pollution is really
    serious in that case, you're spewing all the names from the imported
    module into your own module. And I know that "import *" is considered
    uncool these days, but sometimes you really do want to just grab all
    those symbols, and the whole point of wanting the import inside the
    function is to contain the big import to a limited scope where they
    won't conflict with your own symbols.

    So what's the reason for the syntax warning, and for the convention of
    putting the imports at the top of the module?
  • Michael Hudson

    #2
    Re: Why import only at module level?

    Paul Rubin <http://phr.cx@NOSPAM.i nvalid> writes:
    [color=blue]
    > That's what the Python style guides advise. They[/color]
    ^^^^
    I do wish people would stop using this word in this sort of context...
    [color=blue]
    > don't seem to like
    >
    > def frob(x):
    > import re
    > if re.search('sdf[234]xyz', x): ...
    >
    > instead preferring that you pollute your module's global namespace
    > with the names of all your imports. What's the point of that?[/color]

    What's the problem with that?
    [color=blue]
    > It gets worse when you want to do something like
    >
    > def dispatch(obj):
    > from types import *
    > if type(obj) == ListType: obj_list(obj)
    > elif type(obj) == FunctionType: obj_fcn(obj)
    > ...
    >
    > here you actually get a syntax warning that "from ... import *" is not
    > ALLOWED except at the module level.[/color]

    The problem HERE is for nested scopes; it's impossible to know in

    def f():
    from baz import *
    def g():
    return y

    where y is coming from (i.e. is it a captured binding or a global).
    [color=blue]
    > So what's the reason for the syntax warning, and for the convention of
    > putting the imports at the top of the module?[/color]

    I think putting imports at the top is just so you can find them
    easily, and at a glance see which modules a given module imports.

    Cheers,
    mwh

    --
    "The future" has arrived but they forgot to update the docs.
    -- R. David Murray, 9 May 2000

    Comment

    • Peter Hansen

      #3
      Re: Why import only at module level?

      Paul Rubin wrote:[color=blue]
      >
      > That's what the Python style guides advise. They don't seem to like
      >
      > def frob(x):
      > import re
      > if re.search('sdf[234]xyz', x): ...
      >
      > instead preferring that you pollute your module's global namespace
      > with the names of all your imports. What's the point of that?[/color]

      Maintainability . It's also well understood that there are potential
      benefits to the approach you show above, late-loading of code being
      one of them (which can improve startup time for certain apps), but
      as maintainability should almost always be the primary consideration,
      the standard advice is to put stuff together at the top where it's
      clear to maintainers which modules are used in the code (in other
      words, what a given module is coupled to).

      -i've-been-playing-the-esp-game-so-i-channeled-the-style-guide-authors
      -Peter

      Comment

      • François Pinard

        #4
        Re: Why import only at module level?

        [Peter Hansen][color=blue]
        > Paul Rubin wrote:[/color]
        [color=blue][color=green]
        > > That's what the Python style guides advise. They don't seem to like[/color][/color]
        [color=blue][color=green]
        > > def frob(x):
        > > import re
        > > if re.search('sdf[234]xyz', x): ...[/color][/color]
        [color=blue][color=green]
        > > instead preferring that you pollute your module's global namespace
        > > with the names of all your imports. What's the point of that?[/color][/color]
        [color=blue]
        > Maintainability . It's also well understood that there are potential
        > benefits to the approach you show above, late-loading of code being
        > one of them (which can improve startup time for certain apps), but
        > as maintainability should almost always be the primary consideration,
        > the standard advice is to put stuff together at the top where it's
        > clear to maintainers which modules are used in the code (in other
        > words, what a given module is coupled to).[/color]

        I never understood this "standard advice", nor how it is related to
        maintainability . What makes a module significantly more maintainable
        by merely grouping `import' statements at the beginning? What is it so
        crucial to know that the `re' module is used, or not, in a program? It
        looks like a tiny detail to me.

        If for some strange reason I urgently needed to know everything that a
        program imports, I guess I would then `grep' the source for the word
        `import'i, or just search with an editor! :-) This is surely not a need
        I often have, and for from enough for justifying the convention.

        If within a `def', I will often use a module which I do not use
        elsewhere in a program, there is no reason to make it global. Global
        variables should be avoided on the average, and moreover, Python is
        faster at accessing a local than a global.

        I'm not really crusading for either method, but maybe a bit against
        the mere existence of the "standard advice", unless it acquires some
        better justification. Of course, "maintainabilit y" is a virtue, but the
        relation between maintainability and the "standard advice" is asserted
        as if it were to be evident, without explanation.

        --
        François Pinard http://www.iro.umontreal.ca/~pinard

        Comment

        • John Roth

          #5
          Re: Why import only at module level?


          "Paul Rubin" <http://phr.cx@NOSPAM.i nvalid> wrote in message
          news:7xoervxwxt .fsf@ruckus.bro uhaha.com...[color=blue]
          > That's what the Python style guides advise. They don't seem to like
          >
          > def frob(x):
          > import re
          > if re.search('sdf[234]xyz', x): ...
          >
          > instead preferring that you pollute your module's global namespace
          > with the names of all your imports. What's the point of that? It
          > gets worse when you want to do something like
          >
          > def dispatch(obj):
          > from types import *
          > if type(obj) == ListType: obj_list(obj)
          > elif type(obj) == FunctionType: obj_fcn(obj)
          > ...
          >
          > here you actually get a syntax warning that "from ... import *" is not
          > ALLOWED except at the module level. So the pollution is really
          > serious in that case, you're spewing all the names from the imported
          > module into your own module. And I know that "import *" is considered
          > uncool these days, but sometimes you really do want to just grab all
          > those symbols, and the whole point of wanting the import inside the
          > function is to contain the big import to a limited scope where they
          > won't conflict with your own symbols.
          >
          > So what's the reason for the syntax warning, and for the convention of
          > putting the imports at the top of the module?[/color]

          The reason to put imports at the top of the module
          is that, in general, imported modules are global resources. That's
          also why it's not an issue to insert them in the module level
          namespace.

          Also, there are certain very common program tasks, such
          as creating a subclass, where the superclass has to be
          available at module load time. If it's in another module, that
          module has to be imported before defining the class.

          That said, if a module isn't a global resource then there's
          no particular reason to put the symbol in the module
          symbol table. I use __import__ in my version of FIT
          to import fixtures into a dictionary, for example. I prefer
          that technique to attempting to exec a constructed
          import statement precisely because I don't have any
          control over the names of the imported modules.

          From foobar import * is a specialized case that
          should be used only when you know what the module
          is going to import. If you don't want the scatter shot
          loading, then just import foobar, and reference foobar.whatever
          whenever you need a symbol. No one is holding an
          assault weapon to your head to make you use import *
          after all.

          If you're a control freak (or your program analysis tools
          barf on import *), you can always put assignments
          for the symbols you're going to use after the imports.
          That makes it explicit.

          John Roth



          Comment

          • Peter Hansen

            #6
            Re: Why import only at module level?

            François Pinard wrote:[color=blue]
            >
            > [Peter Hansen][color=green]
            > > Maintainability . It's also well understood that there are potential
            > > benefits to the approach you show above, late-loading of code being
            > > one of them (which can improve startup time for certain apps), but
            > > as maintainability should almost always be the primary consideration,
            > > the standard advice is to put stuff together at the top where it's
            > > clear to maintainers which modules are used in the code (in other
            > > words, what a given module is coupled to).[/color]
            >
            > I never understood this "standard advice", nor how it is related to
            > maintainability . What makes a module significantly more maintainable
            > by merely grouping `import' statements at the beginning? What is it so
            > crucial to know that the `re' module is used, or not, in a program? It
            > looks like a tiny detail to me.[/color]

            "re" is not a good example of the above, though there might also be
            very good but different reasons to import re at the top. Often it
            is used in several different places, and that would mean duplication
            (and a tiny waste of time) if you always imported it at the beginning
            of each method, or even just before it was used.

            A better example is application-specific modules. "Hiding" those down
            in the individual methods that use them makes it much more difficult
            to see the coupling between modules.

            More coupling means less maintainability .

            Second reason: if you put your import, which you use in only one place,
            locally in the method where it's used, then modify the code so that
            another method also uses the module, you will end up with two imports.

            More duplication means less maintainability .
            [color=blue]
            > If within a `def', I will often use a module which I do not use
            > elsewhere in a program, there is no reason to make it global. Global
            > variables should be avoided on the average, and moreover, Python is
            > faster at accessing a local than a global.[/color]

            As you probably know, I almost never put performance anywhere near the
            level I put other considerations, so if we still differ on this matter,
            perhaps it's because of different values.

            -Peter

            Comment

            • François Pinard

              #7
              Re: Why import only at module level?

              [Peter Hansen][color=blue]
              > François Pinard wrote:[/color]
              [color=blue]
              > A better example is application-specific modules. "Hiding" those down
              > in the individual methods that use them makes it much more difficult
              > to see the coupling between modules.[/color]

              Agreed indeed, the coupling between modules is less explicit then.
              [color=blue]
              > More coupling means less maintainability .[/color]

              On the other hand, the location of `import' has no effect on the amount
              of coupling.
              [color=blue]
              > Second reason: if you put your import, which you use in only one
              > place, locally in the method where it's used, then modify the code so
              > that another method also uses the module, you will end up with two
              > imports. More duplication means less maintainability .[/color]

              Maybe not. I may have many functions each having a local counter, and
              despite all the duplication, using a single global counter instead would
              not imply more maintainability . Locality (of definition and use) is
              often best for maintainability , even if it means random duplication.

              There is no definitive rule about what is good or bad, and this is where
              good taste comes in, which turns all this program writing into an art!
              [color=blue][color=green]
              > > Global variables should be avoided on the average, and moreover,
              > > Python is faster at accessing a local than a global.[/color][/color]
              [color=blue]
              > As you probably know, I almost never put performance anywhere near the
              > level I put other considerations, so if we still differ on this matter,
              > perhaps it's because of different values.[/color]

              Performance is not to be a primary concern on the average, quite
              agreed! Still, a few simple habits are easily acquired that yield
              faster programs on average, which happily coincide with good programming
              practices. I consider this coincidence as a reason to rejoice! :-)

              --
              François Pinard http://www.iro.umontreal.ca/~pinard

              Comment

              • Peter Hansen

                #8
                Re: Why import only at module level?

                François Pinard wrote:[color=blue]
                >
                > [Peter Hansen][color=green]
                > > More coupling means less maintainability .[/color]
                >
                > On the other hand, the location of `import' has no effect on the amount
                > of coupling.[/color]

                I think I can make a good argument that it does. Try this on for size:
                if the import of module B is in several methods of module A, then each of
                those methods is directly coupled to module B. You've got several
                instances of coupling. Now move the duplicate imports up to the top
                of module A. Each method is now coupled only to module A, which of
                course all methods in that module are, but only module A has any
                coupling to module B.

                When I analyze coupling, I consider the links to have both strength
                and quantity. Generally a reduction in quantity is just as useful
                as a reduction in strength of coupling, IME.
                [color=blue][color=green]
                > > Second reason: if you put your import, which you use in only one
                > > place, locally in the method where it's used, then modify the code so
                > > that another method also uses the module, you will end up with two
                > > imports. More duplication means less maintainability .[/color]
                >
                > Maybe not. I may have many functions each having a local counter, and
                > despite all the duplication, using a single global counter instead would
                > not imply more maintainability . Locality (of definition and use) is
                > often best for maintainability , even if it means random duplication.[/color]

                Unfortunately you lost me with the example. I can't see the connection
                between having separate local counters and a single global counter,
                since presumably the logic requires either one or the other. (I trust
                you had a valid point but I missed it, sorry.)
                [color=blue]
                > There is no definitive rule about what is good or bad, and this is where
                > good taste comes in, which turns all this program writing into an art![/color]

                Very true of course, as with all design.

                -Peter

                Comment

                • François Pinard

                  #9
                  Re: Why import only at module level?

                  [Peter Hansen]
                  [color=blue]
                  > When I analyze coupling, I consider the links to have both strength
                  > and quantity. Generally a reduction in quantity is just as useful
                  > as a reduction in strength of coupling, IME.[/color]

                  This sounds all quite reasonable! :-)
                  [color=blue]
                  > Unfortunately you lost me with the example. [...] (I trust you had a
                  > valid point but I missed it, sorry.)[/color]

                  Thanks for the confidence! :-) Let me not insist with the example, as
                  I'm not trying to make a point here :-). As long as we agree to seek for
                  aesthetic programs, the methods and tastes of the various artists may
                  differ, and the diversity is one more source of interest. Keep happy!

                  --
                  François Pinard http://www.iro.umontreal.ca/~pinard

                  Comment

                  • Steve

                    #10
                    Re: Why import only at module level?

                    Michael Hudson wrote:
                    [color=blue][color=green]
                    >> def frob(x):
                    >> import re
                    >> if re.search('sdf[234]xyz', x): ...
                    >>
                    >>instead preferring that you pollute your module's global namespace
                    >>with the names of all your imports. What's the point of that?[/color]
                    >
                    > What's the problem with that?[/color]

                    (1) It breaks code encapsulation. Well, perhaps
                    "breaks" is too strong a word, but it makes data hiding
                    more difficult. Unless you jump through hoops to remove
                    all traces of the objects you import, modules that you
                    import will be visible to those that import you.

                    (2) It is messy. The whole point of nested scopes is to
                    make objects visible only to the objects that need
                    them. If only one function needs the import, why force
                    it to be visible to all the others?

                    (3) There was clearly a need (or at least a wish) to be
                    able to hide imported objects from importing modules.
                    The usual way of doing this is:

                    from spam import public as _notpublic

                    Making the import local to the calling function works
                    well too.

                    I sometimes delete the binding when I'm done:

                    from spam import public
                    # use public in here
                    del public

                    although there are disadvantages to this method.



                    [snip][color=blue]
                    > The problem HERE is for nested scopes; it's
                    > impossible to know in
                    >
                    > def f():
                    > from baz import *
                    > def g():
                    > return y
                    >
                    > where y is coming from (i.e. is it a captured[/color]
                    binding > or a global).

                    Why is this any different from the following top-level
                    code:

                    # module baz:
                    y = "spam spam spam"
                    [color=blue][color=green][color=darkred]
                    >>> y= "I don't like spam"
                    >>> from baz import *[/color][/color][/color]

                    Where is the binding y coming from? The question of
                    whether this code is at the top-level or in a function
                    definition is a red herring.


                    It seems to me that forbidding imports except at the
                    top level is an arbitrary restriction. Using import
                    insided function definitions might not work for some
                    people, but it works for others.



                    --
                    Steven D'Aprano


                    Comment

                    Working...