Why does `from module import` support `(module, module)`, but not merely `import`?

In V3.13, why am I able to do:

from PyQt6.QtWidgets import (
    QApplication,
    QMainWindow,
    QDockWidget,
    QTextEdit,
    QMdiArea
)

…but not the undermentioned?

import (
    PyQt6.QtWidgets,
    PyQt6.QtGui,
    PyQt6.QtCore
)

Instead, the sole equivalent is:

import \
    PyQt6.QtWidgets, \
    PyQt6.QtGui, \
    PyQt6.QtCore
1 Like

There are some arguments under this recent proposal to support this:

1 Like

The first one with the from still only imports one module, just the same as a regular import statement.

The contents of the brackets can just then be referred to (without preppending them with their owning module’s name and.) and the importer’s name space can be controlled.

Don’t know if this is the cause, but a possible reason for the discrepancy is that each normal import listed is totally independent - there wouldn’t be a difference in bytecode other than line numbers between parenthesised imports and multiple import statements. On the other hand import from passes that entire list to the import system, and splitting would also require loading the module twice. The global/nonlocal statements also disallow parentheses, and those also wouldn’t have a semantic difference from allowing them.

2 Likes

Usually, it is recommended to just write

import PyQt6.QtWidgets
import PyQt6.QtGui
import PyQt6.QtCore

This is actually not correct, every imported module is cached in sys.modules.

1 Like

@tstefan, I’ve always found PEP8’s recommendation in this regard difficult to comprehend, for it’s both inconsistent:

…and, to a lay man like myself, nonsensical - for what reason would writing multiple import statements be quicker to author, or quicker for the interpreter?

The bytecode is identical:

In [1]: import dis

In [2]: dis.dis("""
   ...: import PyQt6.QtWidgets
   ...: import PyQt6.QtGui
   ...: import PyQt6.QtCore
   ...: """)
  0           RESUME                   0

  2           LOAD_CONST               0 (0)
              LOAD_CONST               1 (None)
              IMPORT_NAME              0 (PyQt6.QtWidgets)
              STORE_NAME               1 (PyQt6)

  3           LOAD_CONST               0 (0)
              LOAD_CONST               1 (None)
              IMPORT_NAME              2 (PyQt6.QtGui)
              STORE_NAME               1 (PyQt6)

  4           LOAD_CONST               0 (0)
              LOAD_CONST               1 (None)
              IMPORT_NAME              3 (PyQt6.QtCore)
              STORE_NAME               1 (PyQt6)
              RETURN_CONST             1 (None)

In [3]: dis.dis("""
   ...: import PyQt6.QtWidgets, PyQt6.QtGui, PyQt6.QtCore
   ...: """)
  0           RESUME                   0

  2           LOAD_CONST               0 (0)
              LOAD_CONST               1 (None)
              IMPORT_NAME              0 (PyQt6.QtWidgets)
              STORE_NAME               1 (PyQt6)
              LOAD_CONST               0 (0)
              LOAD_CONST               1 (None)
              IMPORT_NAME              2 (PyQt6.QtGui)
              STORE_NAME               1 (PyQt6)
              LOAD_CONST               0 (0)
              LOAD_CONST               1 (None)
              IMPORT_NAME              3 (PyQt6.QtCore)
              STORE_NAME               1 (PyQt6)
              RETURN_CONST             1 (None)
1 Like

PEP 8 is not about how fast you can write the code, but about readability.