1

Consider we have these Python files:

# config.py
a = 1

And:

# m.py
import config


config.a = 2

And finally:

# main.py
import config
import m


print(config.a)  # -> prints 2

print(config.a) prints 2.

But then if we change the importation method in m.py like so:

# m.py
from config import a


a = 2

Running main.py again prints 1 instead of 2; Python treats it now as if it's a copy of the original config.a variable instead of a reference.

Either a and config.a are the same object. If so, why does its value change when we reference it as config.a but nothing happens if we reference it as just a?

This answer from Micheal addressed this same issue but it still didn't explain why this happens; we still don't understand why Python is treating each reference differently.

Willian Fuks
  • 11,259
  • 10
  • 50
  • 74
  • 1
    It's not really clear what more you need than the information provided in the linked-to Q&A. Do you realise that ``config.a`` means "the name ``a`` in the module ``config``", but ``a`` means "the name ``a`` in the current module"? Do you realise that there is a difference between the *identifiers* ``a``/``config.a`` and the *objects pointed to by* ``a``/``config.a``? – MisterMiyagi Mar 05 '21 at 14:45
  • Does this answer your question? [Are python variables pointers? or else what are they?](https://stackoverflow.com/questions/13530998/are-python-variables-pointers-or-else-what-are-they) – MisterMiyagi Mar 05 '21 at 14:55

2 Answers2

1

This is, from my understanding, simply the difference between mutating an object, and mutating a reference to an object.

from config import a

Is essentially (while skipping some details)

temp = __import__("config", globals(), locals(), ['a'], 0))
a = temp.a

Or, in other words, it creates a variable a within the scope of the importing module, and that a is a reference to the original 1.

On the other hand,

import config

Is essentially:

config = __import__('config', globals(), locals(), [], 0)

Or, in other words, it creates a variable config that holds a reference to the module object itself.


The difference is, when you do a = 2, you're reassigning the local variable, which has no effect on the variable in the module.

When you do config.a = 2 however, you're mutating the module object itself. That would get translated to a call to:

config.__setattr__("a", 2)

Which is different than a simple reassignment. It's altering the state of the module object, vs a = 2 which only changes what the local a copy is looking at.


"Translation" examples modified from these docs.

Carcigenicate
  • 43,494
  • 9
  • 68
  • 117
0

When you import it via from filename import variable you create an additional copy of the variable in your namespace. This method of importing variables is discouraged for precisely this reason. So a and config.a are no longer the same objects.

Since now you have two copies (in original module namespace and another in your file), you need to be careful which one you want to modify.

In fact, you can see the change you made by print(m.a) which should give you 2.

lllrnr101
  • 2,288
  • 2
  • 4
  • 15
  • 1
    Worth noting: The HOWTO discouraging such imports is no longer present in the current version of the documentation. – MisterMiyagi Mar 05 '21 at 14:59
  • Yes, I was wondering why that is the case. Do you know why? – lllrnr101 Mar 05 '21 at 15:01
  • Honestly, it feels weird and I would sternly disagree with it: ``from X import Y`` often *is* the right thing. The underlying issue is that mutating global shared state is bad – what one should avoid is not *sharing* global state but *mutating* global state. – MisterMiyagi Mar 05 '21 at 15:04
  • 3
    I'm guessing because it was too broadly worded, and when they tried to fix it they decided not to bother. The wording on that page suggests avoiding it even in cases where an object is almost certainly not going to be reassigned, like is the case when importing a function from a module. `from x import some_func` is absolutely fine unless you're reassigning `some_func`, but I'd argue that it's the reassigning of `some_func` that's the bad practice, not the import style. – Carcigenicate Mar 05 '21 at 15:05
  • imo `from X import Y` is right when I am importing a sub module eg `from datetime import datetime`. It looks to be bad if I am importing a variable. In that case I would like to refer to it by its absolute name. – lllrnr101 Mar 05 '21 at 15:07
  • Thanks for your answer! For what I understood there's no copying of objects happening, so `a is config.a` returns `True` (they refer to the same object yet). When we reassign `a` a new object is indeed created (if `a` was a list, changing its values would change `config.a` for instance). – Willian Fuks Mar 05 '21 at 15:59
  • 1
    Ya, the object themselves aren't copied. Before reassigning, `a` will refer to the same literal object in both files. When I used "copy" in my answer, I meant the "reference" to `a` is being copied (as in, both variables now refer to the same object). Technically, that's not super-proper terminology for Python, but it's how I've always thought of it. – Carcigenicate Mar 05 '21 at 16:14
  • Yes, until reassignment, everything is same. No additional copied made. – lllrnr101 Mar 05 '21 at 16:20