Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (Darkly)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. Uncategorized
  3. Can someone explain this #Python import behavior

Can someone explain this #Python import behavior

Scheduled Pinned Locked Moved Uncategorized
python
23 Posts 10 Posters 0 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Baptiste MispelonB Baptiste Mispelon

    Can someone explain this #Python import behavior?
    I'm in a directory with 3 files:

    a.py contains `A = 1; from b import *`
    b.py contains `from a import *; A += 1`
    c.py contains `from a import A; print(A)`

    Can you guess and explain what happens when you run `python c.py`?

    Jon BanafatoJ This user is from outside of this forum
    Jon BanafatoJ This user is from outside of this forum
    Jon Banafato
    wrote last edited by
    #9

    @bmispelon I *think* what's happening is basically sleight of hand (though I guessed wrong at first and would not advise that people use this behavior because it can confuse us). `a.A` gets reassigned when it imports `b.A` (via `b.*`).

    Autoformatting these imports would cause an error, and obviously we want these files structured this way, so setting `__all__ = []` in `b.py` is the correct fix here.

    Baptiste MispelonB 1 Reply Last reply
    0
    • Eric MatthesE Eric Matthes

      @bmispelon My initial guess was 2. c first imports a.A, which is 1. But the call to import from a loads a.py. That includes the call to import * from b, which imports from a. So at that point, A is 1. b then adds one to A, which sets A at 2. Then execution returns to c, with A at 2. So I think the value of A in c comes from b.

      I tried to verify this in a pdb session, but stepping through at a low enough level to see this was bringing me into even lower level Python functions.

      Eric MatthesE This user is from outside of this forum
      Eric MatthesE This user is from outside of this forum
      Eric Matthes
      wrote last edited by
      #10

      @bmispelon Then I ran c.py in a VS Codium debugger session, watching A.

      - A starts as undefined (everything does).
      - After the first line of a.py, A is 1, but I think that VS Codium is actually reporting a.A.
      - The import in a is hit, and A goes to undefined. I think VSC is showing b.A.
      - b's import runs, and A is 1. I think that's b.A.
      - The second line of b is run, and A is 2.
      - Execution goes back to c, where the value of A is 2.

      Eric MatthesE 1 Reply Last reply
      0
      • Jon BanafatoJ Jon Banafato

        @bmispelon I *think* what's happening is basically sleight of hand (though I guessed wrong at first and would not advise that people use this behavior because it can confuse us). `a.A` gets reassigned when it imports `b.A` (via `b.*`).

        Autoformatting these imports would cause an error, and obviously we want these files structured this way, so setting `__all__ = []` in `b.py` is the correct fix here.

        Baptiste MispelonB This user is from outside of this forum
        Baptiste MispelonB This user is from outside of this forum
        Baptiste Mispelon
        wrote last edited by
        #11

        @jonafato Interesting suggestion for a fix! What happens then if all the `from ... import *` are replaced by `from ... import A`?

        Jon BanafatoJ 1 Reply Last reply
        0
        • Eric MatthesE Eric Matthes

          @bmispelon Then I ran c.py in a VS Codium debugger session, watching A.

          - A starts as undefined (everything does).
          - After the first line of a.py, A is 1, but I think that VS Codium is actually reporting a.A.
          - The import in a is hit, and A goes to undefined. I think VSC is showing b.A.
          - b's import runs, and A is 1. I think that's b.A.
          - The second line of b is run, and A is 2.
          - Execution goes back to c, where the value of A is 2.

          Eric MatthesE This user is from outside of this forum
          Eric MatthesE This user is from outside of this forum
          Eric Matthes
          wrote last edited by
          #12

          @bmispelon Here's my VSCodium session:

          1 Reply Last reply
          0
          • Baptiste MispelonB Baptiste Mispelon

            Can someone explain this #Python import behavior?
            I'm in a directory with 3 files:

            a.py contains `A = 1; from b import *`
            b.py contains `from a import *; A += 1`
            c.py contains `from a import A; print(A)`

            Can you guess and explain what happens when you run `python c.py`?

            Eric MatthesE This user is from outside of this forum
            Eric MatthesE This user is from outside of this forum
            Eric Matthes
            wrote last edited by
            #13

            @bmispelon Can you share the real-world motivation for this question at some point?

            Baptiste MispelonB 1 Reply Last reply
            0
            • Eric MatthesE Eric Matthes

              @bmispelon Can you share the real-world motivation for this question at some point?

              Baptiste MispelonB This user is from outside of this forum
              Baptiste MispelonB This user is from outside of this forum
              Baptiste Mispelon
              wrote last edited by
              #14

              @ehmatthes A very old Django project whose multiple settings files were importing from each other, leaving me very confused for a bit 😅

              I definitely would not recommend writing actual code that looks like this!

              Eric MatthesE 1 Reply Last reply
              0
              • Baptiste MispelonB Baptiste Mispelon

                @ehmatthes A very old Django project whose multiple settings files were importing from each other, leaving me very confused for a bit 😅

                I definitely would not recommend writing actual code that looks like this!

                Eric MatthesE This user is from outside of this forum
                Eric MatthesE This user is from outside of this forum
                Eric Matthes
                wrote last edited by
                #15

                @bmispelon

                > whose multiple settings files were importing from each other

                You are not the only one who would be confused, please do not mention this in office hours

                1 Reply Last reply
                0
                • Baptiste MispelonB Baptiste Mispelon

                  Can someone explain this #Python import behavior?
                  I'm in a directory with 3 files:

                  a.py contains `A = 1; from b import *`
                  b.py contains `from a import *; A += 1`
                  c.py contains `from a import A; print(A)`

                  Can you guess and explain what happens when you run `python c.py`?

                  Timothée Mazzucotelli :python:P This user is from outside of this forum
                  Timothée Mazzucotelli :python:P This user is from outside of this forum
                  Timothée Mazzucotelli :python:
                  wrote last edited by
                  #16

                  @bmispelon

                  Got it! Did it in my head then verified with an interpreter 🙂

                  There's nothing weird here. Python executes stuff sequentially, so:

                  - in 😄 from a import A
                  - in a: A = 1
                  - in a: from b import *
                  - in b: from a import * (so we have A = 1 in b)
                  - in b: A += 1 (so we have A = 2 in b)
                  - in a: finishing previous import, so we now have A = 2 in a
                  - in 😄 finishing previous import, so we now have A = 2 in C
                  - in 😄 print(A) -> 2!

                  Timothée Mazzucotelli :python:P 1 Reply Last reply
                  0
                  • Baptiste MispelonB Baptiste Mispelon

                    @jonafato Interesting suggestion for a fix! What happens then if all the `from ... import *` are replaced by `from ... import A`?

                    Jon BanafatoJ This user is from outside of this forum
                    Jon BanafatoJ This user is from outside of this forum
                    Jon Banafato
                    wrote last edited by
                    #17

                    @bmispelon That would result in the same original behavior, since `__all__` controls the import behavior of `*` but not of individual variables (though I think I have seen projects that allow you to turn that kind of thing into an error via name mangling or some other hack under the hood).

                    1 Reply Last reply
                    0
                    • Timothée Mazzucotelli :python:P Timothée Mazzucotelli :python:

                      @bmispelon

                      Got it! Did it in my head then verified with an interpreter 🙂

                      There's nothing weird here. Python executes stuff sequentially, so:

                      - in 😄 from a import A
                      - in a: A = 1
                      - in a: from b import *
                      - in b: from a import * (so we have A = 1 in b)
                      - in b: A += 1 (so we have A = 2 in b)
                      - in a: finishing previous import, so we now have A = 2 in a
                      - in 😄 finishing previous import, so we now have A = 2 in C
                      - in 😄 print(A) -> 2!

                      Timothée Mazzucotelli :python:P This user is from outside of this forum
                      Timothée Mazzucotelli :python:P This user is from outside of this forum
                      Timothée Mazzucotelli :python:
                      wrote last edited by
                      #18

                      @bmispelon by the way I'm not sure to understand why the circular import works. I think Python has special handling for some cases where it's able to tell the circular import is "safe" somehow (like "a is almost finished, there's only * to import from b", meaning b can import from a again, and when b is finisehd a is updated again with any symbols declared in b). Tried to find an actual answer in the past but didn't find anything. Maybe should read the sources!

                      Timothée Mazzucotelli :python:P 1 Reply Last reply
                      0
                      • Timothée Mazzucotelli :python:P Timothée Mazzucotelli :python:

                        @bmispelon by the way I'm not sure to understand why the circular import works. I think Python has special handling for some cases where it's able to tell the circular import is "safe" somehow (like "a is almost finished, there's only * to import from b", meaning b can import from a again, and when b is finisehd a is updated again with any symbols declared in b). Tried to find an actual answer in the past but didn't find anything. Maybe should read the sources!

                        Timothée Mazzucotelli :python:P This user is from outside of this forum
                        Timothée Mazzucotelli :python:P This user is from outside of this forum
                        Timothée Mazzucotelli :python:
                        wrote last edited by
                        #19

                        @bmispelon OK no it's much simpler, module a is simply partially initialized. By the time b imports it, it's not re-executed since it exists in sys.modules, and b imports every existing (yet) symbols within A. from a import A would work too.

                        1 Reply Last reply
                        0
                        • Baptiste MispelonB Baptiste Mispelon

                          Can someone explain this #Python import behavior?
                          I'm in a directory with 3 files:

                          a.py contains `A = 1; from b import *`
                          b.py contains `from a import *; A += 1`
                          c.py contains `from a import A; print(A)`

                          Can you guess and explain what happens when you run `python c.py`?

                          PatrysP This user is from outside of this forum
                          PatrysP This user is from outside of this forum
                          Patrys
                          wrote last edited by
                          #20

                          @bmispelon I think it’s a change from Python 3. Previously, cyclical imports would fail unless you only imported already defined symbols by name. It had its own WTFs as you could later redefine a symbol that was already exported. In Python 3 it instead keeps building each module in place until complete or deadlocked, so you get A = 1, block, A gets imported into b, A is increased, a gets unblocked, a imports A back. I’m sure there is a valid use case 😬

                          1 Reply Last reply
                          0
                          • Baptiste MispelonB Baptiste Mispelon

                            @treyhunner Tagging you on this since it might qualify as a #Pythonoddity

                            Trey Hunner 🐍T This user is from outside of this forum
                            Trey Hunner 🐍T This user is from outside of this forum
                            Trey Hunner 🐍
                            wrote last edited by
                            #21

                            @bmispelon This is absolutely a Python oddity. I guessed incorrectly. I understand why I guessed incorrectly now that I look back at the code... I'm not sure any Python oddity has stress testeded my mental model of Python's import system as much as this one.

                            1 Reply Last reply
                            0
                            • Baptiste MispelonB Baptiste Mispelon

                              Can someone explain this #Python import behavior?
                              I'm in a directory with 3 files:

                              a.py contains `A = 1; from b import *`
                              b.py contains `from a import *; A += 1`
                              c.py contains `from a import A; print(A)`

                              Can you guess and explain what happens when you run `python c.py`?

                              StylusS This user is from outside of this forum
                              StylusS This user is from outside of this forum
                              Stylus
                              wrote last edited by
                              #22

                              @bmispelon

                              $ echo 'A = 1; print("A1"); from b import A; print("A2")' > a.py
                              $ echo 'print("B1"); from a import A; print("B2"); A += 1' > b.py
                              $ python -c 'from a import A; print(A)'
                              A1
                              B1
                              B2
                              A2
                              2

                              I added several prints so that it's possible to tell what order code is executed, and changed import * to import A because I think it improves clarity without changing the behavior.

                              • The main program runs
                              • It encounters an import of a so it starts executing the content of a.py in a newly created a module
                              • It sets A.a=1 via the assignment statement in a.py
                              • It encounters an import of b so it starts executing the content of b.py in a newly created b module
                              • It sets b.A=1 by from...import
                              • It adds 1 to b.A so that b.A is now equal to 2
                              • Execution reaches the end of b.py so it returns to a.py
                              • a.py sets a.A to 2 by from...import
                              • Execution reaches the end of a.py so it returns to the main program.
                              • The main program sets __main__.A to 2 by from ...import
                              • The value of A is printed (2)
                              1 Reply Last reply
                              0
                              • Baptiste MispelonB Baptiste Mispelon

                                Can someone explain this #Python import behavior?
                                I'm in a directory with 3 files:

                                a.py contains `A = 1; from b import *`
                                b.py contains `from a import *; A += 1`
                                c.py contains `from a import A; print(A)`

                                Can you guess and explain what happens when you run `python c.py`?

                                jlapoutreJ This user is from outside of this forum
                                jlapoutreJ This user is from outside of this forum
                                jlapoutre
                                wrote last edited by
                                #23

                                @bmispelon My reasoning was the last option, 2, and then I saw what @pawamoy wrote, which makes perfect sense to me

                                1 Reply Last reply
                                0
                                • R ActivityRelay shared this topic
                                Reply
                                • Reply as topic
                                Log in to reply
                                • Oldest to Newest
                                • Newest to Oldest
                                • Most Votes


                                • Login

                                • Don't have an account? Register

                                • Login or register to search.
                                Powered by NodeBB Contributors
                                • First post
                                  Last post
                                0
                                • Categories
                                • Recent
                                • Tags
                                • Popular
                                • World
                                • Users
                                • Groups