views:

98

answers:

4

Let's say I have 3 files:

a.py

from d import d

class a:
    def type(self):
        return "a"
    def test(self):
        try:
            x = b()
        except:
            print "EXCEPT IN A"
            from b import b
            x = b()
        return x.type()

b.py

import sys

class b:
    def __init__(self):
        if "a" not in sys.modules:
            print "Importing a!"
            from a import a
        pass
    def type(self):
        return "b"
    def test(self):
        for modules in sys.modules:
            print modules
        x = a()
        return x.type()

c.py

from b import b
import sys

x = b()
print x.test()

and run python c.py

Python comes back complaining:

NameError: global name 'a' is not defined

But, a IS in sys.modules:

copy_reg
sre_compile
locale
_sre
functools
encodings
site
__builtin__
operator
__main__
types
encodings.encodings
abc
errno
encodings.codecs
sre_constants
re
_abcoll
ntpath
_codecs
nt
_warnings
genericpath
stat
zipimport
encodings.__builtin__
warnings
UserDict
encodings.cp1252
sys
a
codecs
os.path
_functools
_locale
b
d
signal
linecache
encodings.aliases
exceptions
sre_parse
os

And I can alter b.py such that:

x = a()
changes to
x = sys.modules["a"].a()

And python will happily run that.

A couple questions arise from this:

Why does python say it doesn't know what a is, when it is in sys.modules?
Is using sys.modules a "proper" way to access class and function definitions?
What is the "right" way to import modules?
ie from module import x
or
import module

+3  A: 

I guess it's a problem of scoping, if you import a module in your constructor you can only use it in your constructor, after the import statement.

Claude Vedovini
A: 

It is bad style to conditionally import code modules based on program logic. A name should always mean the same thing everywhere in your code. Think about how confusing this would be to debug:

if (something)
  from office import desk
else
  from home import desk

... somewhere later in the code...
desk()

Even if you don't have scoping issues (which you most likely will have), it's still confusing.

Put all your import statements at the top of your file. That's where other coders will look for them.

As far as whether to use "from foo import bar" verses just "import foo", the tradeoff is more typing (having to type "foo.bar()" or just type "bar()") verses clearness and specificity. If you want your code to be really readable and unambiguous, just say "import foo" and fully specify the call everywhere. Remember, it's much harder to read code than it is to write it.

eeeeaaii
Actually, when you have an optional dependency, this can be a really good feature. It's used all over the Python standard library. The main thing is to make sure that whatever tokens you're declaring (`desk` in this case) need to be "equivalent" in some sense. That is, you're either going to use the `desk` implementation from `office`, or the `desk` implementation from `home`, but they're both desks, so you can do the same things with either one of them.
Daniel Pryden
The only good thing I can see about creating an optional dependency is that it is a performance hack. You could always import all dependencies no matter what features the user needs, but it involves using more memory than you might like to use, and it might slow down performance. That's probably why they use optional imports in the standard library. Performance hacks usually muck up code and make it less readable, this being no exception. The clearest thing is to always put imports at the top of your file whenever you can -- this holds true in almost all of the programming languages I know.
eeeeaaii
+1  A: 

In your case, a is in sys.modules.. but not everything in sys.modules is in b's scope. If you want to use re, you'd have to import that as well.

Conditional importing is occasionally acceptable, but this isn't one of those occasions. For one thing, the circular dependency between a and b in this case is unfortunate, and should be avoided (lots of patterns for doing so in Fowler's Refactoring).. That said, there's no need to conditionally import here.

b ought to simply import a. What were you trying to avoid by not importing it directly at the top of the file?

royal
I was doing some testing and forgot to change the original code back. Originally I had a import b and b import a. Which this is why I moved it as a conditional import.
Nathan Adams
+1  A: 

According to the Python documentation,

Import statements are executed in two steps: (1) find a module, and initialize it if necessary; (2) define a name or names in the local namespace (of the scope where the import statement occurs).

So the problem is that even though module a has been imported, the name a has only been bound in the scope of the b.__init__ method, not the entire scope of b.py. So in the b.test method, there is no such name a, and so you get a NameError.

You might want to read this article on importing Python modules, as it helps to explain best practices for working with imports.

Daniel Pryden