3

Say I have a module params.py in which a bunch of variables are declared:

a = 0.2
b = 0.4
...

In using this module, I want to import its namespace with * for easier referencing. But in that case it seems I'm only able to access the values of the parameters but cannot reset them:

from params import *
def test1():
    print(a)  #this works: printing out the value of params.a

def test2():
    a= 0.5  #doesn't work: does not change the value of params.a 

Trying to reference the variable as params.a = 0.5 also doesn't work (gives a NameError as "params" is not defined)

My question: How can one set/reset the value of a variable that has been imported this way?

Anas Elghafari
  • 1,062
  • 1
  • 10
  • 20
  • 1
    Just to clarify: of course, If I change the import statement to "import params" then I can access the variable by "params.a". However, I would like to know if there is a way to set/reset variable that have been imported with a kleene star *. – Anas Elghafari Jul 17 '14 at 04:39
  • 2
    This problem will happen even if you just define `a` as a global value in the main file other than import from another file. Here is a simliar problem: http://stackoverflow.com/questions/13091357/python-global-local-variables – WKPlus Jul 17 '14 at 05:01

2 Answers2

3

Assigning a = 0.5 in the scope of test2() is local only to that function.

What you want to do is set attributes on the module.

Example:

import params

def test1():
    print(params.a)

def test2():
    params.a = 0.5

See: Short Description of Python's Scoping Rules

Note: It's probably helpful to realize that just about everything in Python is an object of some kind or another and usually has attributes. This includes modules, classes, instances of classes, functions, etc.

Update: Whilst this is discouraged this will do "exactly" what you intended:

foo.py:

from params import *  # noqa


def test1():
    print(a)


def test2():
    global a

    a = 0.5

params.py:

a = 1

demo:

$ python -i foo.py
>>> test1()
1
>>> test2()
>>> test1()
0.5
>>>

Update II: But I ahouls also point as that as stated in my comment(s) this does not actually change the params.a reference. See:

$ python -i foo.py
>>> id(params.a)
140602494749016
>>> id(a)
140602494749016
>>> test1()
1
>>> test2()
>>> test1()
0.5
>>> id(params.a)
140602494749016
>>> id(a)
140602494760472
>>>

Summary: Please use in general the first option presented here.

Community
  • 1
  • 1
James Mills
  • 18,669
  • 3
  • 49
  • 62
  • 1
    Yes, I know I can refer to the var as params.a if I do "import params" instead of "from params import *". But my question is whether I can reset the value of a variable that has been imported with "from params import *" – Anas Elghafari Jul 17 '14 at 04:36
  • 1
    The answer is a resounding "no". Maybe if you declare it ``global`` inside ``test2()``. But even if this works globals are not generally encouraged as a best practice. – James Mills Jul 17 '14 at 04:38
  • I tried "global", does not work. it just declares it as a global variable in that module but does not affect the value in params. – Anas Elghafari Jul 17 '14 at 04:41
  • @AnasElghafari That's precisely correct. Please see explanation in my answer. – James Mills Jul 17 '14 at 04:47
  • Actually, this doesn't even have to do with the function scope itself. Even if I try resetting outside the function, it does not work. It's a bit peculiar that if I decide to import the vars with "from.. import *" then I can not refer to the variable in any way that allows rebinding. – Anas Elghafari Jul 17 '14 at 04:52
  • That's because you cannot rebind attributes of another module by referring to attributes of that module globally. As I said the only way to rebind attributes of a module is to set new attributes on that module. Please refer to the documentation and the links I've provided. This is no "weird" at all -- This is the semantics of Python. – James Mills Jul 17 '14 at 04:58
  • What I meant as peculiar is that, once I choose to import with "from.. import..." then there is no syntax that allows rebinding the value of those imported variables: not by "global", not by prefixing the name of the module. The only way is to change the import statement (or add another a second import statement for the same module.) – Anas Elghafari Jul 17 '14 at 05:13
1

Well, the simple solution that just occured to me is to have 2 import statements for the same module:

from params import *
import params

The first statement allows me to access the value of the variables without having to prefix with "params.", and the second allows me to rebind the variables when needed

params.a = 0.5

I don't know if this in line with best practices (probably not). But it does the job, as far as I can see. Other suggestions are welcome.

Edit: this does not work,, because the value of a will not reflect the updated value:

print(a)  #say it prints 0.2
params.a = 0.5
print(a) # will still print 0.2
print(params.a) #will print 0.5

So the lesson here is that if you import the variables with from params import a then you're stuck with the value of a even if its changes in the original. i.e. it is an import by value not by reference.

Anas Elghafari
  • 1,062
  • 1
  • 10
  • 20