Hello World! What’s the scope?

0
3047

By Jeff Kazmierski, Copy Editor

Last week we looked at the basics of functions in Python. An easy way to think about functions is they’re a lot like miniature programs within programs. They do everything a program does, taking input in the form of arguments, doing something with it, and producing output (usually) in the form of returned values.

Java and C programmers will have noticed you don’t have to tell Python what, if anything, the function returns – you can return a value, or not, depending on what you need the function to do. On the other hand, if you have a variable assignment that relies on a function’s returned value, you’ll get bad results if your function doesn’t return anything. And if you call a function that returns a value, but don’t do anything with it, it’s kind of a waste.

This relationship between variables and functions leads us to today’s discussion, variable scope.

What is scope? In a nutshell, it’s the limits within which a variable can be used. In Python there are two basic types of scope, global and local (there’s a third one called ‘nonlocal’ but it’s beyond the “scope” of this column).

Think Local

Any variable that is initialized and used inside a function is a local variable. Locals exist in memory only as while the function is active, and any values stored in them are lost when the function ends. Unless, of course, they’re passed to another variable as a return value. Take a look at this short script:

def ask_a_question(question):

    response = None

    while response not in (‘y’, ‘n’):

        response = input(question).lower()

    return response

answer = ask_a_question(“May we have your liver? “)

print(answer)

The function ask_a_question() creates a local variable, response, which is then passed to the variable answer. We can then print the value of answer, but if we try to do the same to response, we get this result:

>>> print(response)

Traceback (most recent call last):

  File “<pyshell#4>”, line 1, in

    print(response)

NameError: name ‘response’ is not defined

>>>

because the variable response only exists within its function. In fact, if we create a second function that uses another variable called response, it has no effect on the first variable response, because both exist independently from each other and only as long as their functions are running.

Act Global

Global variables are initialized in the body of the program, usually before any functions are coded, and can be accessed anywhere within the program. With a few exceptions, they can also be changed anywhere in the program. Take a look at the Tic-Tac-Toe program.

After the initial comment lines, you’ll see several lines of variable declarations. These are the program’s global variables. They’ll be used in several different functions, and we also don’t plan on changing them, so it makes sense to declare them as globals from the start. This saves time and several lines of code. We can call the global variables whenever we need them instead of trying to code the values in every function they’re used in.

A short program

Don’t worry too much if none of this makes a lot of sense. It takes some practice before it’s really clear. Another short script should help:

# globallocal.py

# demonstrates global variables

value = 10

def read_global():

    print(“Inside the local scope of read_global(), value =”,value)

def shadow_global():

    value = -10

    print(“Inside the local scope of shadow_global(), value =”, value)

def change_global():

    global value

    value = -10

    print(“Inside the local scope of change_global(), value =”, value)

print(“In the global scope, value =”,value)

read_global()

print(“Back in the global scope, value =”,value)

shadow_global()

print(“Back in the global scope, value =”,value)

change_global()

print(“Back in the global scope, value =”,value)

Hopefully it makes a little more sense now.

Go ahead and plug in the code for the Tic-Tac-Toe program in the sidebar. Pay attention to the comments, and notice where the global variables are used and how. Later, if you’re looking for another challenge, try adding a randomizer to enable a variety of win/lose messages. For an even bigger challenge, rewrite it as a 3-D Tic-Tac-Toe game.

Tic-Tac-Toe

# tictactoe.py

# a simple tic tac toe game

# illustrates tuples, lists and variable scope

# define globals

X = ‘X’

O = ‘O’

EMPTY = ‘ ‘

TIE = ‘TIE’

SQUARES = 9

# a tuple defining the best moves on the board

# for the computer player

# moves are listed in order from best to worst

BEST = (4, 0, 2, 6, 8, 1, 3, 5, 7)

# a two-dimensional tuple containing

# all the winning combinations available

WINS = ( (0, 1, 2), (3, 4, 5), (6, 7, 8),

         (0, 3, 6), (1, 4, 7), (2, 5, 8),

         (0, 4, 6), (2, 4, 6) )

# display game instructions

def gameHelp():

    print(“””

You have proven my equal in Rock-Paper-Scissors!

Are you ready for a real challenge?

Prepare for the ultimate test – Tic Tac Toe!

You will make a move by entering a number from 0 to 8, as shown on the grid below:

     0 | 1 | 2

    —+—+—

     3 | 4 | 5

    —+—+—

     6 | 7 | 8

Prepare for battle!n

“””)

# asks the player a yes or no question.

def yesorno(question):

    ans = None

    while ans not in (‘y’, ‘n’, ‘yes’, ‘no’):

        ans = input(question).lower()

    return ans

# ask for a number in a range

def getMove(question, low, high):

    ans = None

    while ans not in range(low, high):

        ans = int(input(question))

    return ans

# ask if the player wants to go first

def firstMove():

    if yesorno(“Do you want to go first? “) in (‘y’, ‘yes’):

        pl = X

        cp = O

        print(“nYour confidence is inspiring.”)

    else:

        pl = O

        cp = X

        print(“nI will crush you like an egg!”)

 

    return pl, cp

# create a new blank grid

def newGrid():

    grid = [EMPTY, EMPTY, EMPTY,

            EMPTY, EMPTY, EMPTY,

            EMPTY, EMPTY, EMPTY]   

    return grid

# show the grid on the screen

def showGrid(grid):

    print(“nt”,grid[0],”|”,grid[1],”|”,grid[2])

    print(  “t—+—+—“)

    print(  “t”,grid[3],”|”,grid[4],”|”,grid[5])

    print(  “t—+—+—“)

    print(  “t”,grid[6],”|”,grid[7],”|”,grid[8],”n”)

# make a list of available moves

def legalMoves(grid):

    moves = []      # an empty list

    for square in range(SQUARES):

        if grid[square] == EMPTY:

            moves.append(square)

    return moves

def whoWon(grid):

    # check to see if anyone has won

    for row in WINS:

        # if all the squares in a row are the same and not empty

        if grid[row[0]] == grid[row[1]] == grid[row[2]] != EMPTY:

            # the winner is whoever owns the squares

            winner = grid[row[0]]

            return winner

    # if all the spaces are filled but no one won

    if EMPTY not in grid:

        return TIE

    # otherwise, keep playing

    return None

def playerMove(grid, pl):

    # get the human player’s move

    legal = legalMoves(grid)

    move = None

    while move not in legal:

        move = getMove(“Enter your move (0 – 8): “, 0, SQUARES)

        if move not in legal:

            print(“nThat square is occupied.  Try again.”)

    print(“…”)

    return move

def computerMove(grid, cp, pl):

    # make the computer move

    # make a local copy of the grid to work with inside the function

    grid = grid[:]

    # if the computer can win, do it

    for move in legalMoves(grid):

        grid[move] = cp

        if whoWon(grid) == cp:

            print(“I choose square”, move)

            return move

        # we’re done here, so undo it

        grid[move] = EMPTY

    # block the human if possible

    for move in legalMoves(grid):

        grid[move] = pl

        if whoWon(grid) == pl:

            print(“I choose square”, move)

            return move

        # we’re done here, so undo it

        grid[move] = EMPTY

    # if no one can win on the next move, pick the best one

    for move in BEST:

        if move in legalMoves(grid):

            print(“I choose square”, move)

            return move

def nextTurn(turn):

    # switch turns

    if turn == X:

        return O

    else:

        return X

def endGame(winner, cp, pl):

    if winner == TIE:

        print(“It’s a tie!”)

    elif winner == cp:

        print(“I won!  Nyah nyah!”)

    else:

        print(“Enjoy your victory while you can, human.”)

        print(“Have you seen ‘Terminator’?”)

def main():

    gameHelp()

    # assign computer and player pieces

    pl, cp = firstMove()

    turn = X

    grid = newGrid()

    showGrid(grid)

    # all that was preparation

    # now the game really begins

    while not whoWon(grid):

        if turn == pl:

            move = playerMove(grid, pl)

            grid[move] = pl

        else:

            move = computerMove(grid, cp, pl)

            grid[move] = cp

        showGrid(grid)

        turn = nextTurn(turn)

    # who won?

    winner = whoWon(grid)

    endGame(winner, cp, pl)

main()

Comments

comments