How to make a variable's late binding crosses the module boundary?

Jach Feng jfong at ms4.hinet.net
Wed Aug 31 21:53:55 EDT 2022


Mark Bourne 在 2022年9月1日 星期四凌晨2:43:40 [UTC+8] 的信中寫道:
> Jach Feng wrote: 
> > Mark Bourne 在 2022年8月29日 星期一下午6:40:59 [UTC+8] 的信中寫道: 
> >> Jach Feng wrote: 
> >>> Chris Angelico 在 2022年8月29日 星期一下午1:58:58 [UTC+8] 的信中寫道: 
> >>>> On Mon, 29 Aug 2022 at 15:54, Jach Feng <jf... at ms4.hinet.net> wrote: 
> >>>>> 
> >>>>> Richard Damon 在 2022年8月29日 星期一上午10:47:08 [UTC+8] 的信中寫道: 
> >>>>>> On 8/27/22 7:42 AM, Mark Bourne wrote: 
> >>>>>>> Jach Feng wrote: 
> >>>>>>>> I have two files: test.py and test2.py 
> >>>>>>>> --test.py-- 
> >>>>>>>> x = 2 
> >>>>>>>> def foo(): 
> >>>>>>>> print(x) 
> >>>>>>>> foo() 
> >>>>>>>> 
> >>>>>>>> x = 3 
> >>>>>>>> foo() 
> >>>>>>>> 
> >>>>>>>> --test2.py-- 
> >>>>>>>> from test import * 
> >>>>>>>> x = 4 
> >>>>>>>> foo() 
> >>>>>>>> 
> >>>>>>>> ----- 
> >>>>>>>> Run test.py under Winows8.1, I get the expected result: 
> >>>>>>>> e:\MyDocument>py test.py 
> >>>>>>>> 2 
> >>>>>>>> 3 
> >>>>>>>> 
> >>>>>>>> But when run test2.py, the result is not my expected 2,3,4:-( 
> >>>>>>>> e:\MyDocument>py test2.py 
> >>>>>>>> 2 
> >>>>>>>> 3 
> >>>>>>>> 3 
> >>>>>>>> 
> >>>>>>>> What to do? 
> >>>>>>> 
> >>>>>>> `from test import *` does not link the names in `test2` to those in 
> >>>>>>> `test`. It just binds objects bound to names in `test` to the same 
> >>>>>>> names in `test2`. A bit like doing: 
> >>>>>>> 
> >>>>>>> import test 
> >>>>>>> x = test.x 
> >>>>>>> foo = test.foo 
> >>>>>>> del test 
> >>>>>>> 
> >>>>>>> Subsequently assigning a different object to `x` in one module does 
> >>>>>>> not affect the object assigned to `x` in the other module. So `x = 4` 
> >>>>>>> in `test2.py` does not affect the object assigned to `x` in `test.py` 
> >>>>>>> - that's still `3`. If you want to do that, you need to import `test` 
> >>>>>>> and assign to `test.x`, for example: 
> >>>>>>> 
> >>>>>>> import test 
> >>>>>>> test.x = 4 
> >>>>>>> test.foo() 
> >>>>>>> 
> >>>>>> Yes, fundamental issue is that the statement 
> >>>>>> 
> >>>>>> from x import y 
> >>>>>> 
> >>>>>> makes a binding in this module to the object CURRECTLY bound to x.y to 
> >>>>>> the name y, but if x.y gets rebound, this module does not track the changes. 
> >>>>>> 
> >>>>>> You can mutate the object x.y and see the changes, but not rebind it. 
> >>>>>> 
> >>>>>> If you need to see rebindings, you can't use the "from x import y" form, 
> >>>>>> or at a minimum do it as: 
> >>>>>> 
> >>>>>> 
> >>>>>> import x 
> >>>>>> 
> >>>>>> from x import y 
> >>>>>> 
> >>>>>> then later to get rebindings to x.y do a 
> >>>>>> 
> >>>>>> y = x.y 
> >>>>>> 
> >>>>>> to rebind to the current x.y object. 
> >>>>>> 
> >>>>>> -- 
> >>>>>> Richard Damon 
> >>>>> Yes, an extra "import x" will solve my problem too! Sometimes I am wondering why "from x import y" hides x? hum...can't figure out the reason:-) 
> >>>>> 
> >>>> "from x import y" doesn't hide x - it just grabs y. Python does what 
> >>>> you tell it to. :) 
> >>>> 
> >>>> ChrisA 
> >>> But I had heard people say that "from x import y" did import the whole x module into memory, just as "import x" did, not "grabs y" only. Is this correct? 
> >> `from x import y` does import the whole module x into memory, and adds 
> >> it to `sys.modules`. But it only binds the name `y` in the namespace of 
> >> module doing the import (and it binds it to the value of `x.y` at the 
> >> time the import is done - it doesn't magically keep them in sync if one 
> >> or the other is later reassigned). 
> >> 
> >> The point about the whole module being imported is that you don't save 
> >> any memory by using `from x import y` to avoid importing some very large 
> >> object `z` from `x`. Those other large objects might be needed by 
> >> functions which have been imported (e.g. your `foo` function still needs 
> >> `x` even if you haven't imported `x` - so it still needs to be loaded 
> >> into memory) or might be imported and used by other modules importing 
> >> `x`, so they still have to be loaded when any part of `x` is imported - 
> >> they just don't have to be bound to names in the importing module's 
> >> namespace. 
> >> 
> >> As Richard mentioned, if `x.y` is a mutable object (such as a list) you 
> >> can still mutate that object (e.g. add/remove items) and those changes 
> >> will be seen in both modules. That's because both are still bound to 
> >> the same object and you're mutating that existing object. If you assign 
> >> a new list to either, that won't be seen by the other. 
> >> 
> >> -- 
> >> Mark. 
> > When using dot notation to change variable, no matter if 'x.y' is a mutable or immutable object, the change 
> > will be seen in both modules except those early bindings. 
> > 
> > --Jach
> Yes, sorry, I'd used `x.y` as a way of referring to the variable `y` in 
> module `x` as opposed to `y` in the current module. It doesn't help 
> that I added the second paragraph and didn't notice that the third was 
> then out of context. 
> 
> If you use `import x` and assign to `x.y`, that will as you say be seen 
> in both modules. On the other hand, if you use `from x import y`, then 
> (as has been discussed) assigning to `y` in the module which has the 
> import won't affect the value seen in module `x`. However, if `y` is 
> mutable (e.g. a list), and no new object is assigned to it, then `y` 
> still points to the same object in both modules, so mutating that 
> existing object (e.g. `y.append(123)`) *will* affect what's seen in both 
> modules - they're both referencing the same object, and you've modified 
> that object, as opposed to assigning a new object to `y` in one of the 
> modules. 
> 
> -- 
> Mark.
Good Point! I learn from it:-)

I found that using "from test import *" in test2.py makes test2's relation to "test" almost like the function's relation to the module where it is defined. You can refer to all the variables of the module, even modify the mutable objects, but just can't change them. The only way to change it is to declare it a "global." The same situation is in Test 2, but unfortunately there is no "super-global" to declare. The only way to make changes is to "import test" (again? it looks like a redundant  statement to me:-) and use dot notation to do it.

--Jach


More information about the Python-list mailing list