Author Topic: [LESSONS ONLY] Python Scripting by Neptune  (Read 2437 times)

Offline travisN000

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1758
[LESSONS ONLY] Python Scripting by Neptune
« on: December 12, 2009, 09:11:46 PM »
First off..  thank you to Neptune for taking the time to share such useful knowledge & experience.

I have locked this thread so that the python lessons provided by Neptune can be compiled here for easy reading..  Despite the name on the posts, the content of the following posts are NOT MY WORK!  :D ::)  

I will do my best to add new material here as it appears in the original topic, but if I'm slow I'm sure Neal will help out!




« Last Edit: December 12, 2009, 09:32:57 PM by travisn000 »

Offline travisN000

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1758
[LESSON 0] Python Scripting by Neptune
« Reply #1 on: December 12, 2009, 09:12:44 PM »
Hmph - Neal apparently recruits volunteers much the way General Patton did...

What you'll need (for now):
-Python - I believe this is a default for PCLinuxOS, but if not, it's in Synaptic as "python"
-A programmer's editor. Kate, or KWrite is fine. In theory, any program that can edit text will do, but you'll find life easier with features such as auto-indenting and syntax highlighting that you'll get from an editor that "speaks your language".
-A terminal emulator - again, I believe "konsole" is installed by default, and it will do, but if you have a favorite, by all means use it.

What I'll need:
-Some sense of where you are in terms of programming concepts; variables, variable typing, functions, etc. - although, as this forum format is a single-threaded interface, perhaps it's better to assume nothing and start at a very rudimentary level, rather than jumping back and forth in skill/knowledge levels throughout the thread.
-Feedback from you. The last thing I want to do is leave people gasping for air.

« Last Edit: December 12, 2009, 09:33:35 PM by travisn000 »

Offline travisN000

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1758
[LESSON 1] Python Scripting by Neptune
« Reply #2 on: December 12, 2009, 09:17:34 PM »
What is Python, anyway? As has been noted, it is a scripting language. Scripting languages are translated into "computer-speak" as they run. This is as opposed to languages like C, which are pre-translated into a format the machine can digest by using compilers and linkers. Scripting programs generally execute more slowly for this reason, but have a faster development cycle, since you skip the whole compile/link step as you go through development iterations. In an age of multi-gigahertz processors, the perceived speed handicap of scripting languages is becoming less and less relevant.

Python is an object-oriented language. Everything in Python is an object. Generally, think of an object like a car - it has attributes, and methods. Car attributes might include four wheels, green paint, glass windows. Car methods might include accelerating, braking and steering. Similarly, objects in Python will (mostly) have attributes, and methods, which we'll get into shortly.

But first, open your favorite terminal program, such as Konsole, and type in what you see in green, then press enter:


[zccw01@LapNix ~]$ python
Python 2.5.2 (r252:60911, Jun 28 2008, 14:11:09)
[GCC 4.1.1 20060724 (prerelease) (4.1.1-4pclos2007)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>          



If you don't see something that resembles the above, scream now. This is the python interactive shell. Try the following, entering the text shown in green and pressing enter.

[zccw01@LapNix ~]$ python
Python 2.5.2 (r252:60911, Jun 28 2008, 14:11:09)
[GCC 4.1.1 20060724 (prerelease) (4.1.1-4pclos2007)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 5+3.14159
8.1415900000000008
>>>        


Several important things just happened here.

First, Python parsed the expression, and discovered that it had two constants, 5 and 3.14159, with a binary operator, +.

Second, it determined the "type" of the constants. 5 is an integer number, and 3.14159 is a floating-point number.

Third, based on the type of the operands, 5 and 3.14159, it decided that the + operator should be a mathematical summation - addition. The + operator is "overloaded" in Python - depending on the operands, it may perform a different operation, such as concatenating two strings.

Fourth, it "upgraded" the integer, 5 to a floating point. You didn't ask Python to do this, but if there are mixed operand types, Python will always try to A) do what's expected, and B) give you the most precise result. This conversion was necessary because integers and floating-point numbers are stored in two very different ways - binary math operators require that the operand be of the same type before they can work properly.

Fifth, it calculated the result, formatted it and printed it for you.

And, sixth - it got the answer wrong. The wrong answer wasn't really Python's fault - it's a necessary evil of trying to store decimal fractions into a bunch of binary on/off switches  All languages, to one degree or another, share this problem, and in programming, you have to keep it in mind - There are ways to be more accurate - much more accurate, but never completely accurate. Just be sure that your results are accurate enough for the task at hand.

So, since Python is so obliging with object typing, it should have no problem with the following (enter the green text into the Python shell and press enter):
5+"23" # Note the quotes - Yes, enter this too, on the same line

Did you get something that looks like the following?:

[zccw01@LapNix ~]$ python
Python 2.5.2 (r252:60911, Jun 28 2008, 14:11:09)
[GCC 4.1.1 20060724 (prerelease) (4.1.1-4pclos2007)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 5+3.14159
8.1415900000000008
>>> 5+"23" # Note the quotes - Yes, enter this too, on the same line
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>>            


Congratulations - you've just crashed your first program. What happened? Was it that extra # Note the quotes - Yes, enter this too, on the same line?
Nope - the # sign is signifies a comment to python, and the interpreter ignores the comment, and everything that follows on the same line. So that wasn't the problem. If we examine the result, you'll see that your program died in line 1 (not surprising, as there's only one line) and that the operand types are unsupported for the + operator.

This is not strictly true. The plus operator does support string and integer types - just not in the same expression.

Enter the following lines into the python interpreter, pressing enter after each line:

a="53";b=27
a+str(b)
int(a)+b


(sleazy data entry tip - highlight the line with your mouse in the browser window, then, in the terminal window that's running the python shell, click down on your mousewheel, or middle button - I know, every one already knows this, but...)

Did you get something like the following?:

>>> a="53";b=27
>>> a+str(b)
'5327'
>>> int(a)+b
80
>>>      


The first line is really two lines. The semicolon allows you to put more than one logical Python line on a single physical line - don't abuse this trick. Python reads easier vertically than horizontally. On this line, we created two new variables, a and b. Using the = assignment operator, we assigned the string constant '53' to variable a, and the integer constant, 27 to variable b.

On the next line,  a+str(b), we brought a new trick to the party - explicit casting. Up til now, we've let Python imply the casting of variable and constant types, but sometimes, slick as it is, Python needs a little guidance. In this line, we used the explicit str() cast to force the variable, b, to be evaluated as a string type. As a result, the interpreter evaluated two string operands, and decided that the + operator should perform concatenation, instead of addition. The result was that the two strings, '53' and '27' were concatenated to give '5327'.

In the final line, we explicitly cast the string variable a as an integer. The result was the + operator dealing with two integers, and an addition that yielded 80 for an answer.

So, given the preceding, you should have some idea of what will happen when you enter the following:
'This ' * 8 + 'That'

Did that surprise you? Here's what I got:

>>> 'This ' * 8 + 'That'
'This This This This This This This This That'
>>>  


When you think about it, it makes sense - after all, if Python can overload the + operator to mean either addition or concatenation, then it makes sense that the * operator is similarly overloaded to mean either multiplication (which is just repeated addition) or multiple concatenation.

So when the teacher asks you to stay after class and write 'I Love Python' twenty times on the chalkboard...

print('I Love Python\n' * 20)

Should do the job - and introduce you to the ideas of text formatting (\n), and functions, such as print()

Play with the above, and when you're finished, press CTRL-d to close the Python shell.

Next up: Functions and modules and bears, oh my!


« Last Edit: December 12, 2009, 09:33:51 PM by travisn000 »

Offline travisN000

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1758
[LESSON 2] Python Scripting by Neptune
« Reply #3 on: December 12, 2009, 09:21:39 PM »
Recap:
We know how to start and exit the Python interactive shell. We know that Python constants and variables are dynamically typed, automatically at execution time. We know that Python is a strongly-typed language, i.e., it will gripe if you use the wrong type of object. We've learned a couple of operators, + and *, and that those operators are "overloaded", i.e. behave differently depending on their operands. We know how to assign constants to a variable, and how to explicitly re-cast a constant or variable to a different type.

Today, we're going to build on that foundation, dip our toes into the murky waters of modules and functions, and maybe even write our first Python program.

I see a couple of you eager beavers have already opened your python interpreter. A+ for effort. Now, CTRL-d out of it and lets make a new home for some of our projects. You more cautious types who haven't even opened a terminal window should do so now.

I'm going to recommend putting our new home in your home directory. If you have a good reason for putting it in another directory, please cd there now - otherwise, type cd ~ and press enter.

Now, copy and paste the following command into your terminal window, then press enter.
Code: [Select]

mkdir -p pyedu/{lesson01/notes,lesson02/notes,lesson03/notes,lesson04/notes,lesson05/notes}


Yes, I'm a lazy sod who won't type 'mkdir' ten times when once will do. BTW - the number of folders is not a commitment to fill them, nor an indication that we won't overflow them. If we need more, we'll make more, and if we don't, no loss.

Now, type cd pyedu/lesson01 and press enter.

Start up the python interpreter shell (as always, your input is in green):

[zccw01@LapNix lesson01]$ python
Python 2.5.2 (r252:60911, Jun 28 2008, 14:11:09)
[GCC 4.1.1 20060724 (prerelease) (4.1.1-4pclos2007)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>


We touched very briefly on several variable types in the last installment. Let's dig a little deeper into one of those types, the string. Python considers strings to be immutable sequential objects. Immutable means they can't be altered. So we can skip trying to alter them. Sequential means they are ordered, indexable, sliceable, have length, and can be iterated over. Ordered is a pretty obvious attribute - after all, if you entered a customer name like 'Mr. James Smythe', and it came out as "yMams m. tJeshre", you'd properly consider that to be a fairly useless data type. So the next attribute is indexable - let's do some indexing. In your python interpreter shell, type the following lines, pressing enter after each one:


'abcde'[ 0 ]
'abcde'[ 3 ]
'abcde'[ 9 ]

Here's what I got:

>>> 'abcde'[ 0 ]
'a'
>>> 'abcde'[ 3 ]
'd'
>>> 'abcde'[ 9 ]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range
>>>    


So, what do we now know? To index a sequential object, you append the index enclosed in square brackets, as [n]. We know that the first element of a sequential object is 0. And, we know that Python gets mad when your index goes off the end of the object.

I can hear the wheels turning now. So, you're thinking, if zero is the lowest index of the object, and the highest index can never be greater than the length of the object minus 1... What about negative indices? As it turns out, negative indices are fine with Python, and are fairly useful. Try the following, pressing enter after each line:

'abcde'[-1]
'abcde'[-3]
'abcde'[-9]
'abcde'[-0]


You should see something like this:

>>> 'abcde'[-1]
'e'
>>> 'abcde'[-3]
'c'
>>> 'abcde'[-9]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range
>>> 'abcde'[-0]
'a'
>>>


When you feed Python a negative index, it makes the assumption that you want to work backwards from the end of the sequential object. This is actually quite useful, and it's very common to see string[-1] as a means of examining the last character of a string, which may be a null, or carriage return, or line feed, or... This notation is not symmetrical; 'abcde'[-5] will return 'a' where 'abcde'[5] will fail with an 'index out of range' error. This is a necessary evil, since -0 and 0 are equivalent in most languages.

So, that's indexing of a sequential object. What about slicing? A slice is simply a beginning and an ending index separated by a colon, enclosed in square brackets. If the beginning index is blank, it is assumed to be the first element of the object. If the ending index is blank, it is assumed to be the last element of the object. So let's slice:

'abcde'[2:4]
'abcde'[:3]
'abcde'[3:]
'abcde'[:]
'abcde'[4:99]


You should have:

>>> 'abcde'[2:4]
'cd'
>>> 'abcde'[:3]
'abc'
>>> 'abcde'[3:]
'de'
>>> 'abcde'[:]
'abcde'
>>> 'abcde'[4:99]
'e'
>>>


Note that in the last example, the out-of-range index did not cause an error. Slice notation is a little more forgiving than index notation. In this case, Python simply set the out-of-range ending index back to the end of the string.

Python is also fairly forgiving about invalid starting indices. Try:

'abcde'[45:99]
'abcde'[4:2]

 
Both of them return an empty, or null string, ''. In the first case, the starting index was out of range, in the second case, the starting index was greater than the ending index.

So, are negative indices valid in slice notation? You bet:

'abcde'[1:-1]  # everthing except the first and last character
'abcde'[-2:]   # just the last two characters
'abcde'[-3:4]  # the third from last through the fourth character

We've covered indexing and slicing - what about iteration? Iteration is a common programming task, where you walk, stepwise, through an object or group of objects. To demonstrate classical iteration, we're going to need a couple of functions, len(), and range().

The len() function is fairly self-explanatory It returns an integer representing the length of its argument. Try entering the following:

len('A String')
len('A Longer String')
len('')


The results are much as you'd expect. No surprises here:

>>> len('A String')
8
>>> len('A Longer String')
15
>>> len('')
0
>>>      


The range() function is a little trickier. In its simplest form, range(n), where n is an integer, range(n) will return a list of integers, beginning with zero, and up to, but not including n.

Give it a try:

>>> range(5)
[0, 1, 2, 3, 4]
>>> range(9)
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>>

The list that range() returns is a new data type, and one that we'll explore later, but for now, the important attribute of a list is that it is a sequential data type - which means we can iterate it. So now, all we need is an iterator statement, and the classic iterator statement in many languages is the for statement.

Lets put some pieces together. As usual, press enter after each line, noting that before the print() line you will need to press the space three times, and press enter twice after:

>>> my_str='Python'
>>> l=len(my_str)
>>> r=range(l)
>>> for x in r:
...    print(my_str[ x ])
...
P
y
t
h
o
n
>>>


There are a couple of new things here. First, the for statement. The for statement is a type of flow control statement known as a loop. All of the statements we've seen up til now will execute, then proceed to the next statement in line. Flow control statements allow you to conditionally execute code blocks, skip code blocks, or repeat code blocks. If you've worked in other languages, you may have seen a for statement something like this:

for (i=1;i<10,i++)

In other words for (initialize;loop-test;increment)

Not with Python. The Python for statement is an iterator, pure and simple, and it starts at one end of an iterable and works its way to the end, at each pass assigning the next iterable element to the target variable.

for target in iterable-object:

So in our example, where the iterable was a list of integers returned from the range() function, on the first pass, x will be assigned a value of 0, on the next pass, a value of 1, and so forth til the end of the list is reached.

To illustrate better, try the following, remembering that the print statement must be indented by three spaces, and you must press enter twice after.

>>> for x in ['spam', 'Spam', 'SPAM', 'rat', 42]:
...    print(x)
...
spam
Spam
SPAM
rat
42
>>>


So, back to our "Python" string iteration - can we make it better? Sure. Since everything in Python is an object, it's permissible to pass the return from one function directly to another, like this:

>>> my_str='Python'
>>> for x in range(len(my_str)):
...    print(my_str[ x ])
...
P
y
t
h
o
n
>>>


But if we're going to use Python functionality, let's go all the way. Remember when I said this was a classical iteration example? Well, since we know that strings are sequential objects, and we know that all sequential objects are iterable, maybe we can dispense with the classical iteration trappings, and just iterate directly on the string. And so we can:

>>> for x in 'Python':
...    print(x)
...
P
y
t
h
o
n
>>>


With these tools tucked firmly in our workbelt, let's solve a family problem. It seems our dear sister, Dolly, suffers from a bizarre form of dyslexia. Apparently, she can only read backward. Since spelling backward is such a royal pain, poor Dolly never gets any mail from her family.

CTRL-d to close the python interpreter shell.

Enter the following in your terminal window and press enter:

touch text_utils.py

Then, substituting your favorite programmer's editor for kwrite, enter the following:

kwrite text_utils.py &

Enter the following code into your text_utils.py file and save it. Don't close the editor window yet, we'll be needing it shortly. Note that each level of indentation is a multiple of three spaces - this matters. If you just copy-paste the whole body of code, it should align correctly. Don't be tempted to format with tabs - we'll find out why later.
Code: [Select]

def flip_txt(message):
   rvs_msg = ""
   for x in message:
      rvs_msg = x + rvs_msg
   return rvs_msg
There you go. Your first module, text_utils, and your first function, flip_txt(). So lets send Dolly a message.

Be sure you've saved text_utils.py, then, back in your terminal window, restart the Python interpreter:

[zccw01@LapNix lesson01]$ python
Python 2.5.2 (r252:60911, Jun 28 2008, 14:11:09)
[GCC 4.1.1 20060724 (prerelease) (4.1.1-4pclos2007)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

Enter the following statement:

>>> import text_utils
>>>


Notice that nothing happened, except that you got another >>> prompt. That's Python for you. You screw up and it yells at you, you get it right, and there's just silence. You'll have to get your positive reinforcement elsewhere. So, how do we send Dolly a message? Easy:

>>> text_utils.flip_txt("Dear Dolly. I hope you are doing well. Yours Truly.")
'.ylurT sruoY .llew gniod era uoy epoh I .ylloD raeD'
>>>


When you import a module, you bring it into your current scope, or namespace. At that point, all of its public objects and methods are accessible using the '.' notation. There is a variant of the import statement. If you hate the idea of typing text_utils.whatever(), you can import module as name, for example, import textutils as tu. Then we could say tu.flip_txt(). That can save on typing, but it's generally not a good idea to alias module names if you're working with a number of people who have to read your code, and are expecting known modules.

The final variant of the import statement, which is seriously, seriously not recommended, is; from module import x. x may be an asterisk, in which case everything from that module gets dragged into your namespace, or it may be specific, as in from text_utils import flip_txt. At that point, the objects may be directly invoked just as any other object in your code, i.e. instead of text_utils.flip_txt(), we could just issue flip_txt() directly. The reason this form is strongly discouraged is that with a large module you stand a fairly good chance of having name collisions with your code, with unpredictable results. It also obscures the source of the objects you are referencing, and we don't like obscurity.

Finally, let's go back to the editor window which has text_utils.py. Notice the beautifully indented format? Remember how I've been banging on about "three spaces"? Well, I've been lying. Not about the need to indent, but about three spaces - that part is totally arbitrary. You can indent one space, or ten spaces. It's completely up to you. But the indentation has to be consistent for every line of the code block. That's because whitespace is literally part of the Python syntax. C programmers hate this, and here's why. Below is a sample C++ program.

int  main  ()
{
   cout  << "Hello World!";
   return  0:
}

Notice that it's nicely indented? But that's just a matter of preference. I've seen a variety of styles, like:

int  main  () {
              cout  << "Hello World!";
              return  0:
              }

You could even, as one of our classmates that I shamelessly ripped off demonstrated, write it as:

int  main  () { cout  << "Hello World!"; return  0: }

C, and many other languages use special characters to delimit lines and code blocks, but leave the formatting totally at the programmer's discretion. And that's the problem. Programmers have no discretion. I swear on a stack of holy books that if you put five C programmers on a project, they will have six different indentation styles, and by the end of the project, they will be spending 90% of their time reformatting each others code to match their indentation quirks.

Worse, I've seen C programmers get tangled up for hours on something like this:

if (something)
        if (something else)
                do_stuff(stuff_params);
else
        this_blows(something wrong);

It's not immediately obvious even in this small example what went wrong in the above code, and when you're dealing with much larger functions, with many lines of code it can drive you crazy. The problem, of course, is that the indentation does not match what the code is actually doing - the else clause should be lined up under the second if statement, and its code block similarly indented. Python simply can't have these problems.

Python takes the position that indenting code blocks is the right way to do things, and by making it part of the syntax, the language is also able to get rid of most of the semantic delimiter clutter, like squirelly braces and hemi-colons.

Python has retained a colon at the end of a flow control statements to designate the beginning of a new code block. You see two colons in our flip_txt() function; one at the end of the function declaration, and one at the end of the for statement. In both cases, that's your cue to indent another level. Notice also that it's easy to see the end of a code block - the indentation takes a step to the left. The for statement in our example controls a code block of one line, and you can see at a glance where the indentation, hence, the code block ends.

Which is why I cautioned you about tabs above. Let me emphasize that. Tabs are evil. Tabs display at different widths on different systems and editors, they sometimes get translated in strange ways during copy/paste operations, and the absolute worst is tabs and spaces mixed together. The behavior of spaces, on the other hand, is consistent and safe across all platforms and editors. I strongly suggest configuring your editor to convert all tabs to spaces, and to produce a set number of spaces instead of a tab character when the tab key is pressed.

Meanwhile, back to our text_utils.py. There are only two really new things here. The function declaration, which is the keyword def, followed by the function name with any expected parameters in parenthesis, followed by a colon. The other new statement is the return statement. The return statement ends the execution of the function, yields control back to the caller, and optionally, returns any Python object (or, as we'll see later, combination of objects) to the caller.

Other than that, with a bit of thought, it should be possible to puzzle out just how the text is getting reversed. Scream loudly if not.

Well, that should keep some of you scratching your heads for a few minutes.

Next up: There's methods to my madness.

« Last Edit: December 12, 2009, 09:34:16 PM by travisn000 »

Offline travisN000

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1758
[LESSON 3] Python Scripting by Neptune
« Reply #4 on: December 12, 2009, 09:24:37 PM »
Recap:
In our last segment, we created a module which included a function, and learned how to load and use that module. We introduced a new variable type, the list, and discussed some of the characteristics of a couple of variable types, and the use of indexing and slicing with sequence-type variables. We touched on our first flow control statement, "for" and learned that whitespace is an integral part of the Python syntax. We also moved away from the single-letter variable names we had been using in the examples, which is as good a time as any to speak a bit on variable names.

Einstein was a genius - but in a programming class, he'd have been marked down for his variable names.
E=mc2 - really? What does that mean?
To a mathemetician, "E", or "e", might mean Euler's Number - which is a big deal in the math world. To a chemist, "m" might represent molar mass. The point is, in programming, your variable naming is part of the documentation. While compact, and easy to type, single-letter variable names are seldom descriptive. In general, use of single-letter variable names, like x, where the variables are temporary, and are only used in a small block of code, like a short loop, is ok - but for the bulk of your actual programming it's a good idea to be a bit more descriptive, as in our flip_txt() example function from the last segment.

As in all things programming, there are guidelines and rules. Python variables may contain letters, numbers and the underscore. Python names may not begin with a number. Python names are case-sensitive; q4_sales_sum and Q4_Sales_Sum are two different variables. In addition, Python has kept a handful of names for itself - don't use the following:

Code: [Select]
and, assert, break, class, continue,
def, del, elif, else, except, exec,
finally, for, from, global, if, import,
in, is, lambda, not, or, pass, print,
raise, return, try, while, yield

Finally, there are a few names which you can use - but you'll be sorry if you do, like "True", "False", and "None" These are all predefined by Python, and Python programmers who use these will curse your great-grandchildren if you re-define them.

So, in the last segment I teased the idea of methods. If you'll remember from our first segment, I gave the object-oriented perspective of a car as having methods such as accelerate, turn, and brake. In Python, the role of methods is played by functions, but the important object-oriented concept here is that many Python objects, like cars, have their own methods that they carry around with them like portable tool kits.

At this point, open your terminal window, and go to our current working folder, cd ~/pyedu/lesson01 (unless you opted not to use your home folder, in which case substitute out the "~"), and start the python interpreter:

[zccw01@LapNix lesson01]$ python
Python 2.5.2 (r252:60911, Jun 28 2008, 14:11:09)
[GCC 4.1.1 20060724 (prerelease) (4.1.1-4pclos2007)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>


As mentioned above, in the last segment we introduced a new variable type, the list. A list is denoted by square brackets, as shown in the last installment's examples. So what methods does a list variable have? Lets ask Python. The dir() function returns a sorted list of names defined for an object. Simply place the object in the parenthesis, as in the following example where we want to know about a list variable, which is the square brackets:

>>> dir([])
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__str__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>>    


In general, the names bracketed by double underscores are used by other functions - the names we are interested in (for now) are those without the double-underscores.

So what are these names, like append, count, extend, etc.?

Again, let's revisit the concept of object types, and ask Python.

>>> type([].append)
<type 'builtin_function_or_method'>
>>>

Great - so we now know that ['this', 'is', 'a', 'list'].append() is a function - but what does it do?

You guessed it - let's ask Python (note the double underscores before and after "doc"):

>>> print([].append.__doc__)
L.append(object) -- append object to end
>>>


So, knowing this, we can try the following:

>>> my_list = [1,4,3.14159]
>>> my_list.append("Guido")
>>> print(my_list)
[1, 4, 3.1415899999999999, 'Guido']
>>>


By now, you're beginning to suspect something about lists - they don't care what their content is. You can have a list of integers, a list of strings, even a list of lists - or any combination of the above. You can even have a list of functions, which can be a very useful method of dispatching various functions based on a variable or condition.

The other thing I hope you're beginning to suspect is that it's pretty cool that Python documents itself so well. Hey, it's a language that's happy to share with you. The .__doc__ attribute isn't a substitute for a complete Python reference - but it's a very handy memory jogger when all you need is a reminder.

So does this dir() function work on modules? Why not - they're an obect, too, right? Try the following in your Python interpreter:

>>> import math
>>> dir(math)
['__doc__', '__file__', '__name__', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log', 'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']
>>> type(math.pi)
<type 'float'>
>>> math.pi
3.1415926535897931
>>> type(math.sin)
<type 'builtin_function_or_method'>
>>> print(math.sin.__doc__)
sin(x)

Return the sine of x (measured in radians).
>>>


So let's introduce one more flow-control statement and try to solve a vacation challenge. The new flow control statement is the if statement, and its syntax is:

if condition:
elif:
elif:
else:

You may have zero elif clauses, which are just syntactic shorthand for else: if, and the else clause is also optional.

The if statement operates on conditions, the result of which may be either True, or False. Conditions usually involve comparisons of some sort, and Python is no exception. Conditional operators include:
Code: [Select]

> (Greater Than)
< (Less Than)
== (Equal To)
!= (Not Equal To)
<> (Not Equal To - Deprecated - use !=)

In addition, Python considers 0, and null or empty variables to be False, and non-zero, and non-empty variables to be True.

Similarly, functions that return variables may be used for the condition with the same rules as variables.

Finally, complex conditions may be used, and combined with and or or, and the results of any condition may be reversed with not.

Remember - because if, elif and else are related to flow control, they are always followed by a colon, and a code block of one or more indented lines.


So lets see the if statement in action.

CTRL-d out of your python interpreter, and edit our text_utils.py module from our previous segment:

[zccw01@LapNix lesson01]$ kwrite text_utils.py &

(you may, by the way, as one of our sharp-eyed members has already observed, see a text_utils.pyc file. This is a nose hair. You can pluck it out, but it will grow back. Python byte-compiles its modules at run time to make things a little quicker the next time around, and names the byte-compiled modules with a .pyc extension. Ignore it - it's part of Python.)

Now, let's solve our travel problem. You've noticed a poster at the travel agent with great rates to visit the exotic, sun-drenched beaches of Orwaynay. You decide that's just the ticket to cure your winter doldrums, and decide to book a vacation. The only problem is the language. The Orwaynayans speak English, but they learned it from a Jesuit they rescued from the sea, who had been driven just a little bit bonkers by weeks stranded in a crippled boat with nothing but communion wafers to eat. But, no worries - the rules of the language are simple, and Python is easily up to the challenge.

Copy/paste the following into text_utils.py, at the bottom, after the flip_txt function:

Code: [Select]
def splitter(a_word):
   if not len(a_word):
      return('','')
   if not a_word.isalpha():
      return('','')
   if a_word[0].lower() in 'aeiou':
      return('',a_word)
   if a_word[:2].lower()=='qu':
      return(a_word[:2], a_word[2:])
   for i in range(len(a_word)):
      if a_word[i].lower() in 'aeiou':
         return(a_word[:i],a_word[i:])

def xlate_it(sentence):
   out_sentence=''
   for a_word in sentence.split():
      pref,suf=splitter(a_word)
      if pref:
         if pref[0].upper()==pref[0]:
            suf=suf[0].upper()+suf[1:]
         pref=pref.lower()
      if pref=='' and suf[-1:]in 'aeiou':
         suf += 'yay'
      else:
         suf += pref+'ay'
      out_sentence+=suf+' '
   return(out_sentence)
(some of you exerienced programmers may be bristling up - don't worry, we'll deal with it.)

Now, let's fire up the Python interpreter, and try:

>>> import text_utils

We need to start building our list of important Orwaynayan phrases, so:

>>> text_utils.xlate_it("Please direct me to the nearest bail bondsman")
'Easeplay irectday emay otay ethay earestnay ailbay ondsmanbay '

And, if dear reverse-dyslexia sister Dolly comes along, there's no need to leave her out:

>>> text_utils.flip_txt(text_utils.xlate_it("Please direct me to the nearest bail bondsman"))
' yabnamsdno yablia yantserae yahte yato yame yadtceri yalpesaE'
>>>


We're going to break this segment into two installations. While we're waiting for part II, walk through the code, and try some of the tricks we've learned today to gain insight as to what it's doing - we'll break it down stepwise in the next installment and see how close you got.
« Last Edit: December 12, 2009, 09:35:06 PM by travisn000 »