2

Using Python 2.7.6 as provided by Ubuntu 14.04.

I have some functions that reference variables that are defined by the caller. I wanted a way to find if the caller had set the variable(s) (where if they hadn't, I'd set a default value locally), so I found this answer on StackOverflow.

The problem I'm having is that the checking of the existence of the variable makes it invisible/not-exist to the following code.

For the convenience of anyone investigating this, the code block below should be able to be cut-n-pasted directly to a file on your system for testing.

#!/usr/bin/env python

# Tested on Python 2.7.6 from Ubuntu 14.04.

def calledfunction():

  print globals()
  print
  print locals()
  print

  # Try commenting the next five lines out.  While they're here, the variable
  # becomes invisible to the print statement below.
  if not 'globalvar' in globals():
    globalvar = "created within the function"
    print "assigning variable locally"
  else:
    print "variable assigned globally"
  #  global globalvar
  # If you uncomment the above line, the variable is visible, but Python
  # prints a syntax warning.

  print
  print globals()
  print
  print locals()
  print

  print "the variable 'globalvar' is:", globalvar
  print

# -----------------

print "calling function before the variable is defined"
print
calledfunction()

globalvar = "created outside the function"

print "calling function after the variable is defined"
print
calledfunction()

My expectation is that the 'variable' in globals() test should not be causing the variable to disappear from the visibility of the print statement following it. Am I not correct in my expectation? (Python seems to think I'm not.)

Community
  • 1
  • 1
  • I'm a bit confused about what you are trying to do. The function you define will perceive globals that are part of the module the function is defined in. That is, the module you control. Therefore, why not just initialise the global variable with a sensible default? – Dunes Oct 06 '14 at 19:27
  • @Dunes: The function is not always in a module I control. It's part of a library that will be imported into other code, which I may or may not control. – Marvin Glenn Oct 06 '14 at 20:41
  • An imported function's globals are the globals of the module it was defined in. Importing it into a different module does not change that. – Dunes Oct 06 '14 at 21:07
  • @Dunes: In my implementation of the actual project file, the Python file may be run standalone or 'import'ed. When it is 'import'ed, the variables defined by the main code do not get set because the main code is not run, therefor they are not in the globals. – Marvin Glenn Oct 06 '14 at 22:24

3 Answers3

2

Note that when you start dynamically inspecting globals like this, people start to wonder ... with that said, here's a working version of your code that works so long as you only "read" from the global variable.

def calledfunction():
  default_local = 'some default'
  var = globalvar if 'globalvar' in globals() else default_local
  print var

# -----------------

print "calling function before the variable is defined"
print
calledfunction()

globalvar = "created outside the function"

print "calling function after the variable is defined"
print
calledfunction()

Note that within a function, a variable name is either global or it's local (and python3.x adds nonlocal to the bunch). However, a variable name cannot be either global or local depending on how the function is called.

A better way is to just use keyword arguments:

def calledfunction(var="Created within the function"):
    print var

calledfunction()  # Created within the function
calledfunction(var="Created by the caller")  # Created by the caller

There are some gotchas when you want to create new mutable objects this way, but they are well known and documented with work-arounds.

Community
  • 1
  • 1
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • In your example, don't you create a global variable called `globalvar` if one does not already exist. eg. `del globalvar; calledfunction(); "globalvar" in globals()` evaluates to true. – Dunes Oct 06 '14 at 19:08
  • The addition of the extra **global [varname]** before the test worked. (Though with the number of loose variables I have like this, putting them in the call makes a mess.) – Marvin Glenn Oct 06 '14 at 19:17
  • Noting: While this answer solved the problem at hand; I am still curious as to why looking for the variable in 'globals()' would cause it to not be visible anymore to the function that tries to use it. Note the existence of the variable in question in the printing of the 'globals()' in my test code. It's like the _observer effect_ for Python code. – Marvin Glenn Oct 07 '14 at 03:05
1

You are assigning to globalvar in your function (in the case where that name is not found in globals(). Therefore it is local to the function.

You can declare the variable global in advance by placing this somewhere in your function (usually at the beginning):

global globalvar

But if you need to look at variables defined by a calling function, you are probably doing something wrong. The function should pass you the data you need explicitly.

kindall
  • 178,883
  • 35
  • 278
  • 309
1

As I understand, you have your code setup something like the following:

library.py

def function():
    default_local = "local"
    var = globalvar if 'globalvar' in globals() else default_local
    print(var)

if __name__ == "__main__":
    globalvar = "main"
    function() # prints "main"

user.py

from library import function

function() # prints "local"
globalvar = "user"
function() # prints "local"
import library
library.globalvar = "user"
function() # prints "user"

Why not write your library as the following and save yourself a lot of hassle with bizarre global lookups.

new_library.py

globalvar = "main" if __name__ == "__main__" else "imported"

def function():
    print(globalvar)

if __name__ == "__main__":
    function() # prints "main"

user.py would now print "imported" twice and then "user".

Dunes
  • 37,291
  • 7
  • 81
  • 97
  • I use a similar test as the one in your last code chunk to trigger the main code that wraps the functions for the command line. I rarely use the "from x import y" form, as I like having the library_name.function() form on imports to avoid namespace collisions. There can be times where these 'global' variables would be set by code importing them, I just don't want anything importing it to _have_ to set it as most will not have that need. – Marvin Glenn Oct 07 '14 at 02:57
  • By using the "from" import I was just trying to show that the function always executes in the context of the module it was defined in. I was also trying to show that you should set the initial value of the global variable to be that of your local variable. That way the global always exists, and thus you don't need to test whether it exists or not. However, it can be changed if module is being run as the main module, or if the user of the library wants to set a different value. – Dunes Oct 08 '14 at 10:14