1

this is something that I can't quite figure out in pythion's imports. Let's say I have a module 'a' that imports module 'b' with 'import b' Then, there is a module 'c' that imports module 'a'. Will the names form the module 'b' be available in 'c'?

I've checked that it actually depends on how you import the module 'a' in module 'c'. If you do 'import a' then names from 'b' will not be available in 'c'. However, if you do 'from a import *' then they will be available. Can someone pls clarify the difference?

0

3 Answers 3

4

If you think through what these commands actually do, it's pretty simple.

a.py:

import b
foo = 0

b.py:

bar = 1

c.py:

import a

From c, you can't just say foo, you have to say a.foo. The same is true for every name in a—constants, variables, functions, classes, and even modules. And that includes b.

So, you can't say bar or a.bar, but you can say a.b.bar.

Now, what if you change it like this:

a.py:

from b import *
foo = 0

b.py:

bar = 1

c.py:

from a import *

What that from b import * does is take everything in b's namespace, and puts it into a's namespace, so you can directly say bar from within a. And then, when you do from a import *, that takes everything in a's namespace, and puts it into c's, so just as you can do foo, you can also do bar.

This is why you usually don't want to do from b import * anywhere except in your top-level script—because your namespaces get all merged together.

It's also why you can restrict what gets picked up by * with __all__:

a.py:

from b import *
__all__ = ['foo']
foo = 0

b.py:

bar = 1

c.py:

from a import *

Now, from c, you can do c.foo, but not c.bar.

So, a can do from b import * to implement its own features, without exposing all of b's internals to its users.

Sign up to request clarification or add additional context in comments.

8 Comments

+1 because I didn't know about __all__.
"So, you can't say bar or a.bar, but you can say a.b.bar" I'm getting 'Object a has no attribute b' when trying this.
@spoonboy: Yes, I can say a.b.bar, I just can't say it 10 times fast. (Even though I just realized it's pretty close to the start of my own username…)
Ok, this works. Could you pls clarify why it doesn't work when I import module b with 'from b import *' ? (but use 'import a' in 'c')
As well, by some reason it doesn't work with full path names. I.e. let's say all the three modules belong to the same package 'p'. The imports would be 'import p.b' in 'a' and 'import p.a' in c. When I try p.b.p.a.bar in 'c'it produces an error.
|
3

The import statement causes a module to be executed, with all variables being kept in the namespace of the module executed. That is to say, if you import a, then all of a's variables will be under a.[variable]. The from keyword gives slightly different behavior: it puts the variable in the current namespace. For instance, from a import foo puts the variable foo in the current namespace. from a import * imports all variables from a into the current namespace. The as keyword allows you to rename variables when you import them; thus from a import foo as bar allows you to access a.foo, but you must call it bar; import a.foo as foo is equivalent to from a import foo.

Comments

1

A more concrete example than above:

a.py

import b
bar = 'bar'

b.py

foo = 'foo'

c.py

import a


try:
   print(b.foo)
except:
   print('No `b` in current namespace')

try:
   print(a.bar)
except:
   print('No `a` in current namespace')

try:
   print(a.b.foo)
except:
   print('No `a` or `a.b` in current namespace')

This should print (in order):

No `b` in current namespace
bar
foo

In other words, b is available through a but only by accessing a's namespace

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.