Search code examples
pythonmypy

Why use `from module import A as A` instead of just `from module import A`


When reading source code of fastapi, this line make me fuzzy:

from starlette.testclient import TestClient as TestClient

Why not just: from starlette.testclient import TestClient?


Solution

  • From the point of view of executable code, there is absolutely no difference in terms of the Python bytecode being generated by the two different code examples (using Python 3.9):

    >>> dis.dis('from starlette.testclient import TestClient as TestClient')
      1           0 LOAD_CONST               0 (0)
                  2 LOAD_CONST               1 (('TestClient',))
                  4 IMPORT_NAME              0 (starlette.testclient)
                  6 IMPORT_FROM              1 (TestClient)
                  8 STORE_NAME               1 (TestClient)
                 10 POP_TOP
                 12 LOAD_CONST               2 (None)
                 14 RETURN_VALUE
    >>> dis.dis('from starlette.testclient import TestClient')
      1           0 LOAD_CONST               0 (0)
                  2 LOAD_CONST               1 (('TestClient',))
                  4 IMPORT_NAME              0 (starlette.testclient)
                  6 IMPORT_FROM              1 (TestClient)
                  8 STORE_NAME               1 (TestClient)
                 10 POP_TOP
                 12 LOAD_CONST               2 (None)
                 14 RETURN_VALUE
    

    As shown, they are exactly identical. (Related question asked before existence of PEP-0484).

    However, the comment by Graham501617 noted how modern type hinting validators (such as mypy) accept this particular syntax to denote the re-export of that imported name (the other being the __all__, which thankfully they did end up correctly supporting as that has been a standard syntax to denote symbols to (re-)export since Python 2). Specifically, as per the description of Stub Files in the referenced PEP 0484, quote:

    • Modules and variables imported into the stub are not considered exported from the stub unless the import uses the import ... as ... form or the equivalent from ... import ... as ... form. (UPDATE: To clarify, the intention here is that only names imported using the form X as X will be exported, i.e. the name before and after as must be the same.)

    Looking at git blame for the relevant file in the packages pointed to this commit (direct link to relevant diff for the file) indicated that this particular type hinting issue was being addressed (as part of this issue) to ensure mypy will treat those imported names as re-export, thus allowing the usage of the --no-implicit-reexport flag (which --strict has likely implicitly enabled).

    This particular re-export syntax however is very much strict about the "name before and after as must be the same", the syntax referred in the related question (i.e. import foo.bar as bar) can be found in recommendation by certain modern packages (e.g. PyTorch recommends the use of import torch.nn as nn, as discussed in this question), does not in fact allow the bar (or nn) symbol be re-exported from the current module as foo.bar is not the same as bar (likewise torch.nn is not the same as nn, as the whole set of tokens torch.nn is evaluated instead of the just the final identifier after the final .).