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

    @alexgmin Try it and see, then please explain the result to me, I genuinely don't understand what's going on

    Alex GómezA This user is from outside of this forum
    Alex GómezA This user is from outside of this forum
    Alex Gómez
    wrote last edited by
    #5

    @bmispelon Wtf

    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`?

      Chris May_ This user is from outside of this forum
      Chris May_ This user is from outside of this forum
      Chris May
      wrote last edited by
      #6

      @bmispelon My thought would be Python:
      - runs c.py and immediately starts to import a.py
      - sets `A` to 1
      - then starts to import b.py
      - module a(.py) is already in memory, so it bumps A to 2
      - finished importing b and a
      - returns to c.py and prints 2

      Baptiste MispelonB 1 Reply Last reply
      0
      • Chris May_ Chris May

        @bmispelon My thought would be Python:
        - runs c.py and immediately starts to import a.py
        - sets `A` to 1
        - then starts to import b.py
        - module a(.py) is already in memory, so it bumps A to 2
        - finished importing b and a
        - returns to c.py and prints 2

        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
        #7

        @_chrismay I think you're on to something. In my mental model Python would only "cache" an import after it's complete, but that doesn't appear correct.

        Bonus questions for you then, what if `a.py` contains this: `A = 1; from b import *; A+=1`?
        Or even this `A = 1; from b import *; A+=1;from b import *` ?

        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
          #8

          @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 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`?

            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