Sunday, May 27, 2012

Variable Scope

I know this was probably confusing, so let's go over it in detail.

Scope


Again, scope is the word we use to describe the ability to access certain variable names in a certain place in our code.  Languages vary on how scope works, but we'll stick with python.

So far, we've learned about two kinds of scope: function scope and global scope.  The two are separate from each other, but there are ways to cross over, but first let's explore what scope means for our programs.

Open a new file and save it where you want (I recommend calling it "scopeTest.py" and putting in a folder called "tests").  Lets start by creating a function and a few variables in various scopes.


globalVar = "This is global"


def myFunction():
  localVar = "This is local"
  print "myFunction - localVar:  " + localVar
  print "myFunction - globalVar: " + globalVar


myFunction()
print
print "global - globalVar: " + globalVar
print "global - localVar:  " + localVar




Can you figure out what will happen?  Let's run it and see if you're right.



Oops.  We couldn't access the variable localVar outside the function.  Ok so let's get rid of the line where we print that out

print "global - localVar:  " + localVar

Now let's make things a little trickier.  Let's change our function to this.


def myFunction():
  localVar = "This is local"
  globalVar = "This is local"
  print "myFunction - localVar:  " + localVar
  print "myFunction - globalVar: " + globalVar



Ok now let's run it and see what happens.



Surprised?  In one place, globalVar was one string, and in the other it was a different one at the same time.  Apparently, when you assign to globalVar inside the function, Python creates a new variable with function scope and hides or shadows the global-scope globalVar.  In order to access the global version of globalVar, we need to add this line underneath the def line.

global globalVar



That should do it.  Now run it.



There we go.  Now when we assign "This is local" to globalVar, we're assigning it to the global version.  You must do this whenever you want to assign to a global variable inside a function.

I hope that clarifies a few things for you about scope.  I know it's just another thing to get your mind around, but trust me: it's not that difficult once it clicks.  All these things make sense together; it just takes a little practice.


Saturday, May 26, 2012

Lesson 8 - Tuples and String Formatting

All right, last time we wrote our own functions, and now we're going to build on that using a few useful constructs of Python.

Tuples


What is a tuple?  A tuple is kind of like a list (see Lesson 4) except that it is immutable (like a string is immutable).  Tuples don't change once you create them; they will always contain the same objects/primitives in the same order.  (The objects themselves can be modified though, and I'll show you what I mean in a little bit).

Let's go back to our trusty old command line interpreter.  If you don't remember how to open it, just open a command prompt/terminal and type python.



Ok, we're ready.  To form a tuple, use parentheses like this

t = (1, 2, 3)
print t



Now you can access each item in the tuple just like a list (remember that both tuples and lists are zero index based, meaning that the "first" element is number 0).

print t[0]
print t[1]
print t[2]


Now, unlike a list, you cannot add, remove, or change the contents of a tuple.  Try this

t[1] = 42



See?  It won't let you.

So you may be wondering what the purpose of tuples are.  Simply put, tuples are just a way to pass around a sequence object (like a list) without worrying about the elements or size changing.


A Tuple of One

Incidentally, if you want to create a tuple with one item in it, you have to put a comma inside the parentheses.

t = (1,)
print t



Otherwise it treats the parentheses like they are part of a math equation, and you will only get the one element.

t = (1)
print t




Tuples in Functions

One of the most practical applications of a tuple is in regard to functions.  In the last lesson, I hinted that a function could return more than one value (which is rather unique to Python) and this is how it does it.

Open a new file and add this function


def valueAndDigits(strNum):
  return int(strNum), len(strNum)




Python treats that return as a tuple.  Now for the cool part.  You see, in Python you can do cool assignment statements like this

a, b = valueAndDigits("147")



Cool, huh?  With this statement, Python assigns the first item of the tuple to a and the second to b.  In order for this to work, you have to be exact—you can't have too many or two few variables or you'll get an error.  Just to see it work, let's print them out

print a
print b



Now run it and see what happens.


Exactly what we expected.  (Granted this isn't the most useful function, but it's only for demonstration purposes).

To see that the return value is indeed a tuple, you can do this


c = valueAndDigits("2048")
print c




And run it



And there you have it.

And now for something completely different . . .


String Formatting

This is perhaps one  of the most useful things in Python (and certain other languages).  Up until now, whenever we wanted to combine strings, we had to concatenate them.  It works, but it's not very pretty and if you have a really big string you want to create, it can get confusing.

To use string formatting, you create a string with markers inside it.  Each marker begins with a % sign and end in a letter which indicates the type of information you want to show.  Strings are %s and integers are %d (I think the d stands for 'decimal' because you could also do %x to show the integer in hexadecimal format).  After that string, you place another % sign and then a tuple with the different variables and values you want to display.  The whole thing looks something like this

"%s is a gr%d language!" % ("Python", 8)

which when run gives you

"Python is a gr8 language!"


Now let's try it.  Open up a new file and type this


name = raw_input("Please type your name: ")
greeting = "Hello, %s.  Welcome to Python!" % (name)
print greeting




Now run it, type a name, and see what happens.



We created a string without having to use any concatenation.

Certain markers have different options that go with it.  For instance, pretty much all markers let you specify a number for the number of spaces you want to fill.   To demonstrate, lets start a new file.  Well first create a function to display a name and number with a certain number of spaces, which will create our table.


def printNameAndNumber(name, num):
  print "%20s %10d" % (name, num)


Now we'll need some names.


printNameAndNumber("Sally", 27828)
printNameAndNumber("Bob", 58732)
printNameAndNumber("Joe", 91237)
printNameAndNumber("Octavian", 11263)
printNameAndNumber("Olivia", 82373)




Now run it.


There you go.

Now the most useful I have saved for last.  For integers (and floats) you can tell the string formatter to put a certain number of leading zeros onto a number.  To do this, simply put a zero before the number in a %d marker.  To demonstrate, let's change our function to this


def printNameAndNumber(name, num):
  print "%20s %010d" % (name, num)


Now run it and see what happens.



See? The 0 before the 10 made all the difference.


Putting It All Together

Now let's revisit the function we created last time—the one that turned an integer into a dollars-and-cents string—and let's use tuples and string formatting this time.

Start a new file, and lets start by creating a function that takes our integer, and returns the dollars and cents.

def dollarsAndCents(amount):
  return amount / 100, amount % 100

There we go.  Now for a function that takes an amount and creates a string, but this time we'll use string formatting.  We'll also use the function we just wrote.

def dollarString(amount):
  dollars, cents = dollarsAndCents(amount)
  return "%d.%02d" % (dollars, cents)

But wait!  We're using the variable amount in two places.  Won't that mess things up?  Actually no.  Remember what we said last time about scope?  Each function variable has its own variable scope, so we can reuse names that can't be accessed by this function.  In this case, amount cannot be accessed by dollarString() because it exists within the function scope of a different function.  If, however, amount had been declared outside the functions in the global scope, then we would have two variables called amount.  We still would be okay inside the function unless we used the global keyword which tells Python that we want to use the variable amount from the global scope, but even that is only because we declared amount as a local variable (in the parameters), and it hides (or shadows) the global variable.   If this is confusing, maybe this will help.  (I'll try to post a more detailed explanation of scope later.)

Ok, now that we have our functions, lets have more fun with tuples.  This time, we'll put them inside a list of items with prices.


items = [
  ("Milk",   359),
  ("Bread",  142),
  ("Cheese", 520),
  ("Coffee", 510),
  ("Bacon",  466)
]

Ok now we'll iterate through them and print them out in columns


for item in items:
  print "%10s $%5s" % (item[0], dollarString(item[1]))



Don't worry if that looks a little complicated.  Just think about it in pieces.  We have the string for the formatting

"%10s $%5s"


that will give us two strings—the first to fill up 10 characters and the second to fill up 5 characters.  (And we'll have the dollar sign lined up on the left to look all pretty.)

Then we have the % that tells Python to do the string formatting.  Then we have our tuple.

(item[0], dollarString(item[1]))

The first item is just pulled right out of the current item in the list, and the second parameter is taking the second item and putting it into our function.  If we didn't have functions, this would look a lot more complicated.

So after all that, you should have something like this.



Now for the moment of truth!  Run that code!



Beautiful!  It worked.  You're well on your way into the world of programming!


Review

Here's what we did today.
  • A tuple is an immutable sequence
  • You create a tuple by placing objects and primitives inside parentheses with commas
  • You cannot change the items in a tuple, but you can modify the actual objects inside (provided they too are not immutable)
  • You can use tuples to return more than one value from a function
  • You can use tuples to assign objects and primitives to multiple variables at the same time
  • You can use string formatting to quickly construct a string from various pieces of data
  • String formatting markers start with a % sign and end with a letter for the time of data you want to place there (eg s for string and d for integer)
  • Numbers between the % and the designated letter determine how many spaces the data will fill and what character will fill the extra space
That's all for now!  Happy programming!








Lesson 7 - Custom Functions

Hey!  It's been a while!  Things have been busy, and I haven't gotten around to updating the blog until now.  So without further ado . . .

Custom Functions

Last time, we learned all about loops and some of the things loops can help us do.  Now let's move on to writing your own functions.

A function, if you'll remember, is a bit of code that you pass arguments and get a return.  This way you can do complex operations over and over without having to copy code all over the place.  We've seen functions that turn numbers into strings (and vice versa) and the like, but we haven't written our own yet.  This is where things get interesting.

Create a new folder for lesson 7, open up a new code file, and away we go!

First let's just write a basic function and go from there.  Functions look like this


def combineStrings(string1, string2):
  newString = string1 + string2
  return newString



Don't forget to indent because just like loops and ifs, python needs to know what code you want in this block.  This function we just made takes two strings and returns a new string which is a concatenation of those two strings.  Is it useful?  I don't know, but it serves our purpose, which is to learn about functions.

Now run your program.



Hmmm . . . nothing happend.  Why?  Well, functions have to be called before they do anything.  So down below the function, add this line (without indenting).

print combineStrings("Hello", "there")



Now run your program.



Remember that string concatenation does not automatically put spaces in.  If you wanted your function to do so, you could do something like this:


def combineStrings(string1, string2):
  newString = string1 + " " + string2
  return newString


Now run it.



Now the beauty of having a function to do these things is for a situation in which you had a large number of these calls, and then you decide you want to change the way it works (like, say, adding a space like we did above).  If you didn't have this in a function and simply wrote out the code for each string concatenation, you would have to do a lot of typing to change all of them.  With a function, you change it in one place, and you're done!

Functions don't have to return things, but a lot of the time they do.  Functions that don't have a return value generally affect objects in certain ways.  Remember that when you pass an argument into a function, the variable contains a reference to that object.  Anything you do (short of an assignment statement) affects that object.  For example:

def appendIntToArray(i, arr):
  arr.append(i)


a = []
appendIntToArray(1, a)
appendIntToArray(2, a)
appendIntToArray(3, a)
print a



Again, not a whole lot of practical application, but we're just learning for now.  Run your program now



See?  Even though the variable name a isn't in the function, the object stored in a was affected.  This is one of the cool things about object-oriented programming, but it's also one of the pitfalls.  Speaking of pitfalls, that brings me to the next section.


Scope


One of the more difficult (and useful) things to learn in programming is a little thing called scope.  Scope is simply a term for where certain variable names can be used and when they can't.  Scope varies from language to language so we'll concern ourselves with Python's variable scope.

Let me show you what I mean.  First create a new file, and type this


def myFunction():
  myVar = 7
  
myFunction()
print myVar




Think about what you think will happen, and then run the program.



Was that what you expected?  The scope of the variable myVar was only the function myFunction() and so that's the only place you can use it.  This is incredibly useful, but can be a little tricky.  Just keep in mind that when you do a function, everything you do there exists in its own little world that gets destroyed when the function ends.  The only thing that doesn't are variables that exist in other scopes and whatever return value (or values as we'll see later).

Change your file to this


globalVar = 4


def myFunction():
  myVar = 7
  print myVar
  print globalVar


myFunction()
print globalVar




and run it



See?  Inside myFunction we can access variables outside the function, like globalVar.  Variables that can be accessed from anywhere (with a few exceptions and considerations) are said to have global scope.



Now For Something Useful


Let's write some useful functions with some practical applications.  To start, create a new file.

This function will take an amount of money as an int and give you a string.  If you remember, I mentioned before that using floats for money because of certain considerations.  You certainly could if you wanted (and in certain circumstances, you would), but better than that for most simple situations would be to use an integer and remember that you're using cents and not dollars.  Let's start with our dollar amounts

itemPrices = [550, 399, 1071, 407]


These will be the values we want to add together.  We could have had the user input these values (or gotten them out of a database or something), but where we got them isn't important for this exercise.

First, let's write a function that will take an integer like this and turn it into a string with a decimal point and a dollar sign.


def dollarAmount(amount):
  dollars = amount / 100
  cents   = amount % 100
  return "$" + str(dollars) + "." + str(cents)

That percent sign (%) performs what is called a modulo operation which basically means returning the remainder of the division of two integers.  Integer division by 100 will give us the dollar amount, and the remainder is the cents.

Now you may or may not see the problem with this function, but lets run it and see.  But first we'll need to call that function for each item.  Let's use a for loop


for item in itemPrices:
  print dollarAmount(item)



Now let's run it.




See the $4.7?  That should be $4.07 but we don't have the leading zeros on the cents.  There's actually a very easy way to do this, but I'll cover that in the next lesson.  For now, we'll create a custom function.  Add this function above our dollarAmount function.


def padWithZeros(strNum, numDigits):
  numZeros = numDigits - len(strNum)
  if numZeros <= 0:
    return strNum
  else:
    return ("0" * numZeros) + strNum

See that "0" * numZeros in that last return?  Remember that when you use the * operator on a string and an integer that you will get a new string containing that number of repetitions of the original string.  We're using that to give us the right number of leading zeros.

In this function, we take a string version of a number and the number of digits we want it to be.  If the string is already too big, we just return it.  Otherwise we return that string with the right number of leading zeros.

 
Now let's change our dollarAmount function to use our new function.


def dollarAmount(amount):
  dollars = amount / 100
  cents   = amount % 100
  return "$" + str(dollars) + "." + padWithZeros(str(cents), 2)



Now let's run it and see what happens.



Voila!  It worked.


Review

I think that's enough for now.  I had other things I wanted to mention but I'll save that for next time.  Here's what we learned in this lesson.


  • You can create your own functions by using the def keyword.
  • Functions you create take parameters and return a value.
  • The modulo operation (%) gives you the remainder of an integer division operation

That's it!  Happy programming!