Global scope

New:

  • scope
  • global scope

Variables and function names are identifiers (and in future you'll learn about other kinds of identifiers). Scope is the area of the source code where an identifier is accessible (can be used).

A variable created outside of functions has global scope. It's accessible within code that's located anywhere, including inside of functions' bodies. (Of course, except for the cases when the call happens earlier than variable's definition). Here's a simple example:

def gen_title(name):
    return manner + " the " + name


manner = "Her Majesty"
print(gen_title("Pumpkin"))
Her Majesty the Pumpkin

The function utilizes two identifiers: its own parameter name and a globally-scoped variable manner. The value of manner is read at the time of the call. Since variables can be redefined, another call may end up reading a different value:

def gen_title(name):
    return manner + " the " + name


manner = "Her Majesty"
print(gen_title("Pumpkin"))

manner = "Her Excellency"
print(gen_title("Boop"))
Her Majesty the Pumpkin
Her Excellency the Boop

Function names are also globally-scoped identifiers. The only way to use a function name identifier is to call the function1. So, a call may be contained in a body of another function. Below, gen_title contains a call to gen_cute:

def gen_cute(word):
    return "Little Cutey " + word

def gen_title(name):
    return manner + " the " + gen_cute(name)


manner = "Her Majesty"
print(gen_title("Pumpkin"))
Her Majesty the Little Cutey Pumpkin

At this point you may be wondering why is this even worth discussing. Variables and functions can be used anywhere, so?.. Well, there exist other cases where the scope of an identifier is not global. The next lesson will explain one. And in the next courses you'll encounter more syntactic structures with limited scope.


Our examples above were rather plain, so let's work on a real problem to further illustrate globally-scoped variables. The task is to calculate the number of months passed between some year and the current year. Open a new repl and write a function months_since which takes a year as an argument, and returns the number of months between that year and the current year. You need to store the current year as a separate global variable.

Your solution might look like so:

current_year = 2020

def months_since(year):
   return (current_year - year) * 12

print(months_since(2003)); # โ†’โ—ผ 204

Since current_year is globally-scoped, you can write other functions which also use that variable. For example, let's add a weeks_since function:

def weeks_since(year):
   return (current_year - year) * 52

print(weeks_since(2003));  # โ†’โ—ผ 884

And a weeks_until function:

def weeks_until(year):
   return (year - current_year) * 52

print(weeks_until(2031));  # โ†’โ—ผ 572

Now go ahead and, in addition to those 3 functions, write two more:

  • years_until_abba100 which calculates how many years left until 100th anniversary of ABBA, a Swedish pop supergroup formed in Stockholm in 1972 by Agnetha Fรคltskog, Bjรถrn Ulvaeus, Benny Andersson, and Anni-Frid Lyngstad ๐Ÿ’ƒ๐Ÿ•บ.
  • months_until_halley which calculates how many months left until Halley's comet returns to our solar system in 2061.

Having a common variable outside of functions is fine in small programs. But as soon as your program reaches a few dozen lines, problems might arise:

  • It's hard to read the code because you have to jump between three places: variable creation, function definition and function call.
  • It's hard to make sure the variable's value was not changed in between the calls, intentionally or by mistake.
  • Functions have additional dependencies. Dependencies often lead to difficult bugs.

Try to write functions that only take values via arguments, but not by accessing external variables from the body. This way you, the programmer, have more control. Bugs are still possible, but they'll be easier to fix.

The described properties of scope are specific to Python and the majority of mainstream programming languages. There exist other languages (e.g. Haskell, Eiffel, Newspeak) where scope is more limited and strict. In some of them it is impossible for a function to access outer variables, for example.

Summary:

  1. Function's body is an isolated scope inaccessible from outside.
  2. return transports a value, but does not affect the scope.
1

For now. In future, you'll learn about an advanced use-case: using a function name identifier as if it were a variable.