6

How do the following two statements differ and what are the consequences of each?

Import as:

from module.submodule import someclass as myclass

Assign to a variable:

from module.submodule import someclass
myclass = someclass
4
  • Effectively the same thing. Commented Jul 30, 2015 at 6:18
  • 1
    There are differences, check the bytecode. Commented Jul 30, 2015 at 6:19
  • Same thing, u are assigning it to a variable in global space of your current script Commented Jul 30, 2015 at 6:19
  • PyCharm for one cannot show me Quick definition or Go to source, when I use the second approach (Assignment to variable), but it works fine when I use the first one. Commented Jul 30, 2015 at 6:20

2 Answers 2

5

The bytecode output given here is for Python 3.4, but the code to produce the bytecode should work on any version and the same general principles apply.

Harness:

from dis import dis

def compile_and_dis(src):
    dis(compile(src, '<string>', 'exec'))

Case 1:

>>> compile_and_dis('from module.submodule import someclass as myclass')
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               1 (('someclass',))
              6 IMPORT_NAME              0 (module.submodule)
              9 IMPORT_FROM              1 (someclass)
             12 STORE_NAME               2 (myclass)
             15 POP_TOP
             16 LOAD_CONST               2 (None)
             19 RETURN_VALUE

This is the only approach that only adds one name (myclass) into the current locals() (i.e. __dict__, which will become globals() for everything defined in the module). It is also the shortest bytecode.

This method will raise ImportError if someclass is not found in module.submodule. It will, however, attempt to load module.submodule.someclass as a module.

Case 2:

>>> compile_and_dis('from module.submodule import someclass; myclass = someclass')
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               1 (('someclass',))
              6 IMPORT_NAME              0 (module.submodule)
              9 IMPORT_FROM              1 (someclass)
             12 STORE_NAME               1 (someclass)
             15 POP_TOP
             16 LOAD_NAME                1 (someclass)
             19 STORE_NAME               2 (myclass)
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE

This is nearly identical to case 1, except that it leaks a second name (someclass) into the local namespace. If the import and assignment are not contiguous, you do run the theoretical risk of reusing the name for something else, but if you're shadowing names you have horrible design anyway.

Note the useless STORE_NAME/LOAD_NAME cycle in the bytecode (around an unrelated POP_TOP).

Case 3:

>>> compile_and_dis('from module import submodule; myclass = submodule.someclass')
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               1 (('submodule',))
              6 IMPORT_NAME              0 (module)
              9 IMPORT_FROM              1 (submodule)
             12 STORE_NAME               1 (submodule)
             15 POP_TOP
             16 LOAD_NAME                1 (submodule)
             19 LOAD_ATTR                2 (someclass)
             22 STORE_NAME               3 (myclass)
             25 LOAD_CONST               2 (None)
             28 RETURN_VALUE

This approach leaks submodule into the local namespace. It does not raise ImportError if the class is not found, rather it raises AttributeError during the assignment. It does not attempt to load module.submodule.someclass as a module (and in fact doesn't even care whether or not module.submodule is a module).

Case 4:

>>> compile_and_dis('import module.submodule as submodule; myclass = submodule.someclass')
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               1 (None)
              6 IMPORT_NAME              0 (module.submodule)
              9 LOAD_ATTR                1 (submodule)
             12 STORE_NAME               1 (submodule)
             15 LOAD_NAME                1 (submodule)
             18 LOAD_ATTR                2 (someclass)
             21 STORE_NAME               3 (myclass)
             24 LOAD_CONST               1 (None)
             27 RETURN_VALUE

This is similar to case 3 but requires that module.submodule be a module.

Case 5:

>>> compile_and_dis('import module.submodule; myclass = module.submodule.someclass')
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               1 (None)
              6 IMPORT_NAME              0 (module.submodule)
              9 STORE_NAME               1 (module)
             12 LOAD_NAME                1 (module)
             15 LOAD_ATTR                2 (submodule)
             18 LOAD_ATTR                3 (someclass)
             21 STORE_NAME               4 (myclass)
             24 LOAD_CONST               1 (None)
             27 RETURN_VALUE

This approach is similar to case 4, although the 2 attribute loads are at different places so a different variable leaks into the local namespace.

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

1 Comment

This is very helpful!
4

The main difference is that in your variable assignment example, someclass is still available as a name. That'll lead to issues when importing types with the same name.

from datetime import datetime
from arrow import datetime  # problem!

To fix this:

from datetime import datetime
from arrow import datetime as arrow_datetime

1 Comment

Any ideas why PyCharm cannot show Quick definition or Go to source on someclass.some_attribute_or_method, when I use the second approach (Assignment to variable), but it works fine when I use the first one?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.