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 (Cyborg)
  • No Skin
Collapse
Brand Logo

CIRCLE WITH A DOT

  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
21 Posts 8 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.
  • bmispelon@mastodon.socialB This user is from outside of this forum
    bmispelon@mastodon.socialB This user is from outside of this forum
    bmispelon@mastodon.social
    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`?

    alexgmin@mastodon.socialA bmispelon@mastodon.socialB _chrismay@fosstodon.org_ ehmatthes@fosstodon.orgE jonafato@mastodon.socialJ 8 Replies Last reply
    1
    0
    • bmispelon@mastodon.socialB bmispelon@mastodon.social

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

      alexgmin@mastodon.socialA This user is from outside of this forum
      alexgmin@mastodon.socialA This user is from outside of this forum
      alexgmin@mastodon.social
      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

      bmispelon@mastodon.socialB 1 Reply Last reply
      0
      • alexgmin@mastodon.socialA alexgmin@mastodon.social

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

        bmispelon@mastodon.socialB This user is from outside of this forum
        bmispelon@mastodon.socialB This user is from outside of this forum
        bmispelon@mastodon.social
        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

        alexgmin@mastodon.socialA 1 Reply Last reply
        0
        • bmispelon@mastodon.socialB bmispelon@mastodon.social

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

          bmispelon@mastodon.socialB This user is from outside of this forum
          bmispelon@mastodon.socialB This user is from outside of this forum
          bmispelon@mastodon.social
          wrote last edited by
          #4

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

          treyhunner@mastodon.socialT 1 Reply Last reply
          0
          • bmispelon@mastodon.socialB bmispelon@mastodon.social

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

            alexgmin@mastodon.socialA This user is from outside of this forum
            alexgmin@mastodon.socialA This user is from outside of this forum
            alexgmin@mastodon.social
            wrote last edited by
            #5

            @bmispelon Wtf

            1 Reply Last reply
            0
            • bmispelon@mastodon.socialB bmispelon@mastodon.social

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

              _chrismay@fosstodon.org_ This user is from outside of this forum
              _chrismay@fosstodon.org_ This user is from outside of this forum
              _chrismay@fosstodon.org
              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

              bmispelon@mastodon.socialB 1 Reply Last reply
              0
              • _chrismay@fosstodon.org_ _chrismay@fosstodon.org

                @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

                bmispelon@mastodon.socialB This user is from outside of this forum
                bmispelon@mastodon.socialB This user is from outside of this forum
                bmispelon@mastodon.social
                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
                • bmispelon@mastodon.socialB bmispelon@mastodon.social

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

                  ehmatthes@fosstodon.orgE This user is from outside of this forum
                  ehmatthes@fosstodon.orgE This user is from outside of this forum
                  ehmatthes@fosstodon.org
                  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.

                  ehmatthes@fosstodon.orgE 1 Reply Last reply
                  0
                  • bmispelon@mastodon.socialB bmispelon@mastodon.social

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

                    jonafato@mastodon.socialJ This user is from outside of this forum
                    jonafato@mastodon.socialJ This user is from outside of this forum
                    jonafato@mastodon.social
                    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.

                    bmispelon@mastodon.socialB 1 Reply Last reply
                    0
                    • ehmatthes@fosstodon.orgE ehmatthes@fosstodon.org

                      @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.

                      ehmatthes@fosstodon.orgE This user is from outside of this forum
                      ehmatthes@fosstodon.orgE This user is from outside of this forum
                      ehmatthes@fosstodon.org
                      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.

                      ehmatthes@fosstodon.orgE 1 Reply Last reply
                      0
                      • jonafato@mastodon.socialJ jonafato@mastodon.social

                        @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.

                        bmispelon@mastodon.socialB This user is from outside of this forum
                        bmispelon@mastodon.socialB This user is from outside of this forum
                        bmispelon@mastodon.social
                        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`?

                        jonafato@mastodon.socialJ 1 Reply Last reply
                        0
                        • ehmatthes@fosstodon.orgE ehmatthes@fosstodon.org

                          @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.

                          ehmatthes@fosstodon.orgE This user is from outside of this forum
                          ehmatthes@fosstodon.orgE This user is from outside of this forum
                          ehmatthes@fosstodon.org
                          wrote last edited by
                          #12

                          @bmispelon Here's my VSCodium session:

                          1 Reply Last reply
                          0
                          • bmispelon@mastodon.socialB bmispelon@mastodon.social

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

                            ehmatthes@fosstodon.orgE This user is from outside of this forum
                            ehmatthes@fosstodon.orgE This user is from outside of this forum
                            ehmatthes@fosstodon.org
                            wrote last edited by
                            #13

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

                            bmispelon@mastodon.socialB 1 Reply Last reply
                            0
                            • ehmatthes@fosstodon.orgE ehmatthes@fosstodon.org

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

                              bmispelon@mastodon.socialB This user is from outside of this forum
                              bmispelon@mastodon.socialB This user is from outside of this forum
                              bmispelon@mastodon.social
                              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!

                              ehmatthes@fosstodon.orgE 1 Reply Last reply
                              0
                              • bmispelon@mastodon.socialB bmispelon@mastodon.social

                                @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!

                                ehmatthes@fosstodon.orgE This user is from outside of this forum
                                ehmatthes@fosstodon.orgE This user is from outside of this forum
                                ehmatthes@fosstodon.org
                                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
                                • bmispelon@mastodon.socialB bmispelon@mastodon.social

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

                                  pawamoy@fosstodon.orgP This user is from outside of this forum
                                  pawamoy@fosstodon.orgP This user is from outside of this forum
                                  pawamoy@fosstodon.org
                                  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!

                                  pawamoy@fosstodon.orgP 1 Reply Last reply
                                  0
                                  • bmispelon@mastodon.socialB bmispelon@mastodon.social

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

                                    jonafato@mastodon.socialJ This user is from outside of this forum
                                    jonafato@mastodon.socialJ This user is from outside of this forum
                                    jonafato@mastodon.social
                                    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
                                    • pawamoy@fosstodon.orgP pawamoy@fosstodon.org

                                      @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!

                                      pawamoy@fosstodon.orgP This user is from outside of this forum
                                      pawamoy@fosstodon.orgP This user is from outside of this forum
                                      pawamoy@fosstodon.org
                                      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!

                                      pawamoy@fosstodon.orgP 1 Reply Last reply
                                      0
                                      • pawamoy@fosstodon.orgP pawamoy@fosstodon.org

                                        @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!

                                        pawamoy@fosstodon.orgP This user is from outside of this forum
                                        pawamoy@fosstodon.orgP This user is from outside of this forum
                                        pawamoy@fosstodon.org
                                        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
                                        • bmispelon@mastodon.socialB bmispelon@mastodon.social

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

                                          treyhunner@mastodon.socialT This user is from outside of this forum
                                          treyhunner@mastodon.socialT This user is from outside of this forum
                                          treyhunner@mastodon.social
                                          wrote last edited by
                                          #20

                                          @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
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          • Login

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