[20:00] <pedro3005> Hello folks
[20:01] <pedro3005> Can whoever's here raise hands?
[20:01] <pedro3005> Good, good
[20:02] <pedro3005> Well, let me talk a bit about the class itself now
[20:02] <pedro3005> We're nearing the end
[20:03] <pedro3005> If you check out the official python tutorial, we're almost done with the basic python. From this point on they start talking about the standard library
[20:03] <pedro3005> Which is huge
[20:03] <pedro3005> So I won't be going over every detail of that
[20:03] <pedro3005> But with the knowledge you have from these classes, you will have no problem understanding the docs
[20:03] <pedro3005> hopefully
[20:04] <pedro3005> So last class we were talking about functions, dictionaries, files...
[20:04] <pedro3005> does anyone have any question or doubt?
[20:05] <pedro3005> Good.
[20:05] <pedro3005> So, today we can discuss a very important aspect of programming
[20:06] <pedro3005> no matter how much care you take in writing your code, things will screw up at some point
[20:06] <pedro3005> it happens
[20:06] <pedro3005> That's why we have to discuss error handling
[20:06] <pedro3005> Fortunately, Python has a system which makes it all easy
[20:06] <pedro3005> Hence exceptions!
[20:07] <pedro3005> An exception is an error form raised when something bad happens
[20:07] <pedro3005> for instance, let's look at this case
[20:07] <pedro3005> We learned about type conversion
[20:07] <pedro3005> What happens if you try to do int("a") ?
[20:07] <pedro3005> >>> int("a")
[20:07] <pedro3005> Traceback (most recent call last):
[20:07] <pedro3005>   File "<stdin>", line 1, in <module>
[20:07] <pedro3005> ValueError: invalid literal for int() with base 10: 'a'
[20:08] <pedro3005> The most interesting part here for us is the last line
[20:08] <pedro3005> notice the name ValueError
[20:08] <pedro3005> that's the name of a python exception
[20:08] <pedro3005> which was raised in this case because there's no way to convert the string "a" to an int
[20:09] <pedro3005> This exception being raised caused our program to fail
[20:09] <pedro3005> now that's a very bad thing
[20:09] <pedro3005> But fortunately, we can catch that exception
[20:10] <pedro3005> that is, acknowledge in our code that if it happens, the program shouldn't crash, but instead do what you tell it to!
[20:10] <pedro3005> Now, how do we do that?
[20:10] <pedro3005> we wrap it in a try block
[20:10] <pedro3005> Take this example
[20:10] <pedro3005> we received input from the user
[20:11] <pedro3005> age = raw_input("What is your age? ")
[20:11] <pedro3005> now, he might've typed his age, a number, or maybe something else
[20:11] <pedro3005> if we directly try to do int(age), it might work, but it might also fail pretty bad
[20:11] <pedro3005> remember raw_input() is of type STRING
[20:11] <pedro3005> we want to make it an integer, because that makes sense for an age variable
[20:12] <pedro3005> So what do we do? Well, we can wrap the int(age) in a try block
[20:12] <pedro3005> try:
[20:12] <pedro3005>     age = int(age)
[20:12] <pedro3005> except ValueError:
[20:12] <pedro3005>     print "You didn't input a valid age"
[20:14] <pedro3005> You can add an else block after all the exception catches
[20:14] <pedro3005> that will be ran if no exception was caught
[20:14] <pedro3005> in this example
[20:14] <pedro3005> else:
[20:14] <pedro3005>     print "The age is valid"
[20:15] <pedro3005> We have other built-in expressions
[20:15] <pedro3005> I mean exceptions
[20:15] <pedro3005> sorry
[20:15] <pedro3005> in fact you can see them all here http://docs.python.org/library/exceptions.html#bltin-exceptions
[20:15] <pedro3005> let's analyse for instance the case of a dictionary
[20:16] <pedro3005> Suppose you have a dict
[20:16] <pedro3005> and you try to access an element which is not there
[20:16] <pedro3005> >>> a = {}
[20:16] <pedro3005> >>> a["bla"]
[20:16] <pedro3005> Traceback (most recent call last):
[20:16] <pedro3005>   File "<stdin>", line 1, in <module>
[20:16] <pedro3005> KeyError: 'bla'
[20:16] <pedro3005> It raises the KeyError exception
[20:17] <pedro3005> So, suppose your dict is mapping person -> age
[20:17] <pedro3005> but age is still type string
[20:17] <pedro3005> we have
[20:18] <pedro3005> ages = {"Joe": "20", "Mary": "30"}
[20:19] <pedro3005> and our program has two interfaces
[20:19] <pedro3005> one which allows you to input a new person and their age
[20:19] <pedro3005> and another one which allows you to see a person's age
[20:20] <pedro3005> unfortunately, some user mapped a name e.g. "Foo" to a non-numerical string, "Bar"
[20:20] <pedro3005> so when we try to convert that to int we'll get a ValueError expression
[20:20] <pedro3005> but it could happen that a user also tries to access a non-existing person in this dict
[20:21] <pedro3005> one min please
[20:22] <pedro3005> We might need to catch BOTH exceptions
[20:22] <pedro3005> that is easy, we just stick them in a tuple
[20:22] <pedro3005> that is
[20:23] <pedro3005> except (ValueError, KeyError):
[20:23] <pedro3005>     handle_error()
[20:23] <pedro3005> assuming we have a handle_error() function here
[20:23] <pedro3005> the outcome isn't really important in our present analysis
[20:23] <pedro3005> Now, it could happen that you see something like this
[20:23] <pedro3005> except:
[20:24] <pedro3005>     pass
[20:24] <pedro3005> Let's analyse that
[20:24] <pedro3005> the 'pass' keyword is a placeholder
[20:24] <pedro3005> you can use it when python is expecting something to be there but you don't want it to do anything
[20:24] <pedro3005> so 'pass' is telling python to do nothing
[20:24] <pedro3005> the except: statement alone will catch ANY exception
[20:25] <pedro3005> So this block would be saying that if any exception happens, do nothing about it
[20:25] <pedro3005> That is poor practice!
[20:25] <pedro3005> You should always try to specify the exception
[20:26] <pedro3005> or maybe use except: to later write it down in a log
[20:28] <pedro3005> To do that, you may use the traceback module
[20:28] <pedro3005> simply import traceback and use the command traceback.print_last() for instance, to get the last exception
[20:29] <pedro3005> or, if you catch it in a try block
[20:29] <pedro3005> you can write it to a file
[20:29] <pedro3005> a log file
[20:30] <pedro3005> traceback.print_exc(file=log)
[20:30] <pedro3005> assuming you have created the log file descriptor
[20:32] <pedro3005> Take this program, for example
[20:32] <pedro3005> here I will use sys.stdout so you can all see the outcome
[20:32] <pedro3005> but it could just as well have been any file descriptor
[20:32] <pedro3005> >>> import sys, traceback
[20:32] <pedro3005> >>> def run():
[20:32] <pedro3005> ...     age = raw_input("Enter your age: ")
[20:32] <pedro3005> ...     try:
[20:32] <pedro3005> ...             age = int(age)
[20:32] <pedro3005> ...     except:
[20:32] <pedro3005> ...             traceback.print_exc(file=sys.stdout)
[20:32] <pedro3005> ...
[20:32] <pedro3005> >>> run()
[20:32] <pedro3005> Enter your age: foo
[20:32] <pedro3005> Traceback (most recent call last):
[20:32] <pedro3005>   File "<stdin>", line 4, in run
[20:32] <pedro3005> ValueError: invalid literal for int() with base 10: 'foo'
[20:33] <pedro3005> The traceback module has other functions, and as always, you can learn about them here http://docs.python.org/library/traceback.html
[20:33] <pedro3005> Questions?
[20:34] <pedro3005> Ok
[20:34] <pedro3005> let's have a quick look at import statements then
[20:34] <pedro3005> to pave the road for modules
[20:35] <pedro3005> We have been using them for a while
[20:35] <pedro3005> the import statement brings a module into your program
[20:35] <pedro3005> for instance
[20:35] <pedro3005> >>> import math
[20:35] <pedro3005> >>> math.sqrt(math.pi)
[20:35] <pedro3005> 1.7724538509055159
[20:35] <pedro3005> we can also use the 'as' keyword
[20:36] <pedro3005> >>> import math as magic
[20:36] <pedro3005> >>> magic.sqrt(10)
[20:36] <pedro3005> 3.1622776601683795
[20:37] <pedro3005> and we have the 'from' keyword
[20:37] <pedro3005> we can use that to import only a specific attribute we want
[20:38] <pedro3005> >>> from math import cos
[20:38] <pedro3005> >>> cos(0)
[20:38] <pedro3005> 1.0
[20:38] <pedro3005> notice we did cos(0) and not math.cos(0)
[20:38] <pedro3005> because with the 'from' keyword we're importing it directly onto our namespace
[20:38] <pedro3005> similarly you can use * to mean everything
[20:40] <pedro3005> >>> from math import *
[20:40] <pedro3005> >>> sqrt(factorial(5))
[20:40] <pedro3005> 10.954451150103322
[20:41] <pedro3005> Questions?
[20:42] <pedro3005> Good
[20:42] <pedro3005> You can import your own file
[20:42] <pedro3005> for instance
[20:43] <pedro3005> let's say you made a function which checks if a number is odd
[20:43] <pedro3005> in your file odd.py
[20:43] <pedro3005> def is_odd(x):
[20:44] <pedro3005>     return x % 2 != 0
[20:44] <pedro3005> let's look at that function closer
[20:44] <pedro3005> it seems a bit weird
[20:44] <pedro3005> but it makes sense
[20:44] <pedro3005> x % 2 != 0 is a boolean expression
[20:44] <pedro3005> it will be evaluated to either True or False
[20:44] <pedro3005> and that's what we return
[20:44] <pedro3005> any questions about that?
[20:45] <pedro3005> good
[20:45] <pedro3005> now, in a python terminal we can import that file
[20:46] <pedro3005> if we open it in the same folder
[20:46] <pedro3005> >>> import odd
[20:46] <pedro3005> >>> odd.is_odd(5)
[20:46] <pedro3005> True
[20:48] <pedro3005> now let's look at a line present in many python scripts
[20:48] <pedro3005> if you have analyzed some, you maybe have noticed it
[20:48] <pedro3005> towards the bottom it has something like
[20:48] <pedro3005> if __name__ == "__main__":
[20:48] <pedro3005> What does that mean?
[20:48] <pedro3005> It basically checks if the script was called directly "python odd.py" or imported
[20:48] <pedro3005> if it was imported, the if check fails
[20:49] <pedro3005> so let's suppose that if someone runs 'python odd.py' you want to display the message "Welcome to odd.py"
[20:49] <pedro3005> but if someone imports it from another program or shell, then do nothing
[20:49] <pedro3005> let's add that line
[20:49] <pedro3005> if __name__ == "__main__":
[20:49] <pedro3005>     print "Welcome to odd.py!"
[20:50] <pedro3005> now if we run it directly, we get the message
[20:50] <ClassBot> There are 10 minutes remaining in the current session.
[20:50] <pedro3005> [pedro@pedro ~]$ python odd.py
[20:50] <pedro3005> Welcome to odd.py!
[20:50] <pedro3005> but if we import it instead
[20:50] <pedro3005> >>> import odd
[20:50] <pedro3005> >>>
[20:50] <pedro3005> nothing :)
[20:51] <pedro3005> So to end today let's talk about command line arguments
[20:51] <pedro3005> they're like when you call a program with certain parameters
[20:51] <pedro3005> 'ls -a'
[20:51] <pedro3005> -a is an argument
[20:51] <pedro3005> just like in a function
[20:51] <pedro3005> our python programs can have that to
[20:52] <pedro3005> first we need to import the module sys
[20:52] <pedro3005> import sys
[20:52] <pedro3005> then, the arguments can be accessed in sys.argv
[20:52] <pedro3005> let's try that
[20:53] <pedro3005> in our arg.py file we have
[20:53] <pedro3005> import sys
[20:53] <pedro3005> print sys.argv
[20:53] <pedro3005> so let's call it
[20:53] <pedro3005> [pedro@pedro ~]$ python arg.py these are arguments
[20:53] <pedro3005> ['arg.py', 'these', 'are', 'arguments']
[20:54] <pedro3005> well
[20:55] <pedro3005> what if I set that as executable via chmod
[20:55] <pedro3005> then I need to add our good old shebang line
[20:55] <pedro3005> #!/usr/bin/env python
[20:55] <ClassBot> There are 5 minutes remaining in the current session.
[20:55] <pedro3005> to the start of our program
[20:55] <pedro3005> and by calling it the exact same way we get the same thing
[20:55] <pedro3005> [pedro@pedro ~]$ ./arg.py these are arguments
[20:55] <pedro3005> ['./arg.py', 'these', 'are', 'arguments']
[20:56] <pedro3005> Now, if you're a C programmer, you might be wondering about sys.argc, but that is not needed. Since we have a great list functionality, we can merely use len(sys.argv)
[20:56] <pedro3005> which is the count of how many command line arguments we have
[20:56] <pedro3005> Questions?
[20:58] <pedro3005> Well, good
[20:58] <pedro3005> that marks the end of today's session
[20:59] <pedro3005> Hope you all had a good time
[20:59] <pedro3005> thanks for the attention
[21:00] <ClassBot> Logs for this session will be available at http://irclogs.ubuntu.com/2010/10/16/%23ubuntu-classroom.html
[21:00] <palhmbs> where are logs for this channel?
[21:00] <palhmbs> ah - never mind
[21:01] <palhmbs> how long does it take for logs to be populated?
[21:04] <nigelb> palhmbs: should be ready now
[21:04] <palhmbs> yep - got them
[21:04] <palhmbs> thanks
[21:05] <palhmbs> only just found about this - #ubuntu-classroom thing - it's awesome
[21:05] <nigelb> \o/
[21:05] <palhmbs> nigelb, your involved with the Ubuntu Youth team?
[21:06] <nigelb> palhmbs: no, but I am involved with the classroom team
[21:06] <palhmbs> I'm 32, is their a middle-aged group for beginners?
[21:06] <nigelb> well, there's the beginners team
[21:06] <nigelb> https://wiki.ubuntu.com/BeginnersTeam
[21:07] <palhmbs> ah, I want to be more involved with Ubuntu, help - I've joined Openhatch.org - heard of it?
[21:07] <nigelb> yeah, paulproteus wrote it
[21:09] <palhmbs> I've been trying to get Gobby-0.5 connecting to gobby's main server....
[21:10] <palhmbs> would gobby be a good way to teach programming?
[21:12] <nigelb> It would if you can find someone willing to teach :)
[21:37] <palhmbs> nigelb, do you know when the calendar will be updated, I don't see any stuff for the next 2 weeks?
[21:38] <nigelb> palhmbs: that's because there isn't anything for the next 2 weeks
[21:40] <palhmbs> nigelb, oh well, gives me opportunity to read the logs for the next 2 weeks.... I suppose
[21:40] <nigelb> heh :)