1

I recently noticed something that I was not expected with python, it is possible to check/access a variable inside a function that is declared in the main scope, e.g.:

def my_func(mval):
    print ("parameter:", mval)
    print ("myvar:", myvar)
    #myvar = "value" this is not allowed

print ("IN MAIN")
myvar = "123"
my_func(999)

Output:

IN MAIN
parameter: 999
myvar: 123
  1. Why my_func can access the myvar? And then why it fails when trying to change the value?
  2. Is there a way to prevent this behavior, so in this case it would say variable myvar is not defined?

I know we could use global to make this work and it would make sense since we are explicitly saying there is a variable outside of the function scope that we want to access.

I find this tricky because it could lead to errors..

3 Answers 3

1

myvar is in the global scope so its global whether you declare it or not. When read, python looks in the function's local scope and then falls back to the module global scope where the variable is defined. When written, python has a problem. Should it assign the variable in the local function scope or the global scope? The global keyword is used in the function and tells that function how to resolve that problem. The variable is never declared global, the function is told to treat that variable as global.

def my_func(mval):
    global myval
    myval = 'foo'  # set in global scope
    myval2 = 'bar' # set in local scope
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the explanation. I know that the global keyword would solve the issue. Just find it weird the behavior without the global keyword. It might leads to errors if one types by mistake a variable that should not be used/valid in the current scope. Is there a way to avoid it? So it would return an error?
Python checks multiple scopes when reading variables as a convenience. As long as you write a local variable before you read it, there is no confusion. By the time you read it, its been created in the local scope. The risk is when you mistakenly read a variable that you think should be in the local scope but is really in the global scope. In that case you want an error but you get bad data. Dynamic languages stand up and bite us in the rear from time to time!
1

When you display the content of myvar in my_func with the exact code you gave as reference, it will check the global variable table to see if it was previously defined in the execution, which is why it works. Then, when you try to assign a value to it in the function by uncommenting myvar = "value", it tries to define myvar as a local variable of my_func, which shadows the reference to the global instance of myvar.

I'm not exactly sure how using the global keyword would cause any issue, but it should be working as intended. Here is an example of how you can use it:

def my_func(mval):
    global myvar
    print ("parameter:", mval)
    print ("myvar:", myvar)
    myvar = "value"

print ("IN MAIN")
myvar = "123"
my_func(999)
print("myvar after my_func:", myvar)

Output:

IN MAIN
parameter: 999
myvar: 123
myvar after my_func: value

If you really don't want to use the global keyword, you can achieve the desired behavior by passing myvar as a parameter of my_func and return the modified value to reassign it in the main scope:

def my_func(mval, myvar):
    print ("parameter:", mval)
    print ("myvar:", myvar)
    myvar = "value"
    return myvar

print ("IN MAIN")
myvar = "123"
myvar = my_func(999, myvar)
print ("myvar after my_func:", myvar)

Output:

IN MAIN
parameter: 999
myvar: 123
myvar after my_func: value

If my_func was already built to return a value, remember that you can return multiple values in Python. Therefore, assuming my_func would return a variable called returnvar, the above code could be written as:

def my_func(mval, myvar):
    returnvar = mval + 1
    print ("parameter:", mval)
    print ("myvar:", myvar)
    myvar = "value"
    return myvar, returnvar

print ("IN MAIN")
myvar = "123"
myvar, returnvar = my_func(999, myvar)
print ("myvar after my_func:", myvar)
print ("returnvar:", returnvar)

Output:

IN MAIN
parameter: 999
myvar: 123
myvar after my_func: value
returnvar: 1000

Comments

0

When you define your function it is not evaluated for correctness only for syntax, so nothing is raised. Once you call the function myvar is already defined in the global scope so the function is valid. Also if you uncomment myvar = 'value' my_func will also evaluate without exception as long as myvar is define before the function is called.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.