3

I have two modules:

constants.py

def define_sizes(supersample):    
    global SUPERSAMPLE
    global WIDTH
    global HEIGHT
    global LINE_WIDTH

    SUPERSAMPLE = supersample
    WIDTH = 1280*SUPERSAMPLE
    HEIGHT = 854*SUPERSAMPLE
    LINE_WIDTH = 1*SUPERSAMPLE

define_sizes(1)

test.py

from constants import *


print(WIDTH, HEIGHT, LINE_WIDTH)
# Draw something

define_sizes(4)

print(WIDTH, HEIGHT, LINE_WIDTH)
# Draw the same thing, but bigger

The result is:

1280 854 1
1280 854 1

I would expect to get:

1280 854 1
5120 3416 4

Why is that? What am I missing? Can I fix it to give expected results?

Fenikso
  • 9,251
  • 5
  • 44
  • 72
  • 2
    One way would be to `return` the values rather than use globals, so you could do `WIDTH, HEIGHT, LINE_WIDTH = define_sizes(4)` – MrAlexBailey Mar 27 '15 at 12:33
  • @Jkdc - Sure. In this case, which is minimal working example. In my real-life script, there are maybe 50 constants which need redefinition? It would be very ugly to return them all and then put them in global namespace anyway... – Fenikso Mar 27 '15 at 12:35
  • I am trying to hack existing code to execute twice with different `SUPERSAMPLE` constant in one run. – Fenikso Mar 27 '15 at 12:37
  • You might consider [this](http://stackoverflow.com/a/2298388/1068887)? – adarsh Mar 27 '15 at 12:39
  • @adarsh - Yes, I have considered this, however that would require me to refactor existing code which uses those 50 constants. It seems to be the only reasonable way. Anyway, `Python does not support globals shared between several modules: this is a feature.` explains why it does not work. – Fenikso Mar 27 '15 at 12:45
  • I think it's a design choice to promote better programming practices because it's a bad practice to use global variables and implicitly change them during runtime as it causes problems while debugging, although using constants are alright. You could even think about using a class; I have also added an answer stating the documentation. – adarsh Mar 27 '15 at 12:49
  • Thanks all for the answers. A lot of good ideas there. I have accepted solution which is the best for my situation, e.g. as less rework in my real-life equivalent to test.py as possible. – Fenikso Mar 31 '15 at 08:09

4 Answers4

1

You can't share global variables across modules in Python as such. You could use a config module, by importing it where ever you need it in the project and since there would only be one instance of it, you can use that as a global. It is described in the documentation.

adarsh
  • 6,738
  • 4
  • 30
  • 52
  • However, can I use this `config` and `mod` concept to redefine all constants by just changing one parameter? It seems I would need to duplicate all constant code in `mod` module. – Fenikso Mar 27 '15 at 13:04
  • You could have a function that alters all the variables you need to modify using the parameter value as the argument. I am not sure what you mean by "I would need to duplicate all constant code in mod module" – adarsh Mar 27 '15 at 13:10
  • Right, I had to give it more thought. You are right. – Fenikso Mar 27 '15 at 13:31
1

I recommend something like the answer by adarsh for "real code", but if what you need to do is just to hack some existing code as quickly as possibly, you might try reimporting the constants, with a test.py like:

from constants import *

print(WIDTH, HEIGHT, LINE_WIDTH)

define_sizes(4)

from constants import *

print(WIDTH, HEIGHT, LINE_WIDTH)

You would also have to modify constants.py so that it doesn't reset the SUPERSAMPLE to 1 when reimporting, something like:

def define_sizes(supersample):
    global SUPERSAMPLE
    global WIDTH
    global HEIGHT
    global LINE_WIDTH

    SUPERSAMPLE = supersample
    WIDTH = 1280*SUPERSAMPLE
    HEIGHT = 854*SUPERSAMPLE
    LINE_WIDTH = 1*SUPERSAMPLE

if not 'SUPERSAMPLE' in globals():
    define_sizes(1)
skagedal
  • 2,323
  • 23
  • 34
  • "You would also have to modify `constants.py` so that it doesn't reset the `SUPERSAMPLE` to 1 when reimporting" That holds only on `reload(constants)`. If you do `from constants import *` several times, the version cached in `sys.modules` is used so that they aren't reset each time. – glglgl Mar 27 '15 at 13:14
1

In this situation I'd probably do

class Sizes(object):
    def __init__(self, supersample=1)
        self.SUPERSAMPLE = supersample
    def resize(self, supersample)
        self.SUPERSAMPLE = supersample
    @property
    def WIDTH(self): return 1280*self.SUPERSAMPLE
    @property
    def HEIGHT(self): return 854*self.SUPERSAMPLE
    @property
    def LINE_WIDTH(self): return self.SUPERSAMPLE

sizes = Sizes(1)
resize = sizes.resize

which I then can use like

from constants import sizes as s

print(s.WIDTH, s.HEIGHT, s.LINE_WIDTH)
# Draw something

s.resize(4)

print(s.WIDTH, s.HEIGHT, s.LINE_WIDTH)
# Draw the same thing, but bigger
glglgl
  • 89,107
  • 13
  • 149
  • 217
1

Once you did from constants import * names WIDTH, HEIGHT, LINE_WIDTH were imported into this module's namespace, they referring to the values (objects) they had at the time of import.

Even if constants.WIDTH is overwritten, variable test.WIDTH still references the old value.

The cleanest solution is to access those values through the constants module:

import constants


print(constants.WIDTH, constants.HEIGHT, constants.LINE_WIDTH)
# Draw something

define_sizes(4)

print(constants.WIDTH, constants.HEIGHT, constants.LINE_WIDTH)
# Draw the same thing, but bigger
warvariuc
  • 57,116
  • 41
  • 173
  • 227