aerijman
aerijman

Reputation: 2762

override variable imported from module in python

a.py:

import sys

h = 'this is h from a.py'

for i in sys.argv:
    if i in ['-h']:
        print(h)

b.py:

import sys
from a import h


h='this is h from b.py'
for i in sys.argv:
    if i in ['-h']:
        print(h)

Then I get:

>$  python b.py -h
 this is h from a.py
 this is h from b.py

Shouldn't 'h' have been overridden? Or is a.py accessing the '-h' argument given to b.py?

Thank you

Upvotes: 0

Views: 2209

Answers (2)

Philogy
Philogy

Reputation: 293

The main question

You can overwrite the variable from b.py with the following code:

import a
a.h = 'actually replaced value'

Note: actually adding that to a.py will still give the following result:

>$  python b.py -h
 this is h from a.py
 this is h from b.py


Creating the wanted behavior

First note that you can verify that the value of h gets changed with the following code:

a.py:

import sys

h = 'this is h from a.py'

for i in sys.argv:
    if i in ['-h']:
        print(h)


def what_is_h():
    print('h:', h)

b.py:

import sys
from a import h, what_is_h
import a

what_is_h()
a.h = h = 'this is h from b.py'
what_is_h()

for i in sys.argv:
    if i in ['-h']:
        print(h)

This results in the following:

this is h from a.py
h: this is h from a.py
h: this is h from b.py
this is h from b.py

The first what_is_h call shows the original h value and the second shows the modified value. The problem is a.py and b.py seem to have different versions of the variable h this is what is called scope. When you use the import statement python parses your file and runs the code. The way you can prevent this automatic running of the code is by placing it in a function, importing it and running it from b.py preferably passing h as an argument (since global state is usually bad practice in python).

"What if I want to run a function from a.py too but only if I run it directly?"

Now if you want a function executed when you run a.py or b.py but run the function in a.py only if it is run directly you can add the following to the bottom of your script file:

if __name__ == '__main__':
    my_function()

This will run the function my_function only when you execute $> python a.py. Note that when importing a.py it will not run my_function unless run otherwise.

How does this work? __name__ is a built-in global variable that stores the "name" the python interpreter gives to python scripts. The file that is run is given the __name__ '__main__'. Now when importing something from a.py __name__ will equal 'a' and my_function will not run.

Further style notes and tips for your code

  • equality:

instead of

if i in ['-h']:
  # ... your code

do

if i == '-h':
  # ... your code

if you want to check for multiple flags later you can change it.

  • checking if something is in a list or sequence-like object:

if you only want to check if -h is in sys.args you can do:

if '-h' in sys.args:
  # ... your code

instead of

for i in sys.args:
  if i == '-h':
    # ... your code
  • parsing, handling and dealing with command line arguments

If you plan to do more complex things with arguments there is library that should be included by default with every installation of python, namely the argparse library.

I hope I was able to help.

Upvotes: 2

Daniel Lima
Daniel Lima

Reputation: 995

When you import a, all the code inside a.py is executed, thence you will always have the printed output:

this is h from a.py

To avoid it, you can change a.py to:

import sys

h = 'this is h from a.py'

if __name__ == '__main__':
    for i in sys.argv:
        if i in ['-h']:
            print(h)

This way, the code inside the if clause will only be executed when you call python a.py -h, and not when you call python b.py -h.

Upvotes: 1

Related Questions