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 This user is from outside of this forum
    Baptiste MispelonB This user is from outside of this forum
    Baptiste Mispelon
    wrote last edited by
    #1

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

    Alex GómezA Baptiste MispelonB Chris May_ Eric MatthesE Jon BanafatoJ 10 Replies Last reply
    1
    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`?

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

      @bmispelon I feel like it would be either ImportError or printing 1, but knowing you I'm sure it's somehow the RecursionError

      Baptiste MispelonB 1 Reply Last reply
      0
      • Alex GómezA Alex Gómez

        @bmispelon I feel like it would be either ImportError or printing 1, but knowing you I'm sure it's somehow the RecursionError

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

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

        Alex GómezA 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`?

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

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

          Trey Hunner 🐍T 1 Reply Last reply
          0
          • 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
                                          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