From 77ae9ab2d2dd2ed1a9aa8d9fb41dfdae84200f00 Mon Sep 17 00:00:00 2001 From: "juneau001@gmail.com" Date: Sat, 13 Jun 2009 09:31:24 -0500 Subject: [PATCH] Committing Completed First Drafts and Incomplete Chapter rst files. --- chapter1.rst | 780 ++++++++++++++++++++++++++++++++++++ chapter10.rst | 2 + chapter11.rst | 2 + chapter12.rst | 2 + chapter13.rst | 2 + chapter14.rst | 2 + chapter15.rst | 2 + chapter16.rst | 2 + chapter17.rst | 2 + chapter18.rst | 2 + chapter19.rst | 2 + chapter2.rst | 1050 +++++++++++++++++++++++++++++++++++++++++++++++++ chapter20.rst | 2 + chapter3.rst | 359 +++++++++++++++++ chapter4.rst | 2 + chapter5.rst | 489 +++++++++++++++++++++++ chapter6.rst | 887 +++++++++++++++++++++++++++++++++++++++++ chapter7.rst | 578 +++++++++++++++++++++++++++ chapter8.rst | 2 + chapter9.rst | 2 + index.rst | 115 ++++++ toc.txt | 225 ++++++++--- 22 files changed, 4454 insertions(+), 57 deletions(-) create mode 100644 chapter1.rst create mode 100644 chapter10.rst create mode 100644 chapter11.rst create mode 100644 chapter12.rst create mode 100644 chapter13.rst create mode 100644 chapter14.rst create mode 100644 chapter15.rst create mode 100644 chapter16.rst create mode 100644 chapter17.rst create mode 100644 chapter18.rst create mode 100644 chapter19.rst create mode 100644 chapter2.rst create mode 100644 chapter20.rst create mode 100644 chapter3.rst create mode 100644 chapter4.rst create mode 100644 chapter5.rst create mode 100644 chapter6.rst create mode 100644 chapter7.rst create mode 100644 chapter8.rst create mode 100644 chapter9.rst create mode 100644 index.rst diff --git a/chapter1.rst b/chapter1.rst new file mode 100644 index 0000000..beb7170 --- /dev/null +++ b/chapter1.rst @@ -0,0 +1,780 @@ +Chapter 1: Language and Syntax +++++++++++++++++++++++++++++++ + + + + +“Elegant”, that is an adjective that is often used to describe the Python language. The term elegant is defined as “pleasingly graceful and stylish in appearance or manner”. “Uncomplicated”, and “powerful” could also be great words to assist in the description of this language. It is a fact that Python is an elegant language that lets one create powerful applications in an uncomplicated manner. One may say that if you look at trends that are occurring within the computing industry today, Python can be looked at as the main objective. There are dozens, if not hundreds, of programming languages available today which each offer a different flavor to the field. Although there are many flavors, there is a similar objective for each of these programming languages...and that is to produce powerful and uncomplicated code that is easy to construct and maintain. Python does just that. + + + +While we’ve easily defined the goal of programming languages in a broad sense in paragraph one, we have left out one main advantage of learning the Python programming language. The Python language can run everywhere because it has been extended to run on the Java platform. We are talking about Jython, the language implementation that takes the elegance, power, and ease of Python and runs it on the JVM. The Java platform is an asset to the Jython language much like the C libraries are for Python. Jython is able to run just about everywhere, which gives lots of flexibility when deciding how to implement an application. Not only does the Java platform allow for flexibility, but it also offers a vast library containing thousands of APIs which are available for use by Jython. Add-in the maturity of the Java platform and it becomes easy to see why Jython is undoubtedly the main objective of every programming language. The goal, if you will, of any programming language is to grant its developers the same experience which Jython does. Simply put, learning Jython will be an asset to any developer. + + + +As I’ve mentioned that the Jython language implementation basically takes Python and runs it on the JVM, you will find out that it does so much more. Once you have experienced the power of programming on the Java platform it would be a difficult feat to move away from it. Learning Jython not only allows one to stay running on the JVM, but it also allows you to learn a new way to harness the power of the platform. The language increases productivity as it has an easily understood syntax which reads almost as if it were pseudo code. It also adds dynamic abilities that are not available in the Java language itself. In this chapter you will learn how to install and configure your environment, and you will also get an overview of those features which the Python language has to offer. This chapter is not intended to delve so deep into the concepts of syntax as to bore you, but rather to give you a quick and informative introduction to the syntax so that you will know the basics and learn the language as you move on through the book. It will also allow you the chance to compare some Java examples with those which are written in Python so you can see some of the advantages this language has to offer. + + + +By the time you have completed this chapter, you should know the basic structure and organization that Python code should follow. You’ll know how to use basic language concepts such as defining variables, using reserved words, and performing basic tasks. It will give you a taste of using statements and expressions. As every great program contains comments, you’ll learn how to document single lines of code as well as entire code blocks. As you move through the book, you will use this chapter as a reference to the basics. This chapter will not cover each feature in completion, but it will give you enough basic knowledge to start using the Python language. + + + +The Difference Between Jython and Python +======================================== + +Jython is an implementation of the Python language for the Java platform. Throughout this book, you will be learning how to use the Python language, and along the way we will show you where the Jython implementation differs from CPython, which is the canonical implementation of Python written in the C language. It is important to note that the Python language syntax remains consistent throughout the different implementations. At the time of this writing, there are three mainstream implementations of Python. These implementations are: CPython, Jython for the Java platform, and IronPython for the .NET platform. + + + +This book will reference the Python language in sections regarding the language syntax or functionality that is inherent to the language itself. However, the book will reference the name Jython when discussing functionality and techniques that are specific to the Java platform implementation. No doubt about it, this book will go in-depth to cover the key features of Jython and you’ll learn concepts that only adhere the Jython implementation. Along the way, you will learn how to program in Python and advanced techniques. + + + +Developers from all languages and backgrounds will benefit from this book. Whether you are interested in learning Python from for the first time or discovering Jython techniques and advanced concepts, this book is a good fit for any developer. Java developers and those who are new to the Python language will find specific interest in reading through Part I of this book as it will teach the Python language from the basics to more advanced concepts. Seasoned Python developers will probably find more interest in Part II and Part III as they focus more on the Jython implementation specifics. Often times in this reference, you will see Java code compared with Python code. + + + +Installing and Configuring Jython +================================= + + + + +Before we delve into the basics of the language, we’ll learn how to obtain Jython and configure it for your environment. To get started, you will need to obtain a copy of Jython from the official website http://www.jython.org. Since this book focuses on release 2.5, it would be best to visit the site now and download that release. You will see that there are previous releases that are available to you, but they do not contain many of the features which have been included in the 2.5 release. + + + +I think at this point it is important to mention that the Jython implementation maintains consistent features which match those in the Python language for each version. For example, if you download the Jython 2.2.1 release, it will include all of the features that the Python 2.2 release contains. Similarly, when using the 2.5 release you will have access to the same features which are included in Python 2.5. There are also a couple of extra pieces included with the 2.5 release which are specific to Jython. We’ll discuss more about these extra features throughout the book. + + + +Okay, well if you haven’t done so already then please grab yourself a copy of the Jython 2.5 release. You will see that the release is packaged as a cross-platform executable JAR file. Right away, you can see the obvious advantage of running on the Java platform…one installer that works for various platforms. It doesn’t get much easier than that! In order to install the Jython language, you will need to have Java 5 or greater installed on your machine. If you do not have Java 5 or greater then you’d better go and grab that from http://www.java.com and install it before trying to initiate the Jython installer. + + + +You can initiate the Jython installer by simply double-clicking on the JAR file. It will run you through a series of standard installation questions. At one point you will need to determine which features you’d like to install. If you are interested in looking through the source code for Jython, or possibly developing code for the project then you should choose the “All” option to install everything…including source. However, for most Jython developers and especially for those who are just beginning to learn the language, I would recommend choosing the “Standard” installation option. Once you’ve chosen your options and supplied an installation path then you will be off to the races. + + + +In order to run Jython, you will need to invoke the jython.bat executable file on Windows or the jython.sh file on *NIX machines and Mac OS X. That being said, you’ll have to traverse into the directory that you’ve installed Jython where you will find the file. It would be best to place this directory within your PATH environment variable on either Windows, *NIX, or OS X machines so that you can fire up Jython from within any directory on your machine. Once you’ve done this then you should be able to open up a terminal or command prompt and issue type “jython” then hit enter to invoke the interactive interpreter. This is where our journey begins! The Jython interactive interpreter is a great place to evaluate code and learn the language. It is a real-time testing environment that allows you to type code and instantly see the result. As you are reading through this chapter, I recommend you open up the Jython interpreter and follow along with the code examples. + +Identifiers and Declaring Variables +=================================== + +:: + + +Every programming language needs to contain the ability to capture or calculate values and store them. Python is no exception, and doing so is quite easy. Defining variables in Python is very similar to other languages such as Java, but there are a few differences that you need to note. + + + +First, variables in Python have no declared type. Therefore, this allows any variable to hold any type of data. It also allows the ability of having one variable contain of different data types throughout the life cycle of a program. So a variable that is originally assigned with an integer, can later contain a String. + + + +To define a variable in the Python language, you simply name it using an identifier. An identifier is a name that is used to identify an object. The language treats the variable name as a label that points to a value. It does not give any type for the value. Identifiers in Python can consist of any ordering of letters, numbers, or underscores. However, an identifier must always begin with a non-numeric character value. We can use identifiers to name any type of variable, block, or object in Python. As with most other programming languages, once an identifier is defined, it can be referenced elsewhere in the program. + + + +Once declared, a variable is untyped and can take any value. This is one difference between using a statically typed language such as Java, and using dynamic languages like Python. In Java, you need to declare the type of variable which you are creating, and you do not in Python. It may not sound like very much at first, but this ability can lead to some extraordinary results. Consider the following, lets define a value ‘x’ below and we’ll give it a value of zero. + +:: + + int x = 0; + + + + +As you see, we did not have to give a type to this variable. We simply name it and assign a value. You can also see that in Python there is no need to end the declaration with a semicolon. Since we do not need to declare a type for the variable, we can change it to a different value and type later in the program. + + + + x = ‘Hello Jython’ + + +We’ve just changed the value of the variable ‘x’ from a numeric value to a String without any consequences. This is a key to the dynamic language philosophy...change should not be difficult, but rather easy to integrate. + + + +Let us take what we know so far and apply it to some simple calculations. Based upon the definition of a variable in Python, we can assign an integer value to a variable, and change it to a float at a later point. For instance: + + + >>> x = 6 + >>> y = 3.14 + >>> x = x * y + >>> print x + 18.84 + +In the previous example we’ve demonstrated that we can dynamically change the type of any given variable by simply performing a calculation upon it. In other languages, we would have had to begin by assigning a float type to the ‘x’ variable so that we could later change it’s value to a float. Not here, Python allows us to bypass type constriction and gives us an easy way to do it. + +Reserved Words +============== + +:: + + +There are a few more rules to creating identifiers that we must follow in order to adhere to the Python language standard. Certain words are not to be used as identifiers as the Python language reserves them for performing a specific role within our programs. These words which cannot be used are known as reserved words. If we try to use one of these reserved words as an identifier, we will see a SyntaxError thrown as Python wants these reserved words as it’s own. + +There are no symbols allowed in identifiers. Yes, that means the Perl developers will have to get used to defining variables without the $. + + + +The complete listing of reserved words is as follows: + +======== ========= ======= ======= ========== +Words +======== ========= ======= ======= ========== +and assert break class continue +def del elif else except +exec finally for from global +or pass print raise return +try while with yield +======== ========= ======= ======= ========== + +Table 1-1. Reserved Words. The following lists all of the Python language reserved words + + +Coding Structure +---------------- + +Another key factor in which Python differs from other languages is it’s coding structure. Back in the day, we had to develop programs based upon a very strict structure such that certain pieces must begin and end within certain punctuations. Python uses positioning and code must adhere to an ordered structure. Unlike languages such as Java that use brackets to open or close a code block, Python uses spacing as to make code easier to read and also limit unnecessary symbols in your code. It strictly enforces ordered and organized code, but it lets the programmer define the rules. + +Python ensures that each block of code adheres to its defined spacing strategy in a consistent manner. What is the defined spacing strategy? You decide. As long as the first line of a code block is out-dented by at least one space, the rest of the block can maintain a consistent indentation, which makes code easy to read. Many argue that it is the structuring technique that Python adheres to which makes them so easy to read. No doubt, adhering to a standard spacing throughout an application makes for organization. As a matter of fact, the Python standard spacing technique is to use four spaces for indentation. If you adhere to these standards then your code will be easy to read and maintain in the future. + +For instance, let’s jump ahead and look at a simple ‘if’ statement. Although you may not yet be familiar with this construct, I think you will agree that it is easy to determine the outcome. Take a look at the following block of code written in Java first, and then we’ll compare it to the Python equivalent. + +Java if-statement + + + x = 100; + + if(x > 0){ + System.out.println(“Wow, this is Java”); + } else { + System.out.println(“Java likes curly braces”); + } + + +Now, let’s look at a similar block of code written in Python. + +Python if-statement + + x = 100 + if x > 0: + print ‘Wow, this is elegant’ + else: + print ‘Organization is the key’ + + + + +Okay, my example is cheesy but we will go through it nonetheless as it is demonstrating a couple of key points to the Python language. As you see, the program evaluates if the value of the variable ‘x’ is greater than zero. If so, it will print ‘Wow, this is elegant’. Otherwise, it will print ‘Organization is the key’. Look at the indentation which is used within the ‘if’ block. This particular block of code uses four spaces to indent the ‘print’ statement from the initial line of the block. Likewise, the ‘else’ jumps back to the first space of the line and its corresponding implementation is also indented by four spaces. This technique must be adhered to throughout an entire Python application. By doing so we gain a couple of major benefits: Easy-to-read code, and no need to use curly braces. Most other programming languages such as Java use a bracket “[“ or curly brace “{“ to open and close a block of code. There is no need to do so when using Python as the spacing takes care of this for you. Less code = easier to read and maintain. + +Operators +--------- + +The operators that are used by Python are very similar to those used in other languages...straightforward and easy to use. As with any other language, you have your normal operators such as +, -, *, and / which are available for performing calculations. As you can see from the examples below, there is no special trick to using any of these operators. + +Example 1: Performing Integer based operations + +>>> x = 9 +>>> y = 2 +>>> x + y +11 +>>> x - y +7 +>>> x * y +18 +>>> x / y +4 + +Perhaps the most important thing to note with calculations is that if you are performing calculations based on integer values then you will receive a rounded result. If you are performing calculations based upon floats then you will receive float results, etc. + +Example 2: Performing float based operations + +>>> x = 9.0 +>>> y = 2.0 +>>> x + y +11.0 +>>> x - y +7.0 +>>> x * y +18.0 +>>> x / y +4.5 + +It is important to note this distinction because as you can see from the differences in the results of the division (/) operations in examples 1 and 2, we have rounding on the integer values and not on the float. A good rule of thumb is that if your application requires precise calculations to be defined, then it is best to use float values for all of your numeric variables, or else you will run into a rounding issue. + +Expressions +----------- + +Expressions are just what they sound like...they are a piece of Python code that produces a value. For example, if we wish to assign a particular value to a variable then we would use an expression. Similarly, if I wish to perform a calculation based upon two variables or numeric values then I am performing a expression. + +Examples of Expressions + +>>> x = 9 +>>> y = 2 +>>> z = 9 * 2 +>>> x + y +>>> x - y +>>> x * y +>>> x / y + +The examples of expressions that are shown above are very simplistic. Expressions can be made to be very complex and perform powerful computations. They can be combined together to produce complex results. + + + +Statements +---------- + +When we refer to statements, we are really referring to a line of code that does something. There are several statements that can be issued in Python that ultimately define the different constructs available for use within an application. In this section, we will take a tour of statement keywords and learn how they can be used. + +Let’s start out by listing each of these different statement keywords, and then we will go into more detail about how to use each of them with different examples. I will not cover each statement keyword in this chapter as some of them are better left for later in the chapter or the book, but you should have a good idea of how to code an action which performs a task after reading through this section. While this section will provide implementation details about the different statements, you should refer to later chapters to find advance uses of these features. + +Table 1-1. Statement Keywords + + +======== ===== +Words +======== ===== +if +else +for +while +continue +break +======== ===== + + +If - Else Statement +------------------- + + +The if statement simply performs a comparison on two or more values and provides a logical outcome based upon that evaluation. If statements are quite often used for branching code into one direction or another based upon certain values which have been calculated or provided in the code. + + +For instance, the statement will compare the values and return a boolean result, namely True or False. A corresponding action is then taken based upon the outcome of the boolean result. Pseudocode would be as follows: + + + if True: + perform an action + else: + perform another action + + +Any number of *if/else* statements can be linked together in order to create a logical code branch, if you wish to use more than one else statement then all but the last else statements must be *elif* instead...and the last would be *else*. Note that each expression must be indented with the conditional statement out-dented and the resulting operation indented. Remember, a consistent indentation must be followed throughout the course of the program. The if statement is a good example of how well the consistent use of indention helps readability of a program. If you are coding in Java for example, you can space the code however you’d like as long as you use the curly braces to enclose the statement. This can lead to code that is very hard to read…the indentation which Python requires really shines through here. + + + + >>> if x == y: + ... print 'x is equal to y' + ... elif x > y: + ... print 'x is greater than y' + ... else: + ... print 'x is less than y' + ... + x is greater than y + + +While the code is simple, it demonstrates that using an *if* statement can result in branching code logic. + +There are also some statements in Python which assist in logic flow. These statements can be placed within an if statement or a loop (discussed in chapter 2) which will cause the logic of the statement to go in one direction or the other. + + + +pass Statement +-------------- + +Another useful statement for while working within loops is the *pass* statement. Often times we have the need to use a placeholder value in an application in order to simply pass through an area without performing any tasks when an area of code requires an implementation. The pass statement simply does nothing. An example for it’s usage would be when you have a block of code which you’d like to bypass for debugging purposes. It can also be used as a placeholder for a block of code which has not yet been implemented. + + while False: + pass + + +def Statement +------------- + +This is one of those statements that will become second nature for usage throughout any Python programmer's life. The *def* statement is used to define a function in an application. While we will not get into functions in this chapter, I will show you an example of this statement's usage. + +:: + + + def myFunctionName(parameterList): + implementation +:: + + +The pseudocode above demonstrates how one would use the *def* statement. + +print Statement +--------------- + +The *print* statement is used to display program output onto the screen. It can be used for displaying messages which are printed from within a program and also for printing values which may have been calculated. In order to display variable values within a print statement, we need to learn how to use some of the formatting options which are available to Python. This section will cover the basics of using the print statement along with how to display values by formatting your strings of text. + + + +In the Java language, we need to make a call to the System library in order to print something to the command line. In Python, this can be done with the use of the print statement. The most basic use of the *print* statement is to display a line of text. In order to do so, you simply enclose the text which you want to display within single or double quotes. Take a look at the following example written in Java, and compare it to the example immediately following which is rewritten in Python. I think you’ll see why the print statement in Jython makes life a bit easier. + +:: + + inspectValue(String val){ + if (val == null){ + System.out.println(“The value you have entered is not valid, please try again”; + } else { + System.out.println( “The value you have entered is valid”); +:: + + + +As you can see from the example above, printing a line of text in Python is very straight forward. We can also print variable values to the screen using the print statement. + +This is great and all, but really not useful if you'd like to properly format your text or work with int values. After all, the Python parser is treating the (+) operator as a concatenation operator in this case...not as an addition operator. If you try to append a numeric value to a String you will end up with an error. + +As you can see from the example, Python does not like this trick very much. So in order to perform this task correctly we will need to use some of the aforementioned Python formatting options. This is easy and powerful to do, and it allows one to place any content or value into a print statement. Before you see an example, let's take a look at some of the formatting operators and how to choose the one that you need. + +If you wish to include the contents of a variable or the result of an expression in your print statement, you'll use the following syntax: + +In the pseudocode above (if we can really have pseudocode for print statements), we wish to print the string of text which is contained within the single quotes, but also have the values of the variables contained where the formatting operators are located. Each of the formatting operators, which are included in the string of text, will be replaced with the corresponding values from those variables at the end of the print statement. The % symbol between the line of text and the list of variables tells Python that the it should expect the variables to follow, and that these value of these variables should be placed within the string of text in their corresponding positions. + +As you can see this is quite easy to use and very flexible. The next example shows that we also have the option of using expressions as opposed to variables within our statement. + + + +Another useful feature of the print statement is that it can be used for debugging purposes . If we simply need to find out the value of a variable during processing then it is easy to display using the *print* statement. Often times, using this technique can really assist in debugging and writing your code. + + +try-except-finally +------------------- + +The *try-except-finally* is the supported method for performing error handling within a Python application. The idea is that we try to run a piece of code and if it fails then it is caught and the error is handled in a proper fashion. We all know that if someone is using a program that displays an ugly long error message, it is not usually appreciated. Using the *try-except-finally* statement to properly catch and handle our errors can mitigate an ugly program dump. + + + +This approach is the same concept that is used within many languages, including Java. There are a number of defined *error types* within the Python programming language and we can leverage these error types in order to facilitate the *try-except-finally* process. When one of the defined error types is caught, then an implementation can be coded for handling the error, or can simply be logged, ignored, etc. The main idea is to avoid those ugly error messages and handle them neatly. If there is an exception that is caught within the block of code and we need a way to perform some cleanup tasks, we would place the cleanup code within the finally clause of the block. All code within the finally clause is always invoked. + + + +To begin, let's work with defining a generic *try-except-finally* example in which we simply place the *try* block around a piece of code and catch any errors that may be thrown. We'll assume that we are not sure exactly which type of error will be thrown, so to generically define the *try-except-finally*, we will use an error type of *Exception*...the default Python error type. + + + +:: + + + try: + implementation that may throw an error + except Exception: + handle the error which was thrown + finally: + perform some cleanup…called everytime +:: + + +To augment this example, we'll go ahead and define a simple function which takes two parameters and returns the value of the first parameter divided by the second. In order to demonstrate the *try-except-finally*, we'll throw one around the print statement in order to catch the programmer's mistake gracefully. + +:: + + + >>> def myFunction(x,y): + ... try: + ... print x / y + ... except Exception: + ... print 'An error has been caught by the program' + ... finally: + ... print 'Perform some cleanup' + + >>> myFunction(0,0) + An error has been caught by the program + Perform some cleanup + + + +We can see that by throwing the *try-except-finally* statement around the erroneous code, we've successfully caught the error and displayed a nice message. This will make our application users happy. However, this is not very practical because we don't really have any idea why the error was thrown or which error was thrown. In order to provide more specific details of the error, it is possible to name the exception and then display it or log it in the implementation. + +:: + + + >>> def myFunction(x,y): + ... try: + ... print x / y + ... except Exception, err: + ... print 'The following error has been caught: %s' %(err) + + >>> myFunction(4,2) + 2 + >>> myFunction(0,0) + The following error has been caught: integer division or modulo by zero + + + +Alright, this is looking much better now as we have named the exception "err" and then displayed it in our output. Now the application user has a meaningful error message to tell us about when they reach this piece of code. While this is much better than the generic error that we included in the first example, we still have not found the best way to handle the error. The details of this topic can be read about more in Chapter 5: Exception Handling in Jython. + + + +Assert statements are used for debugging purposes and error handling within a Python program. Basically, the assert statement checks to ensure that some value or expression is True. If it is True, then execution will continue without anything happening, but if it is False then the program will indicate as such by throwing an AssertionError. Errors and exceptions will be covered in more detail in later chapters. For now, understand that by throwing an AssertionError, the code can be flagged as incorrect in an instance where we are trying to debug for a True value. + + >>> x = 10 + >>> assert x == 11 + Traceback (most recent call last): + File "", line 1, in + AssertionError +In the given example, the assertion checks to see if x is equal to eleven. Obviously we can see that it is not, so it throws the expected AssertionError. + +:: + + +raise Statement +--------------- + + +The raise statement is used to throw or “raise” an exception in Python. You can place a raise statement anywhere that you wish to throw a specified error. There are a number of defined exceptions within the language which can be thrown. For instance, NameError is thrown when a specific piece of code is undefined or has no name. For a complete list of exceptions in Python, please visit Chapter 5. + +:: + + + >>> raise NameError + Traceback (most recent call last): + File "", line 1, in + NameError +:: + + +If you wish to specify your own message within a raise then you can do so by raising a generic Exception, and then specifying your message on the statement as follows. + + + + >>> raise Exception (‘ Custom Exception ’) + Traceback (most recent call last): + File "", line 1, in + Exception: Custom Exception +:: + + +import Statement +---------------- + + +The import statement is use much like it is in other languages, it brings external modules or code into a program so that it can be used. This statement is ultimately responsible for reuse of code in multiple locations. The import statement allows us to save code into a flat file or script, and then use it in an application at a later time. + + + +There are a couple of different ways in which this statement can be used. It can be used to simply import a named module into an application, or it can be used to import a module or piece of code. If a class is stored in an external module that is named the same as the class itself, the import statement can be used to explicitly bring that class into an application. Similarly, if you wish to import only a portion of code which is contained within an external module, then the specific code can be named within using the syntax from <> import <>. Time to see some examples. + +:: + + + # Import a class named TipCalculator which is contained within + # a module named TipCalculator.py + + import TipCalculator + + + + # Import a function tipCalculator from within a module called ExternalModule.py + + from ExternalModule import tipCalculator + +Other Python Statements +----------------------- + + +There are some other Python statements that can be used within applications as well, but they are probably better meant to be discussed within a later chapter as they provide more advanced functionality. The following is a listing of other Python statements which you will read more about later on: + + + +exec – Execute Python code in a dynamic fashion + +global – References a variable a global (Chapter 4) + +with – New feature in 2.5 using __future__ (Chapter 7) + +class – Create or define a new class object (Chapter 6) + +yield – Used with generators, returns a value (Chapter 4) + + +Iteration +========= + + +The Python language has several iteration structures which are used to traverse through a series of items in a list, database records, or any other type of collection. The most commonly used iteration structure within the language is probably the *for* loop, which is known for its easy syntax and practical usage. However, the *while* loop still plays an important role in iteration, especially when you are not dealing with collections of data, but rather working with conditional expressions. + + + +This section will take you though each of these two iteration structures and touch upon the basics of using them. The *while* loop is relatively basic in usage, whereas there are many different implementations and choices when using the *for* loop. I will only touch upon the *for* loop from a high-level perspective in this introductory chapter, but if you wish to go more in-depth then please visit Chapter 3. + + + +While Loop +---------- + +The *while* loop construct is used in order to iterate through code based upon a provided conditional statement. As long as the condition is true, then the loop will continue to process. Once the condition evaluates to false, the looping ends. The pseudocode for *while* loop logic reads as follows: + +:: + + + while True + perform operation +The loop begins with the declaration of the *while* and conditional expression, and it ends once the conditional has been met. Keep in mind that we need to indent each of the lines of code that exist within the *while* loop. This not only helps the code to maintain readability, but it also allows Python to do away with the curly braces! + + + +:: + + int x = 9; + int y = 2; + int z = x – y; + while (y < x){ + System.out.println(“y is “ + z + “ less than x”); + y = y++; + } + +Now, let’s see the same code written in Python. + + + + >>> x = 9 + >>> y = 2 + >>> while y < x: + ... print 'y is %d less than x' % (x-y) + ... y = y + 1 + ... + y is 7 less than x + y is 6 less than x + y is 5 less than x + y is 4 less than x + y is 3 less than x + y is 2 less than x + y is 1 less than x +:: + + +In the example above, you can see that the conditional *y < x* is evaluated each time the loop passes. Along the way, we increment the value of *y* by one each time we iterate, so that eventually *y* is no longer < than *x* and the loop ends. + + + +For Loop +-------- + +We will lightly touch upon *for* loops in this chapter, but you can delve deeper into the topic in chapter two or three when lists, dictionaries, tuples, and ranges are discussed. For now, you should know that a *for* loop is used to iterate through a defined set of values. *For* loops are very useful for performing iteration through values because this is a concept which is used in just about any application. For instance, if you retrieve a list of database values, you can use a *for* loop to iterate through them and print each one out. + + + +The pseudocode to *for* loop logic is as follows: + +:: + + + for each value in this defined set: + perform operation +As you can see with the pseudocode, I’ve indented in a similar fashion to the way in which the other expression constructs are indented. This uniform indentation practice is consistent throughout the Python programming language. We’ll compare the for loop in Java to the Python syntax below so that you can see how the latter makes code more concise. + + + +:: + + for (x = 0; x <= 10; x++){ + System.out.println(x); + } +:: + + +Now, the same code implemented in Python: + + >>> for x in range(10): + ... print x + ... + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +:: + + +In the above example, we use a construct which has not yet been discussed. A range is a built-in function for Python which simply provides a range from one particular value to another. In the example, we pass the value 10 into the range which gives us all values between 0 and 10. We see this in the resulting print out after the expression. + + + +It is time to go back and cover a couple of Python statement keywords which we passed over previously. Now that we’ve seen how to implement a loop within the language, it is a good time to cover some statements that can be used along with a loop. + +continue Statement +------------------ + +The *continue* statement is to be used when you are within a looping construct, and you have the requirement to tell Python to *continue* processing past the rest of the statements in the current loop. Once the Python interpreter sees a *continue* statement, it ends the current iteration of the loop and goes on to continue processing the next iteration. The continue statement can be used with any looping construct. + + + +:: + + + >>> x = 10 + >>> while x >= 0: + ... if x == 0: + ... continue + ... else: + ... print "x is currently equal to ", x + ... x = x - 1 + ... + x is currently equal to 10 + x is currently equal to 9 + x is currently equal to 8 + x is currently equal to 7 + x is currently equal to 6 + x is currently equal to 5 + x is currently equal to 4 + x is currently equal to 3 + x is currently equal to 2 + x is currently equal to 1 +:: + + +In the example above, the x variable decreased by one each time the loop iterates. On the final pass, as the x is equal to 0 we do not display a message. Why is this example useful? It’s not really…only to give you an understanding of the context in which the continue statement would be used. + +break Statement +--------------- + +Much like the *continue* statement, the *break* statement can be used inside of a loop. We use the *break* statement in order to break out of a current loop so that a program can move onto its next task. If we are working with a break statement that resides within a loop that is contained in another loop (nested loop construct), then that inner loop will be terminated. Let’s check it out: + + + +:: + + + x = 10 while x >= 0: + if x == 0: + print "x is now equal to zero!" + break + else: + if x % 2 == 0: + print x + x = x – 1 + + Results: + + 10 + 8 + 6 + 4 + 2 + x is now equal to zero! + + +Documenting Code +================ + +Code documentation, an annoyingly important part of every application developer’s life. Although many of us despise code documentation, it must exist for any application that is going to be used for production purposes. Not only is proper code documentation a must for manageability and long-term understanding of Python code fragments, but it also plays an important role in debugging some code as we will see in some examples below. + + + +Sometimes we wish to document an entire function or class, and other times we wish to document only a line or two. Whatever the case, Python provides a way to do it in a rather unobtrusive manner. Much like many of the other programming languages that exist today, we can begin a comment on any part of any code line. We can also comment spanning multiple lines if we wish. Just on a personal note, I rather like the Python documentation symbol (#) or hash, as it provides for clear-cut readability. There are not many places in code that you will use the (#) symbol unless you are trying to perform some documentation. Many other languages use symbols such as (/) which can make code harder to read as those symbols are evident in many other non-documenting pieces of code. Okay, it is time to get off my soap box on Python and get down to business. + +In order to document a line of code, you simply start the document or comment with a (#) symbol. This symbol can be placed anywhere on the line and whatever follows it is ignored by the Python compiler and treated as a comment or documentation. Whatever precedes the symbol will be parsed as expected. + +:: + + + >>> # This is a line of documentation + >>> x = 0 # This is also documentation + >>> y = 20 + >>> print x + y + 20 + +As you can see, the Python parser ignores everything after the #, so we can easily document or comment as needed. + + + +One can easily document multiple lines of code using the # symbol as well by placing the hash at the start of each line. +It nicely marks a particular block as documentation. However, Python also provides a multi-line comment using the triple-quote (‘’’) +designation at the beginning and end of a comment. This type of multi-line comment is also referred to as a doc string and it is only +to be used at the start of a module, class, or function. Let’s take a look at these two instances of multi-line documentation in the examples that follow. :: + + + # This function is used in order to provide the square + # of any value which is passed in. The result will be + # passed back to the calling code. + def square_val(value): + return value * value + ... + >>> print square_val(3) + 9 + + + def tip_calc(value, pct): + ''' This function is used as a tip calculator based on a percentage + which is passed in as well as the value of the total amount. In + this function, the first parameter is to be the total amount of a + bill for which we will calculate the tip based upon the second + parameter as a percentage ''' + return value * (pct * .01) + ... + >>> print tip_calc(75,15) + 11.25 + + +Okay, as we can see, both of the documentation methods above can be used to get the task of documenting or comment code done. +In the first example, we used multiple lines of documentation beginning with the # symbol in order to document the *square_val* function. +In the second example, we use the triple-quote method in order to span multiple lines of documentation. Both of them appear to work as +defined...however, the second option provides a greater purpose as it allows one to document specific named code blocks and retrieve that +documentation by calling the __doc__ function on that block. For instance, if we wish to find out what the *square_val* code does, we need +to visit the code and either read the multi-line comment or simply parse the code. However, if we wish to find out what the tip_calc function +does, we can call the tip_calc.__doc__ function and the multi-line comment will be returned to us. This provides a great tool to use for +finding out what code does without actually visiting the code itself. :: + + + >>> print tip_calc.__doc__ + This function is used as a tip calculator based on a percentage + which is passed in as well as the value of the total amount. In + this function, the first parameter is to be the total amount of a + bill for which we will calculate the tip based upon the second + parameter as a percentage + +These examples and short explanations should give you a pretty good feel for the power of documentation that is provided by the Python language. +As you can see, using the multi-line triple-quote method is very suitable for documenting classes or functions. Commenting with the # symbol +provides a great way to organize comments within source and also for documenting those lines of code which may be “not so easy” to understand. + + + +Python Help +=========== + +Getting help when using the Jython interpreter is quite easy. Built into the interactive interpreter is an excellent help() +option which provides information on any module, keyword, or topic available to the Python language. While making use of the +help() system, you can either use the interactive help which is invoked within the interpreter by simply typing help(), or you +can obtain help on a specific object by typing help(object). + +Summary +======= + +This chapter has covered lots of basic Python programming material. It should have provided a basic foundation for the fundamentals +of programming in Python. This chapter shall be used to reflect upon while delving deeper into the language throughout the remainder of this book. + + + +We began by discussing the declaration of variables and explained the dynamic tendencies of the language. This gives us an understanding +that variables do not have any type declared with them, rather, they are untyped and can be modified into any Python data type. +We then went on to present the reserved words of the language and then discussed the coding structure which must be adhered to when +developing a Python application. After that, we discussed operators, expressions, and statements. We learned that expressions are +generally blocks of code that produce a value, and that statements consist of conditional and declarative reserved words that allow +us to perform different tasks within our applications. Each of the Python statements were discussed and examples were given. Iteration +constructs were then discussed so that we could begin to use our statements and program looping tasks. + + + +Following the language overview, documentation was discussed. It is an important part of any application, and Python makes it easy to do. +Not only did we learn how to document lines of code, but also documenting entire blocks of code. +Throughout the rest of the book, you will learn more in-depth and advanced uses of the topics that we’ve discussed in this chapter. +You will also learn concepts and techniques that you’ll be able to utilize in your own programs to make them more powerful and easy to maintain. + + + + + + + diff --git a/chapter10.rst b/chapter10.rst new file mode 100644 index 0000000..9318eff --- /dev/null +++ b/chapter10.rst @@ -0,0 +1,2 @@ +Chapter 10: Java and Jython Integration +======================================== \ No newline at end of file diff --git a/chapter11.rst b/chapter11.rst new file mode 100644 index 0000000..7c05696 --- /dev/null +++ b/chapter11.rst @@ -0,0 +1,2 @@ +Chapter 11: Using Jython in an IDE +=================================== \ No newline at end of file diff --git a/chapter12.rst b/chapter12.rst new file mode 100644 index 0000000..47a7ef7 --- /dev/null +++ b/chapter12.rst @@ -0,0 +1,2 @@ +Chapter 12: Databases and Jython +================================= \ No newline at end of file diff --git a/chapter13.rst b/chapter13.rst new file mode 100644 index 0000000..c90f428 --- /dev/null +++ b/chapter13.rst @@ -0,0 +1,2 @@ +Chapter 13: Simple Web Applications +==================================== \ No newline at end of file diff --git a/chapter14.rst b/chapter14.rst new file mode 100644 index 0000000..e31630f --- /dev/null +++ b/chapter14.rst @@ -0,0 +1,2 @@ +Chapter 14: Web Applications with Django +========================================= \ No newline at end of file diff --git a/chapter15.rst b/chapter15.rst new file mode 100644 index 0000000..c00e79f --- /dev/null +++ b/chapter15.rst @@ -0,0 +1,2 @@ +Chapter 15: Web Applications with Pylons +========================================= \ No newline at end of file diff --git a/chapter16.rst b/chapter16.rst new file mode 100644 index 0000000..de921de --- /dev/null +++ b/chapter16.rst @@ -0,0 +1,2 @@ +Chapter 16: Web Services and SOA +================================= \ No newline at end of file diff --git a/chapter17.rst b/chapter17.rst new file mode 100644 index 0000000..fa28d44 --- /dev/null +++ b/chapter17.rst @@ -0,0 +1,2 @@ +Chapter 17: GUI Applications +============================= \ No newline at end of file diff --git a/chapter18.rst b/chapter18.rst new file mode 100644 index 0000000..c2199d6 --- /dev/null +++ b/chapter18.rst @@ -0,0 +1,2 @@ +Chapter 18: Development Targets +================================ \ No newline at end of file diff --git a/chapter19.rst b/chapter19.rst new file mode 100644 index 0000000..5c09af7 --- /dev/null +++ b/chapter19.rst @@ -0,0 +1,2 @@ +Chapter 19: Testing and Continuous Integration +=============================================== \ No newline at end of file diff --git a/chapter2.rst b/chapter2.rst new file mode 100644 index 0000000..9c2ae12 --- /dev/null +++ b/chapter2.rst @@ -0,0 +1,1050 @@ +Chapter 2: Data Types and Referencing ++++++++++++++++++++++++++++++++++++++ + +We all know that programming languages and applications need data. We define applications to work with data, +and we need to have containers that can be used to hold it. This chapter is all about defining containers and +using them to work with application data. This is the foundation of any programming language...it is how we get +tasks accomplished. Whether the data we are using is coming from a keyboard entry or if we are working with a +database, there needs to be a way to temporarily store it in our programs so that it can be manipulated and used. +Once we're done working with the data then these temporary containers can be destroyed in order to make room for new constructs. + +We'll start by taking a look at the different data types that are offered by the Python language, and then we'll +follow by discussing how to use that data once it has been collected and stored. We will compare and contrast the +different types of structures that we have in our arsenal, and I'll give some examples of which structures to use +for working with different types of data. There are a multitude of tasks that can be accomplished through the use +of lists, dictionaries, and tuples and I will cover the majority of them. Once you learn how to define and use these +structures, then we'll talk a bit about what happens to them once they are no longer needed by our application. + +Lets begin our journey into exploring data types and structures within the Python programming language...these are skills +that you will use in each and every practical Jython program. + + +Python Data Types +================= + +As we’ve discussed, there is a need to store and manipulate data within programs. In order to do so then we must also +have the ability to create containers used to hold that data so that the program can use it. The language needs to know +how to handle data once it is stored, and we can do that by assigning data type to our containers. However, in Python +it is not a requirement to do so because the interpreter is able to determine which type of data we are storing in a dynamic fashion. + +The following table lists each data type and gives a brief description of the characteristics that define each of them. + + + +=========== ========================================================================================= +Data Type Characteristics +=========== ========================================================================================= +None NULL value object +Numeric A data type used to hold numeric values of integer, decimal, float, complex, and long +Boolean True or False value (also characterized as numeric values of 1 and 0 respectively) +Sequence Includes the following types: string, unicode string, basestring, xrange, list, tuple +Mapping Includes the dictionary type +Set Unordered collection of distinct objects; includes the following types: set, frozenset +File Used to make use of file system objects +Iterator Allows for iteration over a container + +=========== ========================================================================================= + +Table 2-1. Python Data Types + + + +Given all of that information and the example above, we need to know a way to declare a variable in the Python language. +You’ve seen some examples in the previous chapter, but here I will formally show how it is done. Let’s take a look at some +examples of defining variables in the following lines of code. :: + + + # Defining a String + x = ‘Hello World’ + x = “Hello World Two” + + # Defining a number + y = 10 + + # Float + z = 8.75 + + # Complex + i = 8.07j + +An important point to note is that there really are no types in Jython. Every object is an instance of a class. Therefore, +in order to find the type of an object in Jython it is perfectly valid to write obj.__class__. :: + + + + # Return the type of an object in Jython using __class__ + >>> a = 'Hello' + >>> a.__class__ + + + +Strings and String Methods +-------------------------- + +Strings are a special type within most programming languages because they are often used to manipulate data. A string +in Python is a sequence of characters, which is immutable. This is very important to know as it has a large impact on +the overall understanding of strings. Once a string has been defined it cannot be changed. However, there are a large +amount of string methods that can be used to manipulate the contents of a particular string. Although we can manipulate +the contents, Python really gives us a manipulated copy of the string…the original string is left unchanged. + +CPython and Jython treat strings a bit differently. There are two types of string objects in CPython, these are known as +*Standard* strings and *Unicode* strings. Standard strings contain 8-bit data, whereas Unicode strings are sequences of data +composed of 16-bit characters. There is a lot of documentation available that specifically focuses on the differences between +the two types of strings, this reference will only cover the basics. It is worth noting that Python contains an abstract string +type known as *basestring* so that it is possible to check any type of string to ensure that it is a string instance. + +In Jython, there is only one string type. The string type in Jython supports full two-byte Unicode characters and all functions +contained in the string module are Unicode-aware. If the u’’ string modifier is specified, it is ignored by Jython. It is also +worth noting that Jython uses character properties from the Java platform. Therefore properties such as isupper and islower, which +we will discuss later in the section, are based upon the Java properties. + +In remainder of this section we will go through each of the many string functions that are at our disposal. These functions will +work on both Standard and Unicode strings. As with many of the other features in Python and other programming languages, there are +often times more than one way to accomplish a task. In the case of strings and string manipulation, this definitely holds true. +However, you will find that in most cases, although there are more than one way to do things, Python experts have added functions +which allow us to achieve better performing and easier to read code. Sometimes one way to perform a task is better achieved by +utilizing a certain function in one case, and doing something different in another case. + +The following table lists all of the string methods that have been built into the Python language as of the 2.5 release. Since Python +is an evolving language, this list is sure to change in future releases. Most often, additions to the language will be made, or +existing features are enhanced. Following the table, I will give numerous examples of the methods and how they are used. Although +I cannot provide an example of how each of these methods work (that would be a book in itself), they all function in the same manner +so it should be rather easy to pick up. + + + +================================== ========================================================================================================================================= === +Method Description of Functionality +================================== ========================================================================================================================================= === +capitalize() Capitalize string +center(width[,fill]) Reposition string and provide optional padding filler character +count(sub[,start[,end]]) Count the number of times the substring occurs within the string +decode([encoding[,errors]]) Decodes and returns Unicode string +encode([encoding[,errors]]) Produces an encoded version of a string +endswith(suffix[,start[,end]]) Returns a boolean to state whether the string ends in a given pattern +expandtabs([tabsize]) Converts tabs within a string into spaces +find(sub[,start[,end]]) Returns the index of the position where the first occurrence of the given substring begins +index(sub[,start[,end]) Returns the index of the position where the first occurrence of the given substring begins +isalnum() Returns a boolean to state whether the string contain both alphabetic and numeric characters +isalpha() Returns a boolean to state whether the string contains all alphabetic characters +isdigit() Returns a boolean to state whether the string contains all numeric characters +islower() Returns a boolean to state whether a string contains all lowercase characters +isspace() Returns a boolean to state whether the string consists of all whitespace +istitle() Returns a boolean to state whether the first character of each word in the string is capitalized +isupper() Returns a boolean to state whether all characters within the string are uppercase +join(sequence) Joins two strings by combining +ljust(width[,fillchar]) Align the string to the left by width +lower() Converts all characters in the string to lowercase +lstrip([chars]) Removes the first found characters in the string from the left that match the given characters. Also removes whitespace from the left. +partition(separator) Partitions a string starting from the left using the provided separator +replace(old,new[,count]) Replaces the portion of string given in *old* with the portion given in *new* +rfind(sub[,start[,end]]) Searches and finds the first occurrence of the given string +rindex(sub[,start[,end]]) Searches and finds the first occurrence of the given string or returns an error +rjust(width[,fillchar]) Align the string to the right by width +rpartition(separator) Partitions a string starting from the right using the provided separator object +rsplit([separator[,maxsplit]]) Splits the string from the right side and uses the given separator as a delimiter +rstrip([chars]) Removes the first found characters in the string from the right that match those given. Also removes whitespace from the right. +split([separator[,maxsplit]]) Splits the string and uses the given separator as a delimiter. +splitlines([keepends]) Splits the string into a list of lines. Keepends denotes if newline delimiters are removed. +startswith(prefix[,start[,end]]) Returns a boolean to state whether the string starts with the given prefix +strip([chars]) Removes the given characters from the string. +swapcase() Converts the case of each character in the string. +title() Returns the string with the first character in each word uppercase. +translate(table[,deletechars]) Use the given character translation table to translate the string. +upper() Converts all of the characters in the string to lowercase. +zfill(width) Pads the string from the left with zeros for the specified width. +================================== ========================================================================================================================================= === + +Table 2-2. String Methods + +Now let’s take a look at some examples so that you get an idea of how to use the string methods. As stated previously, most of them work in a similar manner. :: + + + + ourString=’python is the best language ever’ + + + # Capitalize a String + >>> ourString.capitalize() + 'Python is the best language ever' + + # Center string + >>> ourString.center(50) + ' python is the best language ever ' + >>> ourString.center(50,'-') + '---------python is the best language ever---------' + + # Count substring within a string + >>> ourString.count('a') + 2 + + # Partition a string + >>> x = "Hello, my name is Josh" + >>> x.partition('n') + ('Hello, my ', 'n', 'ame is Josh') + + +String Formatting +~~~~~~~~~~~~~~~~~ + +You have many options when printing strings using the *print* statement. Much like the C programming language, Python string +formatting allows you to make use of a number of different conversion types when printing. :: + + + Using String Formatting + # The two syntaxes below work the same + >>> x = "Josh" + >>> print "My name is %s" % (x) + My name is Josh + >>> print "My name is %s" % x + My name is Josh + + +====== ============================================================================ +Type Description +====== ============================================================================ +d signed integer decimal +i signed integer decimal +o unsigned octal +u unsigned decimal +x unsigned hexidecimal +X unsigned hexidecimal (upper) +E floating point exponential format (upper) +e floating point exponential format +f floating point decimal format +F floating point decimal format (upper) +g floating point exponential format if exponent > -4, otherwise float +G floating point exponential format (uppr) if exponent > -4, otherwise float +c single character +r string (converts any python object using repr()) +s string (converts any python object using str()) +% no conversion, results in a percent (%) character +====== ============================================================================ + +Table 2-3. Conversion Types + + :: + + + >>> x = 10 + >>> y = 5.75 + >>> print 'The expression %d * %f results in %f' % (x, y, x*y) + The expression 10 * 5.750000 results in 57.500000 + +Ranges +------ + +Ranges are not really a data type or a container; they are really a Jython built-in function (Chapter 4). For this reason, +we will only briefly touch upon the range function here, and they’ll be covered in more detail in Chapter 4. However, +because they play such an important role in the iteration of data, usually via the *for* loop, I think it is important to +discuss them in this section of the book. The range is a special function that allows one to iterate between a range of +numbers; and/or list a specific range of numbers. It is especially helpful for performing mathematical iterations, but +it can also be used for simple iterations. + +The format for using the range function includes an optional starting number, an ending number, and an optional stepping number. +If specified, the starting number tells the range where to begin, whereas the ending number specifies where the range should end. +The optional step number tells the range how many numbers should be placed between each number contained within the range output. + + +Range Format +~~~~~~~~~~~~ + + range([start], stop, [step]) + +:: + + >>>range(0,10) + + >>>range(10) + + >>>range(0,10,2) + >>> range(100,0,-10) + [100, 90, 80, 70, 60, 50, 40, 30, 20, 10] + +As stated previously, this function can be quite useful when used within a *for* loop as the Jython *for* loop syntax works +very well with it. The following example displays a couple examples of using the range function within a *for* loop context. :: + + >>> for i in range(10): + ... print i + ... + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + + # Multiplication Example + >>> x = 1 + >>> for i in range(2, 10, 2): + ... x = x + (i * x) + ... print x + ... + 3 + 15 + 105 + 945 + + +As you can see, a range can be used to iterate through just about any number set...be it positive or negative in range. + +Lists, Dictionaries, Sets, and Tuples +------------------------------------- + +Data collection containers are a useful tool for holding and passing data throughout the lifetime of an application. The data +can come from any number of places, be it the keyboard, a file on the system, or a database…it can be stored in a collection +container and used at a later time. Lists, dictionaries, sets, and tuples all offer similar functionality and usability, but +they each have their own niche in the language. We’ll go through several examples of each since they all play an important role +under certain circumstances. + +Since these containers are so important, we’ll go through an exercise at the end of this chapter, which will give you a chance +to try them out for yourself. + +Lists +~~~~~ + +Perhaps one of the most used constructs within the Python programming language is the list. Most other programming languages +provide similar containers for storing and manipulating data within an application. The Python list provides an advantage to +those similar constructs which are available in statically typed languages. The dynamic tendencies of the Python language help +the list construct to harness the great feature of having the ability to contain values of different types. This means that a +list can be used to store any Python data type, and these types can be mixed within a single list. In other languages, this type +of construct is defined as a typed object, which locks the construct to using only one data type. + +The creation and usage of Jython lists is just the same as the rest of the language…very simple and easy to use. Simply assigning +a set of empty square brackets to a variable creates an empty list. We can also use the built-in list() type to create a list. +The list can be constructed and modified as the application runs, they are not declared with a static length. They are easy to +traverse through the usage of loops, and indexes can also be used for positional placement or removal of particular items in the list. +We’ll start out by showing some examples of defining lists, and then go through each of the different avenues which the Jython +language provides us for working with lists. :: + + # Define an empty list + myList = [] + myList = list() + + # Define a list of string values + myStringList = [‘Hello’,’Jython’,’Lists’] + + # Define a list containing mulitple data types + multiList = [1,2,’three’,4,’five’,’six’] + + # Define a list containing a list + comboList = [1,myStringList,multiList] + +As stated previously, in order to obtain the values from a list we can make use of indexes. Much like the Array in the Java language, +using the *list[index]* notation will allow us to access an item. If we wish to obtain a range or set of values from a list, we can +provide a *starting* index, and/or an *ending* index. This technique is also known as *slicing*. What’s more, we can also return +a set of values from the list along with a stepping pattern by providing a *step* index as well. One key to remember is that while +accessing a list via indexing, the first element in the list is contained within the 0 index. :: + + # Obtain elements in the list + >>> myStringList[0] + ‘Hello’ + + >>> myStringList[2] + ‘Lists’ + + >>> myStringList[-1] + 'Lists' + + # Using the slice method + >>> myStringList[0:2] + ['Hello', 'Jython'] + + # Return every other element in a list + >>> newList=[2,4,6,8,10,12,14,16,18,20] + >>> newList[0:10:2] + [2, 6, 10, 14, 18] + + # Leaving a positional index blank will also work + >>> newList[::2] + [2, 6, 10, 14, 18] + +Modifying a list is much the same, you can either use the index in order to insert or remove items from a particular position. +There are also many other ways that you can insert or remove elements from the list. Jython provides each of these different +options as they provide different functionality for your operations. + +In order to add an item to a list, you can make use of the *append()* method in order to add an item to the end of a list. +The *extend()* method allows you to add an entire list or sequence to the end of a list. Lastly, the *insert()* method +allows you to place an item or list into a particular area of an existing list by utilizing positional indexes. +You will examples of each method below. + +Similarly, we have plenty of options for removing items from a list. The *del* statement, as explained in Chapter 1, +can be used to remove or delete an entire list or values from a list using the index notation. You can also use the +*pop() *or *remove()* method to remove single values from a list. The *pop()* method will remove a single value from +the end of the list, and it will also return that value at the same time. If an index is provided to the *pop()* function, +then it will remove and return the value at that index. The *remove()* method can be used to find and remove a particular +value in the list. If more than one value in the list matches the value passed into the *remove()* function, the first one +will be removed. Another note about the *remove()* function is that the value removed is not returned. Let’s take a look +at these examples of modifying a list. :: + + # Adding values to a list + >>> newList=['a','b','c','d','e','f','g'] + >>> newList.append('h') + >>> print newList + ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] + + # Add another list to the existing list + >>> newList2=['h','i','j','k','l','m','n','o','p'] + >>> newList.extend(newList2) + >>> print newList + ['a', 'b', 'c', 'd', 'e', 'f', 'g', ‘h’,'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] + + # Insert a value into a particular location via the index + >>> newList.insert(2,'c') + >>> print newList + ['a', 'b', 'c', 'c', 'd', 'e', 'f', 'g', 'h', ‘h’,'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] + + # Use the slice notation to insert another list or sequence + >>> newListA=[100,200,300,400] + >>> newListB=[500,600,700,800] + >>> newListA[0:2]=newListB + >>> print newListA + [500, 600, 700, 800, 300, 400] + + # Use the del statement to delete a list + >>> newList3=[1,2,3,4,5] + >>> print newList3 + [1, 2, 3, 4, 5] + >>> del newList3 + >>> print newList3 + Traceback (most recent call last): + File "", line 1, in + NameError: name 'newList3' is not defined + + # Use the del statement to remove a value or range of values from a list + >>> newList3=['a','b','c','d','e','f'] + >>> del newList3[2] + >>> newList3 + ['a', 'b', 'd', 'e', 'f'] + >>> del newList3[1:3] + >>> newList3 + ['a', 'e', 'f'] + + # Remove values from a list using pop and remove functions + >>> print newList + ['a', 'b', 'c', 'c', 'd', 'e', 'f', 'g', 'h',’h’, 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] + >>> newList.pop(2) + 'c' + >>> print newList + ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',’h’, 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] + >>> newList.remove('h') + >>> print newList + ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] + + # Useful example of using pop() function + >>> x = 5 + >>> timesList = [1,2,3,4,5] + >>> while timesList: + ... print x * timesList.pop(0) + ... + 5 + 10 + 15 + 20 + 25 + +Now that we know how to add and remove items from a list, it is time to learn how to manipulate the data within them. +Python provides a number of different methods that can be used to help us manage our lists. See the table below for a +list of these functions and what they can do. + + + +========= =============================================================================== +Method Tasks Performed +========= =============================================================================== +index Returns the index of the first value in the list which matches a given value. +count Returns the number of items in the list which match a given value. +sort Sorts the items contained within the list. +reverse Reverses the order of the items contained within the list +========= =============================================================================== + +Table 2-4. Python List Methods + +Let’s take a look at some examples of how these functions can be used on lists. :: + + # Returning the index for any given value + >>> newList=[1,2,3,4,5,6,7,8,9,10] + >>> newList.index(4) + 3 + + # Add a duplicate into the list and then return the index + >>> newList.append(6) + >>> newList + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 6] + >>> newList.index(6) + 5 + + # Using count() function to return the number of items which match a given value + >>> newList.count(2) + 1 + >>> newList.count(6) + 2 + + # Sort the values in the list + >>> newList.sort() + >>> newList + [1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10] + + # Reverse the order of the value in the list + >>> newList.reverse() + >>> newList + [10, 9, 8, 7, 6, 6, 5, 4, 3, 2, 1] + +Lists +~~~~~ + +Moving around within a list is quite simple. Once a list is populated, often times we wish to traverse through it +and perform some action against each element contained within it. You can use any of the Python looping constructs +to traverse through each element within a list. While there are plenty of options available, the *for* loop works +especially well. The reason is because of the simple syntax that the Python *for* loop uses. This section will show +you how to traverse a list using each of the different Python looping constructs. You will see that each of them has +advantages and disadvantages. + +Let’s first take a look at the syntax that is used to traverse a list using a *for* loop. This is by far one of the +easiest modes of going through each of the values contained within a list. The *for* loop traverses the list one +element at a time, allowing the developer to perform some action on each element if so desired. :: + + >>> ourList=[1,2,3,4,5,6,7,8,9,10] + >>> for elem in ourList: + ... print elem + ... + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + + +As you can see from this simple example, it is quite easy to go through a list and work with each item individually. The +*for* loop syntax requires a variable to which each element in the list will be assigned for each pass of the loop. +Additionally, we can still make use of the current index while traversing a loop this way if needed. The only requirement +is to make use of the *index()* method on the list and pass the current element. :: + + >>>ourList=[1,2,3,4,5,6,7,8,9,10] + >>> for elem in ourList: + ... print 'The current index is: %d' % (ourList.index(elem)) + ... + The current index is: 0 + The current index is: 1 + The current index is: 2 + The current index is: 3 + The current index is: 4 + The current index is: 5 + The current index is: 6 + The current index is: 7 + The current index is: 8 + The current index is: 9 + +If we do not wish to go through each element within the list then that is also possible via the use of the *for* loop. +In this case, we’ll simply use a list slice to retrieve the exact elements we want to see. For instance, take a look +a the following code which traverses through the first 5 elements in our list. :: + + + >>> for elem in ourList[0:5]: + ... print elem + ... + 1 + 2 + 3 + 4 + 5 + +To illustrate a more detailed example, lets say that you wished to retrieve every other element within the list. :: + + >>> for elem in ourList[0::2]: + ... print elem + ... + 1 + 3 + 5 + 7 + 9 + +As you can see, doing so is quite easy by simply making use of the built-in features that Python offers. + + +List Comprehensions +~~~~~~~~~~~~~~~~~~~ + +There are some advanced features for lists that can help to make a developer’s life easier. Once such feature is known +as a *list comprehension*. While this concept may be daunting at first, it offers a good alternative to creating many separate +lists manually or using map(). List comprehensions take a given list, and then iterate through it and apply a given expression +against each of the objects in the list. This allows one to quickly take a list and alter it via the use of the provided expression. +Of course, as with many other Python methods the list comprehension returns an altered copy of the list. The original list is left untouched. + + +Let’s take a look at the syntax for a list comprehension. They are basically comprised of an expression of some kind followed by a +*for* statement and then optionally more *for* or *if* statements. As they are a difficult technique to describe, let’s take a look +at some examples. Once you’ve seen list comprehensions in action you are sure to understand them and see how useful they can be. :: + + # Create a list of ages and add one to each of those ages using a list comprehension + >>> ages=[20,25,28,30] + >>> [age+1 for age in ages] + [21, 26, 29, 31] + + # Create a list of names and convert the first letter of each name to uppercase as it should be + >>> names=['jim','frank','vic','leo','josh'] + >>> [name.title() for name in names] + ['Jim', 'Frank', 'Vic', 'Leo', 'Josh'] + + # Create a list of numbers and return the square of each EVEN number + >>> numList=[1,2,3,4,5,6,7,8,9,10,11,12] + >>> [num*num for num in numList if num % 2 == 0] + [4, 16, 36, 64, 100, 144] + + # Use a list comprehension with a range + >>> [x*5 for x in range(1,20)] + [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95] + + +List comprehensions can make code much more concise and allows one to apply expressions or functions to list elements quite easily. +Let’s take a quick look at an example written in Java for performing the same type of work as an easy list comprehension. It is plain +to see that list comprehensions are much more concise. :: + + int[] ages = {20, 25, 28, 30}; + + // Use a newstyle Java for loop to go through each element in the array + for (int age : ages){ + age++; + } + +Dictionaries +~~~~~~~~~~~~ + +A dictionary is quite different than a typical list in Python as there is no automatically populated index for any given element +within the dictionary. When you use a list, you need not worry about assigning an index to any value that is placed within it. +However, a dictionary forces the developer to assign an index or “key” for every element that is placed into the construct. Therefore, +each entry into a dictionary requires two values, the *key* and the *element*. + +The beauty of the dictionary is that it allows the developer to choose the data type of the key value. Therefore, if one wishes +to use a string value as a key then it is entirely possible. Dictionary types also have a multitude of methods and operations that +can be applied to them to make them easier to work with. + +===================================================================================================================================== ============================================================================================================ +Method or Operation Description +===================================================================================================================================== ============================================================================================================ +len(dictionary) Returns number of items within the given dictionary. +dictionary[key] Returns the item from the list that is associated with the given key. +dictionary[key] = value Sets the associated item in the list to the given value. +del dictionary[key] Deletes the given key/value pair from the list. +dictionary.clear() Removes all items from the dictionary. +dictionary.copy() Creates a shallow copy of the dictionary. +has_key(key) Returns a boolean stating whether the dictionary contains the given key. +items() Returns a copy of the key/value pairs within the dictionary. +keys() Returns the keys within the dictionary. +update([dictionary2]) Updates dictionary with the key/value pairs from the given dictionary. Existing keys will be overwritten. +fromkeys(sequence[,value]) Creates a new dictionary with keys from the given sequence. The values will be set to the values given. +values() Returns the values within the dictionary. +get(key[, b]) Returns the value associated with the given key. If the key does not exist, then returns b. +setdefault(key[, b]) Returns the value associated with the given key. If the key does not exist, then returns and sets b. +pop(key[, b]) Returns and removes the value associated with the given key. If the key does not exist then returns b. +popItem() Removes and returns the first key/value pair in the dictionary. +iteritems() Returns an iterator over the key/value pairs in the dictionary. +iterkeys() Returns an iterator over the keys in the dictionary. +itervalues() Returns an iterator over the values in the dictionary. +===================================================================================================================================== ============================================================================================================ + +Table 2-5. Mapping type methods and operations. + +Now we will take a look at some dictionary examples. This reference will not show you an example of using each of the mapping operations, +but it should provide you with a good enough base understanding of how they work. :: + + # Create an empty dictionary and a populated dictionary + >>> myDict={} + >>> myDict.values() + [] + >>> myDict.has_key(1) + False + >>> myDict[1] = 'test' + >>> myDict.values() + ['test'] + >>> len(myDict) + 1 + + # Replace the original dictionary with a dictionary containing string-based keys + # The following dictionary represents a hockey team line + >>> myDict = {'r_wing':'Josh','l_wing':'Frank','center':'Jim','l_defense':'Leo','r_defense':'Vic'} + >>> myDict.values() + ['Josh', 'Vic', 'Jim', 'Frank', 'Leo'] + >>> myDict.get('r_wing') + 'Josh' + + # Iterate over the items in the dictionary + >>> hockeyTeam = myDict.iteritems() + >>> for player in hockeyTeam: + ... print player + ... + ('r_wing', 'Josh') + ('r_defense', 'Vic') + ('center', 'Jim') + ('l_wing', 'Frank') + ('l_defense', 'Leo') + + >>> for key,value in myDict.iteritems(): + ... print key, value + ... + r_wing Josh + r_defense Vic + center Jim + l_wing Frank + l_defense Leo + +Sets +~~~~ + +Sets are unordered collections of unique elements. What makes sets different than other sequence types is that they contain +no indexing. They are also unlike dictionaries because there are no key values associated with the elements. They are an arbitrary +collection of unique elements. Sets cannot contain mutable objects, but they can be mutable. + +There are two different types of sets, namely *set* and *frozenset*. The difference between the two is quite easily conveyed +from the name itself. A regular *set* is a mutable collection object, whereas a *frozen* set is immutable. Much like sequences and +mapping types, sets have an assortment of methods and operations that can be used on them. Many of the operations and methods work +on both mutable and immutable sets. However, there are a number of them that only work on the mutable set types. In the two tables +that follow, we’ll take a look at the different methods and operations. + + + +============================ ============================================================== +Method or Operation Description +============================ ============================================================== +len(set) Returns the number of elements in a given set. +copy() +difference(set2) +intersection(set2) +issubbset(set2) +issuperset(set2) +symmetric_difference(set2) +union(set2) +============================ ============================================================== + +Table 2-6. Set Type Methods and Operations + + + +=================================== ===================================================================== +Method or Operation Description +=================================== ===================================================================== +add(item) Adds an item to a set if it is not already in the set. +clear() Removes all items in a set. +difference_update(set2) +discard(item) +intersection_update(set2) +pop() +remove() +symmetric_difference_update(set2) +update(set2) +=================================== ===================================================================== + +Table 2-7. Mutable Set Type Methods and Operations + +Tuples +~~~~~~ + +Tuples are much like lists, however they are immutable. Once a tuple has been defined, it cannot be changed. +They contain indexes just like lists, but again, they cannot be altered once defined. Therefore, the index in +a tuple may be used to retrieve a particular value and not to assign or modify. + +Since tuples are a member of the sequence type, they can use the same set of methods an operations available +to all sequence types. :: + + # Creating an empty tuple + >>> myTuple = () + + # Creating tuples and using them + >>> myTuple2 = (1, 'two',3, 'four') + >>> myTuple2 + (1, 'two', 3, 'four') + + +Jython Specific Collections +--------------------------- + +There are a number of Jython specific collection objects that are available for use. Most of these collection +objects are used to pass data into Java classes and so forth, but they add additional functionality into the Jython +implementation that will assist Python newcomers that are coming from the Java world. Nonetheless, many of these +additional collection objects can be quite useful under certain situations. + +In the Jython 2.2 release, Java collection integration was introduced. This enables a bidirectional interaction +between Jython and Java collection types. For instance, a Java ArrayList can be imported in Jython and then used +as if it were part of the language. Prior to 2.2, Java collection objects could act as a Jython object, but Jython +objects could not act as Java objects. :: + + # Import and use a Java ArrayList + >>> import java.util.ArrayList as ArrayList + >>> arr = ArrayList() + >>> arr.add(1) + True + >>> arr.add(2) + True + >>> print arr + [1, 2] + + +Ahead of the integration of Java collections, Jython also had implemented the *jarray* object which basically allows +for the construction of a Java array in Jython. In order to work with a *jarray*, simply define a sequence type in +Jython and pass it to the *jarray* object along with the type of object contained within the sequence. The *jarray* +is definitely useful for creating Java arrays and then passing them into java objects, but it is not very useful for +working in Jython objects. Moreover, all values within a jarray must be the same type. If you try to pass a sequence +containing multiple types to a jarray then you’ll be given a *TypeError* of one kind or another. + +=========== === ================= ========= +Character Java Equivalent +=========== === ================= ========= +z boolean +b byte +c char +d double +f float +h short +i int +l long +=========== === ================= ========= + +Table 2-8. Character Typecodes for use with Jarray :: + + >>> mySeq = (1,2,3,4,5) + >>> from jarray import array + >>> array(mySeq,int) + array(org.python.core.PyInteger, [1, 2, 3, 4, 5]) + + >>> myStr = "Hello Jython" + >>> array(myStr,'c') + array('c', 'Hello Jython') + +Files +----- + +File objects are used to read and write data to a file on disk. The file object is used to obtain a reference +to the file on disk and open it for reading, writing, appending, or a number of different tasks. If we simply +use the *open(filename[, mode])* function, we can return a file type and assign it to a variable for processing. +If the file does not yet exist on disk, then it will automatically be created. The *mode* argument is used to +tell what type of processing we wish to perform on the file. This argument is optional and if omitted then the +file is opened in read-only mode. + +======= === ==================================== +Mode Description +======= === ==================================== +‘r’ read only +‘w’ write +‘a’ append +‘r+’ read and write +‘rb’ Windows binary file read +‘wb’ Windows binary file write +‘r+b’ Windows binary file read and write +======= === ==================================== + +Table 2-9. Modes of Operations for File Types + + # Open a file and assign it to variable f + + +There are plenty of methods that can be used on file objects for manipulation of the file content. We can call +*read([size])* on a file in order to read it’s content. Size is an optional argument here and it is used to tell +how much content to read from the file. If it is omitted then the entire file content is read. The *readline()* +method can be used to read a single line from a file. *readlines([size])* is used to return a list containing +all of the lines of data that are contained within a file. Again, there is an optional *size* parameter that +can be used to tell how many bytes from the file to read. If we wish to place content into the file, the *write(string)* +method does just that. The *write()* method writes a string to the file. + +When writing to a file it is oftentimes important to know exactly what position in the file you are going to write to. +There are a group of methods to help us out with positioning within a file using integers to represent bytes in the file. +The *tell()* method can be called on a file to give the file object’s current position. The integer returned is in bytes +and is an offset from the beginning of the file. The *seek(offset, from)* method can be used to change position in a +file. The *offset* is the number in bytes of the position you’d like to go, and *from* represents the place in the file +where you’d like to calculate the *offset* from. If *from* equals 0, then the offset will be calculated from the beginning +of the file. Likewise, if it equals 1 then it is calculated from the current file position, and 2 will be from the end of +the file. The default is 0 if *from* is omitted. + +Lastly, it is important to allocate and de-allocate resources efficiently in our programs or we will incur a memory overhead +and leaks. The *close()* method should be called on a file when we are through working with it. The proper methodology +to use when working with a file is to open, process, and then close each time. However, there are more efficient ways +of performing such tasks. In Chapter 5 we will discuss the use of context managers to perform the same functionality in +a more efficient manner. :: + + File Manipulation in Python + # Create a file, write to it, and then read it’s content + + >>> f = open('newfile.txt','r+') + >>> f.write('This is some new text for our file\n') + >>> f.write('This should be another line in our file\n') + # No lines will be read because we are at the end of the written content + >>> f.read() + '' + >>> f.readlines() + [] + >>> f.tell() + 75L + # Move our position back to the beginning of the file + >>> f.seek(0) + >>> f.read() + 'This is some new text for our file\nThis should be another line in our file\n' + >>> f.seek(0) + >>> f.readlines() + ['This is some new text for our file\n', 'This should be another line in our file\n'] + >>> f.close() + +Iterators +--------- + +The iterator type was introduced into Python back in version 2.2. It allows for iteration over Python containers. +All iterable containers have built-in support for the iterator type. For instance, sequence objects are iterable +as they allow for iteration over each element within the sequence. If you try to return an iterator on an object +that does not support iteration, you will most likely receive an *AttributeError* which tells you that __iter__ +has not been defined as an attribute for that object. + +Iterators allow for easy access to sequences and other iterable containers. Some containers such as dictionaries +have specialized iteration methods built into them as you have seen in previous sections. Iterator objects are +required to support two main methods that form the iterator protocol. Those methods are defined below. + + + +===================== =================================================================================================== ========================================= +Method Description +===================== =================================================================================================== ========================================= +iterator.__iter__() Returns the iterator object on a container. Required to allow use with *for* and *in* statements +iterator.next() Returns the next item from a container. +===================== =================================================================================================== ========================================= + +Table 2-10: Iterator Protocol + +To return an iterator on a container, just assign *container.__iter__()* to some variable. That variable will become +the iterator for the object. If using the *next()* call, it will continue to return the next item within the list +until all items have been retrieved. Once this occurs, a *StopIteration* error is issued. The important thing to note +here is that we are actually creating a copy of the list when we return the iterator and assign it to a variable. That +variable returns and removes an item from that copy each time the *next()* method is called on it. If we continue to +call *next()* on the iterator variable until the *StopIteration* error is issued, the variable will no longer contain +any items and is empty. + +Referencing and Copies +====================== + +Creating copies and referencing items in the Python language is fairly straightforward. The only thing you’ll need to +keep in mind is that the techniques used to copy mutable and immutable objects differ a bit. + +In order to create a copy of an immutable object, you simply assign it to a different variable. The new variable is an +exact copy of the object. If you attempt to do the same with a mutable object, you will actually just create a reference +to the original object. Therefore, if you perform operations on the “copy” of the original then the same operation will +actually be performed on the original. This occurs because the new assignment references the same mutable object in memory +as the original. It is kind of like someone calling you by a different name. One person may call you by your birth name +and another may call you by your nickname, but both names will reference you of course. + +To effectively create a copy of a mutable object, you have two choices. You can either create what is known as a *shallow* +copy or a *deep* copy of the original object. The difference is that a shallow copy of an object will create a new object +and then populate it with references to the items that are contained in the original object. Hence, if you modify any of +those items then each object will be affected since they both reference the same items. A deep copy creates a new object +and then recursively copies the contents of the original object into the new copy. Once you perform a deep copy of an object +then you can perform operations on the copied object without affecting the original. You can use the *deepcopy* function in +the Python standard library to create such a copy. Let’s look at some examples of creating copies in order to give you a +better idea of how this works. :: + + + # Create an integer variable, copy it, and modify the copy + >>> a = 5 + >>> b = a + >>> print b + 5 + >>> b = a * 5 + >>> b + 25 + >>> a + 5 + + # Create a list, assign it to a different variable and then modify + >>> listA = [1,2,3,4,5,6] + >>> print listA + [1, 2, 3, 4, 5, 6] + >>> listB = listA + >>> print listB + [1, 2, 3, 4, 5, 6] + >>> del listB[2] + # Oops, we’ve altered the original list! + >>> print listA + [1, 2, 4, 5, 6] + + # Create a deep copy of the list and modify it + >>> import copy + >>> listA = [1,2,3,4,5,6] + >>> listB = copy.deepcopy(listA) + >>> print listA + [1, 2, 3, 4, 5, 6] + >>> del listB[2] + >>> print listB + [1, 2, 4, 5, 6] + >>> print listA + [1, 2, 3, 4, 5, 6] + + +Garbage Collection +================== + +This is one of those major differences between CPython and Jython. Unline CPython, Jython does not implement a +reference counting technique for aging out or garbage collection unused objects. Instead, Jython makes use of the +garbage collection mechanisms that the Java platform provides. When a Jython object becomes stale or unreachable, +the JVM may or may not reclaim it. One of the main aspects of the JVM that made developers so happy in the early +days is that there was no longer a need to worry about cleaning up after your code. In the C programming language, +one must maintain an awareness of which objects are currently being used so that when they are no longer needed the +program would perform some clean up. Not in the Java world, the gc thread on the JVM takes care of all garbage +collection and cleanup for you. This is a benefit of using the Jython implementation; unlike Python there is no need +to worry about reference counting. + +Even though we haven’t spoken about classes yet, it is a good time to mention that Jython provides a mechanism for +object cleanup. A finalizer method can be defined in any class in order to ensure that the garbage collector performs +specific tasks. Any cleanup code that needs to be performed when an object goes out of scope can be placed within +this finalizer method. It is important to note that the finalizer method cannot be counted on as a method which will +always be invoked when an object is stale. This is the case because the finalizer method is invoked by the Java garbage +collection thread, and there is no way to be sure when and if the garbage collector will be called on an object. Another +issue of note with the finalizer is that they incur a performance penalty. If you’re coding an application that already +performs poorly then it may not be a good idea to throw lots of finalizers into it. + + +Below is an example of a Jython finalizer. It is an instance method that must be named __del__. :: + + class MyClass: + def __del__(self): + pass # Perform some cleanup here + + +The downside to using the JVM garbage collection mechanisms is that there is really no guarantee as to when and if an +object will be reclaimed. Therefore, when working with performance intensive objects it is best to not rely on a finalizer +to be called. It is always important to ensure that proper coding techniques are used in such cases when working with objects +like files and databases. Never code the close() method for a file into a finalizer because it may cause an issue if the +finalizer is not invoked. Best practice is to ensure that all mandatory cleanup activities are performed before a finalizer +would be invoked. + +Summary +======= + +A lot of material was covered in this chapter. You should be feeling better acquainted with Python after reading through +this material. We began the chapter by covering the basics of assignment an assigning data to particular objects or data types. +We learned that working with each type of data object opens different doors as the way we work with each type of data object +differs. Our journey into data objects began with numbers and strings, and we discussed the many functions available to the +string object. We learned that strings are part of the sequence family of Python collection objects along with lists and tuples. +We covered how to create and work with lists, and the variety of options available to us when using lists. We discovered that +list comprehensions can help us create copies of a given list and manipulate their elements according to an expression or function. +After discussing lists, we went on to discuss dictionaries, sets and tuples. These objects give us different alternatives to +the list object. + +After discussing the collection types, we learned that Jython has it’s own set of collection objects that differ from those in +Python. We can leverage the advantage of having the Java platform at our fingertips and use Java collection types from within +Jython. Likewise, we can pass a Jython collection to Java as a *jarray* object. We followed that topic with a discussion of file +objects and how they are used in Python. The topic of iteration and creating iterables followed. We finished up by discussing +referencing, copies, and garbage collection. We saw how creating different copies of objects does not always give you what you’d +expect, and that Jython garbage collection differs quite a bit from that of Python. + +The next chapter will help you to combine some of the topics you’ve learned about in this chapter as you will learn how to define +expressions and work with control flow. + + + + + + diff --git a/chapter20.rst b/chapter20.rst new file mode 100644 index 0000000..582d0ec --- /dev/null +++ b/chapter20.rst @@ -0,0 +1,2 @@ +Chapter 20: Concurrency and Parallelism +======================================== \ No newline at end of file diff --git a/chapter3.rst b/chapter3.rst new file mode 100644 index 0000000..e85052d --- /dev/null +++ b/chapter3.rst @@ -0,0 +1,359 @@ +Chapter 3: Operators, Expressions, and Program Flow ++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Up until this point, we’ve have not yet covered the different means of writing expressions in Python. The focus of this chapter is to go in depth on each of the ways we can evaluate code, and write meaningful blocks of conditional logic. We’ll cover the details of each operator that can be used in Python expressions. This chapter will also cover some topics that have already been discussed in more meaningful detail. + + + +We will begin by discussing details of expressions. We have already seen some expressions in use while reading through the previous chapters. Here we will focus more on the internals of operators used to create expressions, and also different types of expressions that we can use. This chapter will go into further detail on how we can define blocks of code for looping and conditionals. + +Types of Expressions +-------------------- + +An expression in Python is a block of code that produces a result or value. Most often, we think of expressions that are used to perform mathematical operations within our code. However, there are a multitude of expressions used for other purposes as well. In Chapter 2, we covered the details of String manipulation, sequence and dictionary operations, and touched upon working with sets. All of the operations performed on these objects are forms of expressions in Python. + +This chapter will go into detail on how you write and evaluate mathematical expressions, boolean expressions, and augmented assignments. + +Mathematical Operations +----------------------- + +The Python language of course contains all of your basic mathematical operations. This section will briefly touch upon each operator that is available for use in Python and how they function. You will also learn about a few built-in functions which can be used to assist in your mathematical expressions. Finally, you’ll see how to use conditionals in the Python language and learn order of evaluation. + +Assuming that this is not the first programming language you are learning, there is no doubt that you are at least somewhat familiar with performing mathematical operations within your programs. Python is no different than the rest when it comes to mathematics, as with most programming languages, performing mathematical computations and working with numeric expressions is straightforward. + + + +========== ============================================= +Operator Description +========== ============================================= +'+' Addition +'-' Subtraction +'*' Multiplication +/ Division +// Truncating Division +% Modulo (Remainder of Division) +** Power Operator ++var Unary Plus +-var Unary Minus +========== ============================================= + +Table 3-1: Numeric Operators + +Most of the operators in the table above are easily understood. However, the truncating division, modulo, power, and unary operators could use some explanation. Truncating division will automatically truncate a division result into an integer. Modulo will return the remainder of a division operation. The power operator does just what you’d expect as it returns the result of the number to the left of the operator multiplied by itself n times, where n represents the number to the right of the operator. Unary plus and unary minus are used to evaluate positive or negative numbers. The following set of examples will help to clarify these topics. :: + + # Performing basic mathematical computations + + >>> 10 - 6 + 4 + >>> 9 * 7 + 63 + >>> 9 / 3 + 3 + >>> 10 / 3 + 3 + >>> 10 // 3 + 3 + >>> 3.14 / 2 + 1.57 + >>> 3.14 // 2 + 1.0 + >>> 36 / 5 + 7 + >>> 36 % 5 + 1 + >>> 5**2 + 25 + >>> 100**2 + 10000 + >>> -10 + 5 + -5 + >>> +5 - 5 + 0 + +There is a new means of division available in Jython 2.5 by importing from __future__. In a standard division for 2.5 and previous releases, the quotient returned is an integer or the floor of the quotient when arguments are ints or longs. However, a reasonable approximation of the division is returned if the arguments are floats or complex. Often times this solution is incorrect as the quotient should be the reasonable approximation or “true division” in any case. When we import *division* from the __future__ module then we alter the return value of division by causing true division when using the / operator, and floor division when using the // operator. Since this is going to be the standard in future releases, it is best practice to import from __future__ when performing division in Jython 2.5. :: + + >>> from __future__ import division + >>> 9/5 + 1.8 + >>> 9/4 + 2.25 + >>> 9/3 + 3.0 + >>> 9//3 + 3 + >>> 9//5 + 1 + + +As stated at the beginning of the section, there are a number of built-in mathematical functions that are at your disposal. + +================= =============================================================================== +Function Description +================= =============================================================================== +abs(var) Absolute value +pow(x, y) Used in place of power operator +pow(x,y,modulo) Ternary power-modulo +round(var[, n]) Returns a value rounded to the nearest 10-n +divmod(x, y) Returns both the quotient and remainder of division operation +================= =============================================================================== + +Table 3-2: Mathematical Built-in functions :: + + # The following code provides some examples for using mathematical built-ins + >>> abs(9) + 9 + >>> abs(-9) + 9 + >>> divmod(8,4) + (2, 0) + >>> pow(8,2) + 64 + >>> pow(8,2,3) + 1 + >>> round(5.67,1) + 5.7 + >>> round(5.67) + 6.0 + + + +The bitwise and logical operators as well as the conditional operators can be used to combine and compare logic. As with the mathematical operators described above, these operators have no significant difference to that of Java. + + + +========== ==================================== +Operator Description +========== ==================================== +> Greater than +< Less than +>= Greater than or equal +<= Less than or equal +!= Not equal +== Equal +& Bitwise and +| Bitwise or +^ Bitwise xor +~ Bitwise negation +<< Shift left +>> Shift right +========== ==================================== + +Table 3-3: Bitwise and Conditional Operators + + + +Augmented assignment operators are those that combine two or more operations into one. While augmented assignment can assist in coding concisely, some say that too many such operators can make code more difficult to read. + + + +========== =================================== +Operator Description and Logic +========== =================================== ++= a = a + b +-= a = a – b +*= a = a * b +/= a = a / b +%= a = a % b +//= a = a // b +**= a = a** b +&= a = a & b +|= a = a | b +^= a = a ^ b +>>= a = a >> b +<<= a = a << b +========== =================================== + +Table 3-4: Augmented Assignment Operators + + + +Boolean Expressions +------------------- + +Comparing two or more values or expressions also uses a similar syntax to that of other languages, and the logic is quite the same. Note that in Python, *True* and *False* are very similar to constants in the Java language. *True* actually represents the number *1*, and *False* represents the number *0*. One could just as easily code using 0 and 1 to represent the boolean values, but for readability and maintenance the *True* and *False* “constants” are preferred. Java developers, make sure that you capitalize the first letter of these two words as you will receive an ugly *NameError* if you do not. + + + +============= ======= ================================================================= +Conditional Logic +============= ======= ================================================================= +and In an *x and y* evaluation, both x and y must evaluate to True +or In an *x or y* evaluation, if x is false then y is evaluated. +not In a *not x* evaluation, if *not x*, we mean the opposite of x +============= ======= ================================================================= + +Table 3-5: Boolean Conditionals + +Conversions +----------- + + + +There are a number of conversion functions built into the language in order to help conversion of one data type to another. While every data type in Jython is actually a class object, these conversion functions will really convert one class type into another. For the most part, the built-in conversion functions are easy to remember because they are primarily named after the type to which you are trying to convert. + +======================= ======================================================================================================= +Function Description +======================= ======================================================================================================= +chr(value) Converts integer to a character +complex(real [,imag]) Produces a complex number +dict(sequence) Produces a dictionary from a given sequence of (key,value) tuples +eval(string) Evaluates a string to return an object…useful for mathematical computations +float(value) Converts to float +frozenset(set) Converts a set into a frozen set +hex(value) Converts an integer into a hex string +int(value [, base]) Converts to an integer using a base if a string is given +list(sequence) Converts a given sequence into a list +long(value [, base]) Converts to a long using a base if a string is given +oct(value) Converts integer to octal +ord(value) Converts a character into it’s integer value +repr(value) Converts object into an expression string. Same as enclosing expression in reverse quotes ( `x + y`) +set(sequence) Converts a sequence into a set +str(value) Converts an object into a string +tuple(sequence) Converts a given sequence to a tuple +unichr(value) Converts integer to a Unicode character +======================= ======================================================================================================= + +Table 3-6: Conversion Functions + +The following is an example of using the *eval()* functionality as it is perhaps the one conversion function for which an example helps to understand. :: + + # Suppose keyboard input contains an expression in string format (x * y) + >>> x = 5 + >>> y = 12 + >>> keyboardInput = 'x * y' + >>> eval(keyboardInput) + 60 + +Program Flow +------------ + +The Python programming language has structure that sets it apart from the others. As you’ve learned in previous references in this book, the statements that make up programs in Python are structured with attention to spacing, order, and technique. In order to develop a statement in Python, you must adhere to proper spacing techniques throughout the code block. Convention and good practice adhere to four spaces of indentation per statement throughout the entire program. Follow this convention along with some control flow and you’re sure to develop some easily maintainable software. + +The standard Python if-else conditional statement is used in order to evaluate expressions and branch program logic based upon the outcome. Expressions that are usable in an if-else statement can consist of any operators we’ve discussed previously. The objective is to write and compare expressions in order to evaluate to a *True* or *False* outcome. As shown in Chapter 1, the logic for an *if-else* statement follows one path if an expression evaluates to *True*, or a different path if it evaluates to *False.* + +You can chain as many *if-else* expressions together as needed. The combining *if-else* keyword is *elif*, which is used for every expression in between the first and the last expressions within a conditional statement. :: + + # terminal symbols are left out of this example so that you can see the concise indentation + pi =3.14 + x = 2.7 * 1.45 + if x == pi: + print ‘The number is pi’ + elif x > pi: + print ‘The number is greater than pi’ + else: + print ‘The number is less than pi’ + +Another construct that we touched upon in Chapter 1 was the loop. Every programming language provides looping implementations, and Python is no different. The Python language provides two main types of loops known as the *while* and the *for* loop. The *while* loop logic follows the same semantics as the *while* loop in Java. The loop will continue processing until the expression evaluates to *False*. At this time the looping ends and that would be it for the Java implementation. However, in Python the *while * loop construct also contains an *else* clause which is executed when the looping completes. :: + + while True: + # perform some processing + else: + print ‘Processing Complete…’ + +This *else* clause can come in handy while performing intensive processing so that we can inform the user of the completion of such tasks. It can also be handy when debugging code. Also mentioned in Chapter 1 were the *break*, and *continue* statements. These all come in handy when using any looping construct. The *break* statement can be used to break out of a loop. It should be noted that if there are nested loops then the *break* statement will break out of the inner-most loop only, the outer loops will continue to process. The *continue* statement can be used to break out of the current processing statement and continue the loop from the beginning. The *continue* can be thought of as a skipping statement as it will cause execution to skip all remaining statements in the block and restart from the beginning (if the loop expression still evaluates to *True* of course). :: + + while x != y: + # perform some processing + if x < 0: + break + else: + print ‘The program executed to completion’ + +In the example above, the program will continue to process until x does not equal y. However, if at any point during the processing the x variable evaluates less than zero, then the execution stops. The *else* clause will not be executed if the *break* statement is invoked. It will only be executed under normal termination of the loop. + + + +The *for* loop can be used on any iterable object. It will simply iterate through the object and perform some processing during each pass. Both the *break* and *continue* statements can also be used within the *for* loop. The *for* statement in Python also differs from the same statement in Java because in Python we also have the *else* clause with this construct. Once again, the *else* clause is executed when the *for* loop processes to completion without any *break* intervention. Also, if you are familiar with pre-Java 5 *for* loops then you will love the Python syntax. In Java 5, the syntax of the *for* statement was adjusted a bit to make it more in line with syntactically easy languages such as Python. :: + + for(x = 0; x <= myList.size(); x++){ + // processing statements iterating through myList + System.out.println(“The current index is: “ + x); + } + + x = 0 + for value in myList: + # processing statements using value as the current item in myList + print ‘The current index is %i’ % (x) + x = x + 1 + +As you can see, the Python syntax is a little easier to understand, but it doesn’t really save too many keystrokes at this point. We still have to manage the index (x in this case) by ourselves by incrementing it with each iteration of the loop. However, Python does provide a built-in function that can save us some keystrokes and provides a similar functionality to that of Java with the automatically incrementing index on the *for* loop. The *enumerate(sequence)* function does just that. It will provide an index for our use and automatically manage it for us. :: + + >>> myList = ['jython','java','python','jruby','groovy'] + >>> for index, value in enumerate(myList): + ... print index, value + ... + 0 jython + 1 java + 2 python + 3 jruby + 4 groovy + + +Now we have covered the program flow for conditionals and looping constructs in the Python language. Note that you can next to any level, and provide as many *if-else* conditionals as you’d like. However, good programming practice will tell you to keep it as simple as possible or the logic will become too hard to follow. + +Example Code +------------ + +Let’s take a look at an example program that uses some of the program flow which was discussed in this chapter. The example program simply makes use of an external text file to manage a list of players on a sports team. You will see how to follow proper program structure and use spacing effectively in this example. You will also see file utilization in action, along with utiliation of the *raw_input()* function. :: + + playerDict = {} + saveFile = False + exitSystem = False + # Enter a loop to enter inforation from keyboard + while not exitSystem: + print 'Sports Team Administration App' + enterPlayer = raw_input("Would you like to create a team or manage an existing team?\n (Enter 'C' for create, 'M' for manage, 'X' to exit) ") + if enterPlayer.upper() == 'C': + exitSystem = False + # Enter a player for the team + print 'Enter a list of players on our team along with their position' + enterCont = 'Y' + # While continuing to enter new players, perform the following + while enterCont.upper() == 'Y': + name = raw_input('Enter players first name: ') + position = raw_input('Enter players position: ') + playerDict[name] = position + saveFile = True + enterCont = raw_input("Enter another player? (Press 'N' to exit or 'Y' to continue)") + else: + exitSystem = True + elif enterPlayer.upper() == 'M': + exitSystem = False + # Read values from the external file into a dictionary object + print '\n’ + print 'Manage the Team' + playerfile = open('players.txt','r') + for player in playerfile: + playerList = player.split(':') + playerDict[playerList[0]] = playerList[1] + print 'Team Listing' + print '++++++++++++' + for i, player in enumerate(playerDict): + print 'Player %s Name: %s -- Position: %s' %(i, player, playerDict[player]) + else: + exitSystem = True + else: + # Save the external file and close resources + if saveFile: + print 'Saving Team Data...' + playerfile = open('players.txt','w') + for player in playerDict: + playerfile.write(player + ':' + playerDict[player] + '\n') + playerfile.close() + + +Summary +------- + +All programs are constructed out of definitions, statements and expressions. In this chapter we covered details of creating expressions and using them. Expressions can be composed of any number of mathematical operators and comparisons. In this chapter we discussed the basics of using mathematical operators in our programs. The __future__ division topic introduced us to using features from the __future__. We then delved into comparisons and comparison operators. + +We ended this short chapter by discussing proper program flow and properly learned about the *if* statement as well as how to construct different types of loops in Python. In the next chapter you will learn how to write functions, and the use of many built-in functions will be discussed. + + + + + + + + + diff --git a/chapter4.rst b/chapter4.rst new file mode 100644 index 0000000..fd73a9b --- /dev/null +++ b/chapter4.rst @@ -0,0 +1,2 @@ +Chapter 4: Defining Functions and Using Built-Ins +================================================== \ No newline at end of file diff --git a/chapter5.rst b/chapter5.rst new file mode 100644 index 0000000..16864d8 --- /dev/null +++ b/chapter5.rst @@ -0,0 +1,489 @@ +Chapter 5: Exception Handling and Debugging ++++++++++++++++++++++++++++++++++++++++++++ + +Any good program makes use of a language’s exception handling mechanisms. There is no better way to frustrate an end-user then by having them run into an issue with your software and displaying a big ugly error message on the screen, followed by a program crash. Exception handling is all about ensuring that when your program encounters an issue, it will continue to run and provide informative feedback to the end-user or program administrator. Any Java programmer becomes familiar with exception handling on day one, as some Java code won’t even compile unless there is some form of exception handling put into place via the try-catch-finally syntax. Python has similar constructs to that of Java, and we’ll discuss them in this chapter. + + + +After you have found an exception, or preferably before your software is distributed, you should go through the code and debug it in order to find and repair the erroneous code. There are many different ways to debug and repair code; we will go through some debugging methodologies in this chapter. In Python as well as Java, the *assert* keyword can help out tremendously in this area. We’ll cover *assert* in depth here and learn the different ways that it can be used to help you out and save time debugging those hard-to-find errors. + +Exception Handling Syntax and Differences with Java +=================================================== + +Java developers are very familiar with the *try-catch-finally* block as this is the main mechanism that is used to perform exception handling. Python exception handling differs a bit from Java, but the syntax is fairly similar. However, Java differs a bit in the way that an exception is *thrown* in code. Now, realize that I just used the term *throw* …this is Java terminology. Python does not *throw* exceptions, but instead it *raises* them. Two different terms which mean basically the same thing. In this section, we’ll step through the process of handling and raising exceptions in Python code, and show you how it differs from that in Java. + +For those who are unfamiliar, I will show you how to perform some exception handling in the Java language. This will give you an opportunity to compare the two syntaxes and appreciate the flexibility that Python offers.:: + + try { + // perform some tasks that may throw an exception + } catch (ExceptionType messageVariable) { + // perform some exception handling + } finally { + // execute code that must always be invoked + } + + +Now let’s go on to learn how to make this work in Python. Not only will we see how to handle and raise exceptions, but you’ll also learn some other great techniques later in the chapter. + +Catching Exceptions +------------------- + +How often have you been working in a program and performed some action that caused the program to abort and display a nasty error message? It happens more often than it should because most exceptions can be caught and handled nicely. By nicely, I mean that the program will not abort and the end user will receive a descriptive error message stating what the problem is, and in some cases how it can be resolved. The exception handling mechanisms within programming languages were developed for this purpose. + +Below is a table of all exceptions that are built into the Python language along with a description of each. You can write any of these into a clause and try to handle them. Later in this chapter I will show you how you and them if you’d like. Lastly, if there is a specific type of exception that you’d like to throw that does not fit any of these, then you can write your own exception type object. + + +================================ ================================================================= +Exception Descripton +================================ ================================================================= +BaseException This is the root exception for all others + GeneratorExit Raised by close() method of generators for terminating iteration + KeyboardInterrupt Raised by the interrupt key + SystemExit Program exit + Exception Root for all non-exiting exceptions + StopIteration Raised to stop an iteration action + StandardError Base class for all built-in exceptions + ArithmeticError Base for all arithmetic exceptions + FloatingPointError Raised when a floating-point operation fails + OverflowError Arithmetic operations that are too large + ZeroDivisoinError Division or modulo operation with zero is divisor + AssertionError Used when an assert statement fails + AttributeError Attribute reference or failure to assign correctly + EnvironmentError An error occurred outside of Python + IOError Error in Input/Output operation + OSError An error occurred in the os module + EOFError input() or raw_input() tried to read past the end of a file + ImportError Import failed to find module or name + LookupError Base class for IndexError and KeyError + IndexError A sequence index goes out of range + KeyError Referenced a non-existent mapping (dict) key + MemoryError Memory exhausted + NameError Failure to find a local or global name + UnboundLocalError Unassigned local variable is referenced + ReferenceError Attempt to access a garbage-collected object + RuntimeError Obsolete catch-all error + NotImplementedError Raised when a feature is not implemented + SyntaxError Parser encountered a syntax error + IndentationError Parser encountered an indentation issue + TabError Incorrect mixture of tabs and spaces + SystemError Non-fatal interpreter error + TypeError Inappropriate type was passed to a built-in operator or function + ValueError Argument error not covered by TypeError or a more precise error + Warning Base for all warnings +================================ ================================================================= + +The *try-except-finally* block is used in Python programs to perform the exception-handling task. Much like that of Java, +code that may or may not raise an exception should be placed in the *try* block. Differently though, exceptions that may be +caught go into an *except* block much like the Java *catch* equivalent. Any tasks that must be performed no matter if an exception +is thrown or not should go into the *finally* block. + +try-except-finally Logic :: + + try: + # perform some task that may raise an exception + except Exception, value: + # perform some exception handling + finally: + # perform tasks that must always be completed + +Python also offers an optional *else* clause to create the *try-except-else* logic. This optional code placed inside the +*else* block is run if there are no exceptions found in the block. + +try-finally logic: :: + + try: + # perform some tasks that may raise an exception + finally: + # perform tasks that must always be completed + +try-except-else logic: :: + + try: + # perform some tasks that may raise an exception + except: + # perform some exception handling + else: + # perform some tasks that should only be performed if no exceptions are thrown + +You can name the specific type of exception to catch within the *except* block , or you can generically define an exception +handling block by not naming any exception at all. Best practice of course states that you should always try to name +the exception and then provide the best possible handling solution for the case. After all, if the program is simply +going to spit out a nasty error then the exception handling block does not help resolve the issue at all. However, there +are some rare cases where it would be advantageous to not explicitly refer to an exception type when we simply wish to +ignore errors and move on. The *except* block also allows us to define a variable to which the exception message will +be assigned. This allows us the ability to store that message and display it somewhere within our exception handling code +block. If you are calling a piece of Java code from within Jython and the Java code throws an exception, it can be handled +within Jython in the same manner as Jython exceptions. + +Example 5-1: Exception Handling in Python :: + + + # Code without an exception handler + >>> x = 10 + >>> z = x / y + Traceback (most recent call last): + File "", line 1, in + NameError: name 'y' is not defined + + # The same code with an exception handling block + >>> x = 10 + >>> try: + ... z = x / y + ... except NameError, err: + ... print "One of the variables was undefined: ", err + ... + + One of the variables was undefined: name 'y' is not defined + + +Take note of the syntax that is being used for defining the variable that holds the error message. +Namely, the *except ExceptionType, value* statement syntax in Python and Jython 2.5 differs from that beyond 2.5. +In Python 2.6, the syntax changes a bit in order to ready developers for Python 3, which exclusively uses the new syntax. +Without going off topic too much, I think it is important to take note that this syntax will be changing in future +releases of Jython. + +Jython and Python 2.5 and Prior :: + + try: + // code + except ExceptionType, messageVar: + // code + +Jython 2.6 (Not Yet Implemented) and Python 2.6 and Beyond :: + + try: + // code + except ExceptionType as messageVar: + // code + +We had previously mentioned that it was simply bad programming practice to not explicitly name an exception type +when writing exception handling code. This is true, however Python provides us with another means to obtain the +type of exception that was thrown. There is a function provided in the *sys* package known as *sys.exc_info()* +that will provide us with both the exception type and the exception message. This can be quite useful if we are +wrapping some code in a *try-except* block but we really aren’t sure what type of exception may be thrown. Below +is an example of using this technique. + +Example 5-2: Using sys.exc_info() :: + + # Perform exception handling without explicitly naming the exception type + >>> x = 10 + >>> try: + ... z = x / y + ... except: + ... print "Unexpected error: ", sys.exc_info()[0], sys.exc_info()[1] + ... + Unexpected error: name 'y' is not defined + +Sometimes you may run into a situation where it is applicable to catch more than one exception. Python offers a +couple of different options if you need to do such exception handling. You can either use multiple *except­ clauses*, +which does the trick and works well, but may become too wordy. The other option that you have is to enclose your +exception types within parentheses and separated by commas on your *except* statement. Take a look at the following +example that portrays the latter approach using the same example from *Example 5-1.* + +Example 5-3: Handling Multiple Exceptions :: + + # Catch NameError, but also a ZeroDivisionError in case a zero is used in the equation + >>> x = 10 + >>> try: + ... z = x / y + ... except (NameError,ZeroDivisionError), err: + ... print "One of the variables was undefined: ", err + ... + One of the variables was undefined: name 'y' is not defined + + + # Using mulitple except clauses + >>> x = 10 + >>> y = 0 + >>> try: + ... z = x / y + ... except NameError, err1: + ... print err1 + ... except ZeroDivisionError, err2: + ... print 'You cannot divide a number by zero!' + ... + You cannot divide a number by zero! + +The *try-except­* block can be nested as deep as you’d like. In the case of nested exception handling blocks, +if an exception is thrown then the program control will jump out of the inner most block that received the error, +and up to the block just above it. This is very much the same type of action that is taken when you are working +in a nested loop and then run into a *break* statement, your code will stop executing and jump back up to the outer +loop. The following example shows an example for such logic. + +Example 5-4: Nested Exception Handling Blocks :: + + # Perform some division on numbers entered by keyboard + try: + # do some work + try: + x = raw_input ('Enter a number for the dividend: ') + y = raw_input('Enter a number to divisor: ') + x = int(x) + y = int(y) + except ValueError, err2: + # handle exception and move to outer try-except + print 'You must enter a numeric value!' + z = x / y + except ZeroDivisionError, err1: + # handle exception + print 'You cannot divide by zero!' + except TypeError, err3: + print 'Retry and only use numeric values this time!' + else: print 'Your quotient is: %d' % (z) + +Raising Exceptions +------------------ + +Often times you will find reason to raise your own exceptions. Maybe you are expecting a certain type +of keyboard entry, and a user enters something incorrectly that your program does not like. This would +be a case when you’d like to raise your own exception. The *raise* statement can be used to allow you to +raise an exception where you deem appropriate. Using the *raise* statement, you can cause any of the Python +exception types to be raised, you could raise your own exception that you define (discussed in the next section), +or you could raise a string exception. The *raise* statement is analogous to the *throw* statement in the +Java language. In Java we may opt to throw an exception if necessary. However, Java also allows you to +apply a *throws* clause to a particular method if an exception may possibly be thrown within instead of +using try-catch handler in the method. Python does not allow you do perform such techniques using the +*raise* statement. + +raise Statement Syntax :: + + raise ExceptionType or String[, message[, traceback]] + +As you can see from the syntax, using *raise* allows you to become creative in that you could use your own +string when raising an error. However, this is not really looked upon as a best practice as you should try +to raise a defined exception type if at all possible. You can also provide a short message explaining the +error. This message can be any string. Lastly, you can provide a *traceback* via use of *sys.exc_info()*. +Now you’ve surely seen some exceptions raised in the Python interpreter by now. Each time an exception is +raised, a message appears that was created by the interpreter to give you feedback about the exception and +where the offending line of code may be. There is always a *traceback* section when any exception is raised. +This really gives you more information on where the exception was raised. + +Example 5-5: Using the raise Statement :: + + >>> raise TypeError,"This is a special message" + Traceback (most recent call last): + File "", line 1, in + TypeError: This is a special message + +Defining Your Own Exceptions +============================ + +You can define your own exceptions in Python by creating an exception class. Now classes are a topic that +we have not yet covered, so this section gets a little ahead, but it is fairly straightforward. You simply +define a class using the *class* keyword and then give it a name. An exception class should inherit from +the base exception class, *Exception*. The easiest defined exception can simply use a pass statement inside +the class. More involved exception classes can accept parameters and define an initializer. It is also +a good practice to name your exception giving it a suffix of *Error*. + +Example 5-6: Defining an Exception Class :: + + class MyNewError(Exception): + pass + +The example above is the simplest type of exception you can create. This exception that was created above +can be raised just like any other exception now. :: + + raise MyNewError, “Something happened in my program” + +A more involved exception class may be written as follows. + +Example 5-7: Exception Class Using Initializer :: + + class MegaError(Exception): + “”” This is raised when there is a huge problem with my program””” + def __init__(self, val): + self.val = val + def __str__(self): + return repr(self.val) + +Issuing Warnings +================ + +Warnings can be raised at any time in your program and can be used to display some type of warning message, +but they do not necessarily cause execution to abort. A good example is when you wish to deprecate a method +or implementation but still make it usable for compatibility. You could create a warning to alert the user +and let them know that such methods are deprecated and point them to the new definition, but the program would +not abort. Warnings are easy to define, but they can be complex if you wish to define rules on them using +filters. Much like exceptions, there are a number of defined warnings that can be used for categorizing. In +order to allow these warnings to be easily converted into exceptions, they are all instances of the *Exception* +type. + +Table 5-2. Python Warning Categories + +======================= ========================================================================== +Warning Description +======================= ========================================================================== +Warning Root warning class +UserWarning A user-defined warning +DeprecationWarning Warns about use of a deprecated feature +SyntaxWarning Syntax issues +RuntimeWarning Runtime issues +FutureWarning Warns that a particular feature will be changing in a future release +======================= ========================================================================== + +Table 5-1: Exceptions + +To issue a warning, you must first import the *warnings* module into your program. Once this has been done +then it is as simple as making a call to the *warnings.warn()* function and passing it a string with the warning +message. However, if you’d like to control the type of warning that is issued, you can also pass the warning +category. :: + + import warnings + … + warnings.warn(“this feature will be deprecated”) + warnings.warn(“this is a more involved warning”, RuntimeWarning) + + +Importing the warnings module into your code gives you access to a number of built-in warning functions that can +be used. If you’d like to filter a warning and change its behavior then you can do so by creating a filter. +The following is a list of functions that come with the *warnings* module. + +=========================================================================== =============================================================== +Function and Description +=========================================================================== =============================================================== +warn(message[, category[, stacklevel]]) + Issues a warning. Parameters include a message string, + the optional category of warning, and the optional + stacklevel that tells which stack frame the warning + should originate from. + +warn_explicit(message, category, filename, lineno[, module[, registry]]) + This offers a more detailed warning message and makes category + a mandatory parameter. filename, lineno, and + module tell where the warning is located. registry represents + all of the current warning filters that are active. + +showwarning(message, category, filename, lineno[, file]) + Gives you the ability to write the warning to a file. + +formatwarning(message, category, filename, lineno) + Creates a formatted string representing the warning. + +resetwarnings() + Resets all of the warning filters. + +filterwarnings(action[, message[, category[, module[, lineno[, append]]]]]) +=========================================================================== =============================================================== + +This adds an entry into a warning filter list. Warning filters allow you to modify the behavior of a warning. +The action in the warning filter can be one from the following table of actions, message is a regular expression, +category is the type of a warning to be issued, module can be a regular expression, lineno is a line number to +match against all lines, append specifies whether the filter should be appended to the list of all filters. + +====================== =========================================================== +Filter Actions Description +====================== =========================================================== +‘always’ Always print warning message +‘default’ Print warning once for each location where warning occurs +‘error’ Converts a warning into an exception +‘ignore’ Ignores the warning +‘module’ Print warning once for each module in which warning occurs +‘once’ Print warning only one time +====================== =========================================================== + +Table 5-3. Warning Functions + +Warning filters are used to modify the behavior of a particular warning. There can be many different warning filters +in use, and each call to the *filterwarnings()* function will append another warning to the list of filters if so desired. +In order to see which filters are currently in use, issue the command *print warnings.filters*. One can also specify +a warning filter from the command line by use of the –W option. Lastly, all warnings can be reset to defaults by using +the *resetwarnings()* function.:: + + -Waction:message:category:module:lineno + + +Assertions and Debugging +======================== + +Debugging can be an easy task in Python via use of the *assert* statement and the *__debug__* variable. Assertions +are statements that can print to indicate that a particular piece of code is not behaving as expected. The assertion +checks an expression for a True or False value, and if False then it issues an *AssertionError* along with an optional +message. If the expression evaluates to True then the assertion is ignored completely. :: + + assert expression [, message] + +By effectively using the *assert* statement throughout your program, you can easily catch any errors that may occur +and make debugging life much easier. The following example will show you the use of the assert statement.:: + + # The following example shows how assertions are evaluated + >>> x = 5 + >>> y = 10 + >>> assert x < y, "The assertion is ignored" + >>> assert x > y, "The assertion works" + Traceback (most recent call last): + File "", line 1, in + AssertionError: The assertion works + + +You can make use of the internal ­­*­­__debug__* variable by placing entire blocks of code that should be run for debugging +purposes only inside a conditional based upon value of the variable. + +Example 5-10: Making Use of __debug__ :: + + if __debug__: + # perform some debugging tasks + +Context Managers +================ + +Ensuring that code is written properly in order to manage resources such as files or database connections is an important +topic. If files or database connections are opened and never closed then our program could incur issues. Often times, +developers elect to make use of the issues. Often times, developers elect to make use of the *try-finally* blocks to ensure +that such resources are handled properly. While this is an acceptable method for resource management, it can sometimes +be misused and lead to problems when exceptions are raised in programs. For instance, if we are working with a database +connection and an exception occurs after we’ve opened the connection, the program control may break out of the current block +and skip all further processing. The connection may never be closed in such a case. That is where the concept of context +management becomes an important new feature in Jython. Context management via the use of the *with* statement is new to +Jython 2.5, and it is a very nice way to ensure that resources are managed as expected. + +In order to use the *with* statement, you must import from __future__. The *with* statement basically allows you to take +an object and use it without worrying about resource management. For instance, let’s say that we’d like to open a file +on the system and read some lines from it. To perform a file operation you first need to open the file, perform any +processing or reading of file content, and then close the file to free the resource. Context management using the *with* +statement allows you to simply open the file and work with it in a concise syntax. + +Example 5-11: Python with Statement Example :: + + # Read from a text file named players.txt + >>> from __future__ import with_statement + >>> with open('players.txt','r') as file: + ... x = file.read() + ... + >>> print x + This is read from the file + + +In the example above, we did not worry about closing the file because the context took care of that for us. This works +with object that extends the context management protocol. In other words, any object that implements two methods named +*__enter__()* and *__exit__()* adhere to the context management protocol. When the *with *statement begins, the *__enter__()* +method is executed. Likewise, as the last action performed when the *with* statement is ending, the *__exit__()* +method is executed. The __enter__() method takes no arguments, whereas the __exit__() method takes three optional arguments +*type, value, *and* traceback. *The *__exit__()* method returns a *True* or *False* value to indicate whether an exception +was thrown. The *as variable* clause on the *with* statement is optional as it will allow you to make use of the object from +within the code block. If you are working with resources such as a lock then you may not the optional clause. + +If you follow the context management protocol, it is possible to create your own objects that can be used with this technique. +The *__enter__()* method should create whatever object you are trying to work if needed. If you are working with an immutable +object then you’ll need to create a copy of that object to work with in the *__enter__()* method. The *__exit__()* method +on the other hand can simply return *False* unless there is some other type of cleanup processing that needs to take place. + +Summary +======= + + + +In this chapter, we discussed many different topics regarding exceptions and exception handling within a Python application. +First, you learned the exception handling syntax of the *try-except-finally* code block and how it is used. We then discussed +why it may be important to *raise* your own exceptions at times and how to do so. That topic led to the discussion of how +to define an exception and we learned that in order to do so we must define a class that extends the *Exception* type object. + +After learning about exceptions, we went into the warnings framework and discussed how to use it. It may be important to use +warnings in such cases where code may be deprecated and you want to warn users, but you do not wish to *raise* any exceptions. +That topic was followed by assertions and how assertion statement can be used to help us debug our programs. Lastly, we touched +upon the topic of context managers and using the *with* statement that is new in Jython 2.5. + +In the next chapter you will delve into creating classes and learning about object-oriented programming in Python. Hopefully +if there were topics discussed in this chapter or previously in the book that may have been unclear due to unfamiliarity with +object orientation, they will be clarified in Chapter 6. + + diff --git a/chapter6.rst b/chapter6.rst new file mode 100644 index 0000000..9ded0ef --- /dev/null +++ b/chapter6.rst @@ -0,0 +1,887 @@ +Chapter 6: Object Oriented Jython +================================== + +This chapter is going to cover the basics of object oriented +programming. If you're familiar with the concepts. I'll start with +covering the basic reasons for why you would want to write object +oriented code in the first place, then cover all the basic syntax, and +finally I'll show you a non-trivial example. + +Object oriented programming is a method of programming where you +package your code up into bundles of data and behaviour. In Jython, +you can define a template for this bundle with a class definition +With this first class written, you can then instantiate copies of your +object and have them act upon each other. This helps you organize +your code into smaller more manageable bundles + +Throughout this chapter, I interchangably use Python and Jython - for +regular object oriented programming - the two dialects of Python are +so similar that there are no meaningful differences between the two +languages. Enough introduction text though - let's take a look at +some basic syntax to see what this is all about. + +Basic Syntax +------------ + +Writing a class is really simple. It is fundamentally about managing +some kind of 'state' and exposing some functions to manipulate that +state. In object jargon - we just call those functions 'methods'. + +Let's start by creating a car class. The goal is to create an object +that will manage it's own location on a two dimensional plane. We +want to be able to tell it to turn, move forward, and we want to be +able to interrogate the object to find out where it's current location +is. :: + + class Car(object): + + NORTH = 0 + EAST = 1 + SOUTH = 2 + WEST = 3 + + def __init__(self, x=0, y=0): + self.x = x + self.y = y + self.direction = 0 + + def turn_right(self): + self.direction += 1 + self.direction = self.direction % 4 + + def turn_left(self): + self.direction -= 1 + self.direction = self.direction % 4 + + def move(self, distance): + if self.direction == self.NORTH: + self.y += distance + elif self.direction == self.SOUTH: + self.y -= distance + elif self.direction == self.EAST: + self.x += distance + else: + self.x -= distance + + def position(self): + return (self.x, self.y) + +We'll go over that class definition in detail but right now, let's +just see how to create a car, move it around and ask the car where it +is. :: + + from car import Car + + def test_car(): + c = Car() + c.turn_right() + c.move(5) + assert (5, 0) == c.position() + + c.turn_left() + c.move(3) + + assert (5, 3) == c.position() + + if __name__ == '__main__': + test_car() + +The best way to think of a class is to think of it like a special kind of +function that acts like a factory that generates object instances. For +each call to the class - you are creating a new discrete copy of your +object. + +Once we've created the car instance, we can simply call functions that +are attached to the car class and the object will manage it's own +location. From the point of view of our test code - we do not need to +manage the location of the car - nor do we need to manage the +direction that the car is pointing in. We just tell it to move - and +it does the right thing. + +Let's go over the syntax in detail to see exactly what's going on +here. + +In Line 1, we declare that our Car object is a subclass of the root +"object" class. Python, like many object oriented languages has a +'root' object that all other objects are based off of. This 'object' +class defines basic behavior that all classes can reuse. + +Python actually has two kinds of classes - 'newstyle' and old style. +The old way of declaring classes didn't require you to type 'object' - +you'll occassionally see the old-style class usage in some Python +code, but it's not consider a good practice Just subclass 'object' +for any of your base classes and your life will be simpler[1]_. + +Lines 3 to 6 declare class attributes for the direction that any car +can point to. These are *class* attributes so they can be shared +across all object instances of the car object. + +Now for the good stuff. + +Line 8-11 declares the object initializer. In some languages, you +might be familiar with a constructor - in Jython, we have an +initializer which lets us pass values into an object at the time of +creation. + +In our initializer, we are setting the initial position of the car to +(0, 0) on a 2 dimensional plane and then the direction of the car is +initialized to pointing north. Fairly straight forward so far. + +The function signature uses Python's default argument list feature so +we don't have to explicitly set the initial location to (0,0), but +there's a new argument introduced called 'self'. This is a reference +to the current object. + +Remember - your class definition is creating instances of objects. +Once your object is created, it has it's own set of internal variables +to manage. Your object will inevitably need to access these as well +as any of the classes internal methods. Python will pass a reference +to the current object as the first argument to all your instance +methods. + +If you're coming from some other object oriented language, you're +probably familiar with the 'this' variable. Unlike C++ or Java, Python +doesn't magically introduce the reference into the namespace of +accessible variables, but this is consistent with Python's philosophy +of making things explicit for clarity. + +When we want to assign the initial x,y position, we just need to +assign values on to the name 'x', and 'y' on the object. Binding +the values of x and y to self makes the position values accessible to +any code that has access to self - namely the other methods of the +object. One minor detail here - in Python, you can technically +name the arguments however you want. There's nothing stopping you +from calling the first argument 'this' instead of 'self', but the +community standard is to use 'self' [2]_. + +Line 13 to 19 declare two methods to turn the vehicle in different +directions. Notice how the direction is never directly manipulated by +the caller of the Car object. We just asked the car to turn, and the +car changed it's own internal 'direction' state. + +Line 21 to 29 define where the car should move to when we move the car +forward. The internal direction variable informs the car how it +should manipulate the x and y position. Notice how the caller of the +car object never needs to know precisely what direction the car is +pointing in. The caller only needs to tell the object to turn and +move forward. The particular details of how that message is used is +abstracted away. + +That's not too bad for a couple dozen lines of code. + +This concept of hiding internal details is called encapsulation. This +is a core concept in object oriented programming. As you can see from +even this simple example - it allows you to structure your code so +that you can provide a simplified interface to the users of your code. + +Having a simplified interface means that we could have all kinds of +behaviour happening behind the function calls to turn and move - but +the caller can ignore all those details and concentrate on *using* the +car instead of managing the car. + +As long as the method signatures don't change, the caller really +doesn't need to care about any of that. We can easily add +persistence to this class - so we can save and load the car's state +to disk. + +First, pull in the pickle module - pickle will let us convert python +objects into byte strings that can be restored to full objects later. + + import pickle + +Now, just add two new methods to load and save the state of the object. :: + + def save(self): + state = (self.direction, self.x, self.y) + pickle.dump(state, open('mycar.pickle','wb')) + + def load(self): + state = pickle.load(open('mycar.pickle','rb')) + (self.direction, self.x, self.y) = state + +Simply add calls to save() at the end of the turn and move methods, +and the object will automatically save all the relevant internal +values to disk. + +People who use the car object don't even need to know that it's saving +to disk, because the Car object handles it behind the scenes. :: + + def turn_right(self): + self.direction += 1 + self.direction = self.direction % 4 + self.save() + + def turn_left(self): + self.direction -= 1 + self.direction = self.direction % 4 + self.save() + + def move(self, distance): + if self.direction == self.NORTH: + self.y += distance + elif self.direction == self.SOUTH: + self.y -= distance + elif self.direction == self.EAST: + self.x += distance + else: + self.x -= distance + self.save() + +Now, when you call the turn, or move methods, the car will +automatically save itself to disk. If you want to reconstruct the car +object's state from a previously saved pickle file, you can simply +call the load() method. + +Object Attribute Lookups +------------------------ + +If you've beeen paying attention, you're probably wondering how the +NORTH, SOUTH, EAST and WEST variables got bound to self. We never +actually assigned them to the self variable during object +initialization - so what's going on when we call move()? How is +Jythpon actually resolving the value of those four variables? + +Now seems like a good time to show how Jython resolves name lookups. + +The direction names actually got bound to the Car class. The Jython +object system does a little bit of magic when you try accessing any +*name* against an object, it first searches for anything that was +bound to 'self'. If python can't resolve any attribute on self with +that name, it goes up the object graph to the class definition. The +direction attributes NORTH, SOUTH, EAST, WEST were bound to the class +definition - so the name resolution succeeds and we get the value of +the class attribute. + +An very short example will help clarify this :: + + >>> class Foobar(object): + ... def __init__(self): + ... self.somevar = 42 + ... class_attr = 99 + ... + >>> + >>> obj = Foobar() + >>> obj.somevar + 42 + >>> obj.class_attr + 99 + >>> obj.not_there + Traceback (most recent call last): + File "", line 1, in + AttributeError: 'Foobar' object has no attribute 'not_there' + >>> + +So the key difference here is *what* you bind a value to. The values +you bind to self are available only to a single object. Values you +bind to the class definition are available to all instances of the +class. The sharing of class attributes among all instances is a +critical distinction because mutating a class attribute will affect +all instances. This may cause unintended side effects if you're not +paying attention as a variable may change value on you when you aren't +expecting it to. :: + + >>> other = Foobar() + >>> other.somevar + 42 + >>> other.class_attr + 99 + >>> # obj and other will have different values for somevar + >>> obj.somevar = 77 + >>> obj.somevar + 77 + >>> other.somevar + 42 + >>> # Now show that we have the same copy of class_attr + >>> other.class_attr = 66 + >>> other.class_attr + 66 + >>> obj.class_attr + 66 + +I think it's important to stress just how transparent Python's object +system really is. Object attributes are just stored in a plain python +dictionary. You can directly access this dictionary by looking at the +__dict__ attribute. :: + + >>> obj = Foobar() + >>> obj.__dict__ + {'somevar': 42} + +Notice that there are no references to the methods of the class, or +the class attribute. I'll reiterate it again - Python is going to +just go up your inheritance graph - and go to the class definition to +look for the methods of Foobar and the class attributes of foobar. + +The same trick can be used to inspect all the attributes of the class, +just look into the __dict__ attribute of the class definition and +you'll find your class attributes and all the methods that are +attached to your class definition :: + + >>> Foobar.__dict__ + {'__module__': '__main__', + 'class_attr': 99, + '__dict__': , + '__init__': } + +This transparency can be leveraged with dynamic programming techniques +using closures and binding new functions into your class definition at +runtime. We'll revisit this later in the chapter when we look at +generating function dynamically and finally with a short introduction +to metaprogramming. + +Inheritance and Overloading +--------------------------- + +In the car example, we subclass from the root object type. You can +also subclass your own classes to specialize the behaviour of your +objects. You may want to do this if you notice that your code +naturally has a structure where you have many different classes that +all share some common behaviour. + +With objects, you can write one class, and then reuse it using +inheritance to automatically gain access to the pre-existing behavior +and attributes of the parent class. Your 'base' objects will inherit +behaviour from the root 'object' class, but any subsequent subclasses +will inherit from your own classes. + +Let's take a simple example of using some animal classes to see how +this works. Define a module "animals.py" with the following code: + + class Animal(object): + def sound(self): + return "I don't make any sounds" + + class Goat(Animal): + def sound(self): + return "Bleeattt!" + + class Rabbit(Animal): + def jump(self): + return "hippity hop hippity hop" + + class Jackalope(Goat, Rabbit): + pass + +Now you should be able to explore that module with the jython +interpreter: + + >>> from animals import * + >>> animal = Animal() + >>> goat = Goat() + >>> rabbit = Rabbit() + >>> jack = Jackalope() + + >>> animal.sound() + "I don't make any sounds" + >>> animal.jump() + Traceback (most recent call last): + File "", line 1, in + AttributeError: 'Animal' object has no attribute 'jump' + + >>> rabbit.sound() + "I don't make any sounds" + >>> rabbit.jump() + 'hippity hop hippity hop' + + >>> goat.sound() + 'Bleeattt!' + >>> goat.jump() + Traceback (most recent call last): + File "", line 1, in + AttributeError: 'Goat' object has no attribute 'jump' + + >>> jack.jump() + 'hippity hop hippity hop' + >>> jack.sound() + 'Bleeattt!' + +Inheritance is a very simple concept, when you declare your class, you +simply specify which parent classes you would like to reuse. Your new +class can then automatically access all the methods and attributes of +the super class. Notice how the goat couldn't jump and the rabbit +couldn't make any sound, but the Jackalope had access to methods from +both the rabbit and the goat. + +With single inheritance - when your class simply inherits from one +parent class - the rules for resolving where to find an attribute or a +method are very straight forward. Jython just looks up to the parent if +the current object doesn't have a matching attribute. + +It's important to point out now that the Rabbit class is a type of +Animal - the Python runtime can tell you that programmatically by +using the isinstance function :: + + >>> isinstance(bunny, Rabbit) + True + >>> isinstance(bunny, Animal) + True + >>> isinstance(bunny, Goat) + False + +For many classes, you may want to extend the behavior of the parent +class instead of just completley overriding it. For this, you'll want +to use the super(). Let's specialize the Rabbit class like this. :: + + class EasterBunny(Rabbit): + def sound(self): + orig = super(EasterBunny, self).sound() + return "%s - but I have eggs!" % orig + +If you now try making this rabbit speak, it will extend the original +sound() method from the base Rabbit class :: + + >>> bunny = EasterBunny() + >>> bunny.sound() + "I don't make any sounds - but I have eggs!" + +That wasn't so bad. For these examples, I only demonstrated that +inherited methods can be invoked, but you can do exactly the same +thing with attributes that are bound to the self. + +For multiple inheritance, things get very tricky. In fact, the rules +for resolving how attributes are looked up would easily fill an entire +chapter (look up "The Python 2.3 Method Resolution Order" on Google if +you don't believe me). There's not enough space in this chapter to +properly cover the topic which should be a good indication to you that +you really don't want to use multiple inheritance. + +More advanced abstraction +------------------------- + +Abstraction using plain classes is wonderful and all, but it's even +better if your code seems to naturally fit into the syntax of the +language. Python supports a variety of underscore methods - methods +that start and end with double "_" signs that let you overload the +behaviour of your objects. This means that your objects will seem to +integrate more tightly with the language itself. + +With the underscore methods, you can give you objects behaviour for +logical and mathematical operations. You can even make your objects +behave more like standard builtin types like lists, sets or +dictionaries. + + from __future__ import with_statement + from contextlib import closing + + with closing(open('simplefile','w')) as fout: + fout.writelines(["blah"]) + + with closing(open('simplefile','r')) as fin: + print fin.readlines() + +The above snippet of code just opens a file, writes a little bit of +text and then we just read the contents out. Not terriblly exciting. +Most objects in Python are serializable to strings using the pickle +module. We can leverage pickle to write out full blown objects to +disk. Let's see the functional version of this: :: + + from __future__ import with_statement + from contextlib import closing + from pickle import dumps, loads + + def write_object(fout, obj): + data = dumps(obj) + fout.write("%020d" % len(data)) + fout.write(data) + + def read_object(fin): + length = int(fin.read(20)) + obj = loads(fin.read(length)) + return obj + + class Simple(object): + def __init__(self, value): + self.value = value + def __unicode__(self): + return "Simple[%s]" % self.value + + with closing(open('simplefile','wb')) as fout: + for i in range(10): + obj = Simple(i) + write_object(fout, obj) + + print "Loading objects from disk!" + print '=' * 20 + + with closing(open('simplefile','rb')) as fin: + for i in range(10): + print read_object(fin) + +This should output something like this: :: + + Loading objects from disk! + ==================== + Simple[0] + Simple[1] + Simple[2] + Simple[3] + Simple[4] + Simple[5] + Simple[6] + Simple[7] + Simple[8] + Simple[9] + +So now we're doing something interesting. Let's look at exactly what +happening here. + +First, you'll notice that the Simple object is rendering a nice - the +Simple object can render itself using the __unicode__ method. This is +clearly an improvement over the earlier rendering of the object with angle +brackets and a hex code. + +The write_object function is fairly straight forward, we're just +converting our objects into strings using the pickle module, computing +the length of the string and then writing the length and the actual +serialized object to disk. + +This is fine, but the read side is a bit clunky. We don't really know +when to stop reading. We can fix this using the iteration protocol. +Which bring us to one of my favourite reasons to use objects at all in +Python. + +Protocols +--------- + +In Python, we have 'duck typing'. If it sounds like a duck, quacks +like a duck and looks like a duck - well - it's a duck. This is in +stark contrast to more rigid languagse like C# or Java which have +formal interface definitions. One of the nice benefits of having duck +typing is that Python has the notion of object 'protocols'. + +If you happen to implement the right methods - python will recognize +your object as a certain type of 'thing'. + +Iterators are objects that look like lists that let you read the next +object. Implementing an iterator protocol is straight forward - just +implement a next() method and a __iter__ method and you're ready to +rock and roll. Let's see this in action: :: + + class PickleStream(object): + """ + This stream can be used to stream objects off of a raw file stream + """ + def __init__(self, file): + self.file = file + + def write(self, obj): + data = dumps(obj) + length = len(data) + self.file.write("%020d" % length) + self.file.write(data) + + def __iter__(self): + return self + + def next(self): + data = self.file.read(20) + if len(data) == 0: + raise StopIteration + length = int(data)well + return loads(self.file.read(length)) + + def close(self): + self.file.close() + +The above class will let you wrap a simple file object and you can now +send it raw python objects to write to a file, or you can read objects +out as if the stream was just a list of objects. Writing and reading +becomes much simpler :: + + with closing(PickleStream(open('simplefile','wb'))) as stream: + for i in range(10): + obj = Simple(i) + stream.write(obj) + + with closing(PickleStream(open('simplefile','rb'))) as stream: + for obj in stream: + print obj + +Abstracting out the details of serialization into the PickleStream +lets us 'forget' about the details of how we are writing to disk. All +we care about is that the object will do the right thing when we call +the write() method. + +The iteration protocol can be used for much more advanced uses, but +even with this example, it should be obvious how useful it is. While +you could implement the reading behaviour with a read() mo loethod, just +using the stream as something you can loop over makes the code much +easier to understand. + +An aside a common problem that everyone seems to have +----------------------------------------------------- + +One particular snag that seems to catch every python programmer is +when you use default values in a method signature. :: + + >>> class Tricky(object): + ... def mutate(self, x=[]): + ... x.append(1) + ... return x + ... + >>> obj = Tricky() + >>> obj.mutate() + [1] + >>> obj.mutate() + [1, 1] + >>> obj.mutate() + [1, 1, 1] + +What's happening here is that the instance method 'mutate' is an +object. The method object stores the default value for 'x' in an +attribute *inside* the method object. So when you go and mutate the +list, you're actually changing the value of an attribute of the method +itself. Remember - this happens because when you invoke the mutate +method, you're just accessing a callable attribute on the Tricky +object. + +Runtime binding of methods +-------------------------- + +One interesting feature in Python is that instance methods are +actually just attributes hanging off of the class defintion - the +functions are just attributes like any other variable, except that +they happen to be 'callable'. + +It's even possible to create and bind in functions to a class +definition at runtime using the new module to create instance methods. +In the following example, you can see that it's possible to define a +class with nothing in it, and then bind methods to the class +definition at runtime. :: + + >>> def some_func(self, x, y): + ... print "I'm in object: %s" % self + ... return x * y + ... + >>> import new + >>> class Foo(object): pass + ... + >>> f = Foo() + >>> f + <__main__.Foo object at 0x1> + >>> Foo.mymethod = new.instancemethod(some_func, f, Foo) + >>> f.mymethod(6,3) + I'm in object: <__main__.Foo object at 0x1> + 18 + +When you invoke the 'mymethod' method, the same attribute lookup +machinery is being invoked. Python looks up the name against the +'self' object. When it can't find anything there, it goes to the +class definition. When it finds it there, the instancemethod object +is returned. The function is then caled with two arguments and you +get to see the final result. + +This kind of dynamism in Jython is extremely powerful. You can write +code that generates functions at program runtime and then bind those +functions to objects. You can do all of this because in Jython, +classes are what are known as 'first class objects'. The class +definition itself is an actual object - just like any other object. +Manipulating classes is as easy as manipulating any other object. + +Closures and Passing Objects +---------------------------- + +Python supports the notion of nested scopes - this can be used by to +preserve some state information inside of another function. This +technique isn't all that common outside of dynamic languages, so you +may have never seen this before. Let's look at a simple example :: + + def adder(x): + def inner(y): + return x + y + return inner + + >>> func = adder(5) + >>> func + + >>> func(8) + 13 + +This is pretty cool - we can actually create functions from templates of other +functions. If you can think of a way to parameterize the behavior of a +function, it becomes possible to create new functions dynamically. +You can think of currying as yet another way of creating templates - +this time you are creating a template for new functions. + +This is a tremendously powerful tool once you gain some experience +with it. Remember - everything in python is an object - even +functions are first class objects in Python so you can pass those in +as arguments as well. A practical use of this is to partially +construct new functions from 'base' functions with some basic known +behavior. + +Let's take the previous adder closure and convert it to a more general +form :: + + def arith(math_func, x): + def inner(y): + return math_func(x, y) + return inner + + def adder(x, y): + return x + y + + >>> func = arith(adder, 91) + >>> func(5) + 96 + +This technique is called currying - you're now creating new function +objects based on previous functions. The most common use for this is +to create decorators. In Python, you can define special kinds of +objects that wrap up your methods and add extra behavior. Some +decorators are builtin already like 'property', 'classmethod' and +'staticmethod'. Once you have a decorator, you can sprinkle it on to +of another function to add new behavior. + +Decorator syntax looks something like this :: + + @decorator_func_name(arg1, arg2, arg3, ...) + def some_functions(x, y, z, ...): + # Do something useful here + pass + +Suppose we have some method that requires intensive computational +resoures to run, but the results do not vary much over time. Wouldn't +it be nice if we could cache the results so that the computation +wouldn't have to run each and every time? + +Here's our class with a slow computation method :: + + import time + class Foobar(object): + def slow_compute(self, *args, **kwargs): + time.sleep(1) + return args, kwargs, 42 + +Now let's cache the value using a decorator function. Our strategy +is that for any function named X with some argument list, we want to +create a unique name and save the final computed value to that name. +We want our cached value to have a human readable name, we we want to +reuse the original function name, as well as the arguments that were +passed in the first time. + +Let's get to some code! :: + + import hashlib + def cache(func): + """ + This decorator will add a _cache_functionName_HEXDIGEST + attribute after the first invocation of an instance method to + store cached values. + """ + # Obtain the function's name + func_name = func.func_name + # Compute a unique value for the unnamed and named arguments + arghash = hashlib.sha1(str(args) + str(kwargs)).hexdigest() + cache_name = '_cache_%s_%s' % (func_name, arghash) + def inner(self, *args, **kwargs): + if hasattr(self, cache_name): + # If we have a cached value, just use it + print "Fetching cached value from : %s" % cache_name + return getattr(self, cache_name) + result = func(self, *args, **kwargs) + setattr(self, cache_name, result) + return result + return inner + +There are only two new tricks that are in this code. + +1) I'm using the hashlib module to convert the arguments to the + function into a unique single string. +2) The use of getattr, hasattr and setattr to manipulate the cached + value on the instance object. + +Now, if we want to cache the slow method, we just throw on a @cache +line above the method declaration. :: + + @cache + def slow_compute(self, *args, **kwargs): + time.sleep(1) + return args, kwargs, 42 + +Fantastic - we can reuse this cache decorator for any method we want +now. Let's suppose now that we want our cache to invalidate itself after +every 3 calls. This practical use of currying is only a slight +modification to the original caching code. :: + + import hashlib + def cache(loop_iter): + def function_closure(func): + func_name = func.func_name + def closure(self, loop_iter, *args, **kwargs): + arghash = hashlib.sha1(str(args) + str(kwargs)).hexdigest() + cache_name = '_cache_%s_%s' % (func_name, arghash) + counter_name = '_counter_%s_%s' % (func_name, arghash) + if hasattr(self, cache_name): + # If we have a cached value, just use it + print "Fetching cached value from : %s" % cache_name + loop_iter -= 1 + setattr(self, counter_name, loop_iter) + result = getattr(self, cache_name) + if loop_iter == 0: + delattr(self, counter_name) + delattr(self, cache_name) + print "Cleared cached value" + return result + result = func(self, *args, **kwargs) + setattr(self, cache_name, result) + setattr(self, counter_name, loop_iter) + return result + return closure + return function_closure + +Now we're free to use @cache for any slow method and caching will +come in for free - including automatic invalidation of the cached +value. Just use it like this :: + + @cache(10) + def slow_compute(self, *args, **kwargs): + # TODO: stuff goes here... + pass + +Review - and a taste of how we could fit all of this together +------------------------------------------------------------- + +Now - I'm going to ask you to use your imagination a litte. We've +covered quite a bit of ground really quickly. + +We can : + + * look up attributes in an object (use the __dict__ attribute). + * check if an object belongs to a particular class hierarchy (use the isinstance function). + * build functions out of other functions using currying.and even bind those functions to arbitrary names + +This is fantastic - we now have all the basic building blocks we need +to generate complex methods based on the attributes of our class. +Imagine a simplified addressbook application with a simple contact. :: + + class Contact(object): + first_name = str + last_name = str + date_of_birth = datetime.Date + +Assuming we know how to save and load to a database, we can use the +function generation techniques to automatically generate load() and +save() methods and bind them into our Contact class. We can use our +introspection techniques to determine what attributes need to be saved +to our database. We could even grow special methods onto our Contact +class so that we could iterate over all of the class attributes and +magically grow 'searchby_first_name' and 'searchby_last_name' methods. + +See how powerful this can be? We can write extremly minimal code, and +we could code generate all of our required specialized behavior for +saving, loading and searching for records in a database. Since we do +all of that programmatically - we can dramatically reduce the amount +of code that we have to write by hand and by doing so - we can redue +the chance that we introduce bugs into our system. + +We're going to do exactly that in a later chapter. Build a simple +database abstraction layer to demonstrate how to create your own +object system that will automatically know how to read and write to a +database. + +.. Footnotes + +.. [1] New style classes provide a large number of useful features that simply aren't available to old-style classes. If you end up mixing old and new style classes together, you'll usually get unexpected behaviour that will surprise you - and not in the good way. It'll surprise you in the kind of way that will keep you up late at night wondering why your code doesn't work and you'll curse the fact that both styles of classes exist at all. + +.. [2] One of Python's strengths is legibility - of your code and other code. Community standards help the legibility of code tremendously. diff --git a/chapter7.rst b/chapter7.rst new file mode 100644 index 0000000..ad27ecc --- /dev/null +++ b/chapter7.rst @@ -0,0 +1,578 @@ +Chapter 7: Modules and Packages ++++++++++++++++++++++++++++++++ + +Up until this chapter we have been looking at code at the level of the interactive console and +simple scripts. This works well for small examples, but when your program gets larger, it becomes +necessary to break programs up into smaller units. In Python, the basic building block for these +units in larger programs is the module. + +Imports For Re-Use +================== + +Breaking code up into modules helps to organize large code bases. Modules can be used to logically +separate code that belongs together, making programs easier to understand. Modules are helpful +for creating libraries that can be imported and used in different applications that share some +functionality. Jython's standard library comes with a large number of modules that can be used +in your programs right away. + +Definitions +----------- + +Here are some basic concepts that are needed to discuss imports in Jython. + +Namespace + a logical grouping of unique identifiers. + +Python Module + A file containing Python definitions and statements which in turn define a namespace. The module + name is the same as the file name with the suffix .py removed, so for example the Python file + “foo.py” defines the module “foo”. + +Python Package + A directory containing an __init__.py file and usually some Python modules which are said + to be contained in the package. The __init__.py file is executed before any contained modules + are imported. + +Java Package + Java packages organize Java classes into a namespace using nested directories. Java packages + do not require an __init__.py file. Also unlike Python packages, Java packages are explicitly + referenced in each Java file with a package directive at the top. + + +The Import Statement +-------------------- + +In Java, the import statement is strictly a compiler directive that must occur at the top of the source +file. In Jython, the import statement is an expression that can occur anywhere in the source +file, and can even be conditionally executed. + +As an example, a common idiom is to attempt to import something that may not be there in a try block, +and in the except block import a module that is known to be there. :: + + >>> try: + ... from blah import foo + ... except: + ... def foo(): + ... return "hello from backup foo" + ... + >>> foo() + 'hello from backup foo' + >>> + + + +If a module named blah had existed, the definition of foo would have been taken from there. Since no +such module existed, foo was defined in the except block, and when we called foo, the 'hello from backup foo' string was returned. + +An Example Program +------------------ + +To have a reasonable discussion about modules and packages, it helps to have a motivating example that is complex +enough for discussion, but simple enough to describe in a short space. I have chosen to show an application that +takes command line input that will then be used to search through the files in a given directory for a given bit +of text and list the files that match the input in a swing window. :: + + chapter7/ + searchdir.py + search/ + __init__.py + walker.py + scanner.py + +The example contains one package: search, which is a package because it is a directory containing the special +__init__.py file. In this case __init__.py is empty and so only serves as a marker that search is a package +. If __init__.py contained code, it would be executed before any of its containing modules could be imported. +Note that the directory chapter7 itself is not a package because it does not contain an __init__.py. There are +three modules in the example program: searchdir, search.input and search.scanner. The code for this program can be downloaded at XXX. + +searchdir.py +~~~~~~~~~~~~ :: + + import search.scanner as scanner + import sys + + help = """ + Usage: search.py directory terms... + """ + + args = sys.argv + + if args == None or len(args) < 2: + print help + exit() + + dir = args[1] + terms = args[2:] + scan = scanner.scan(dir, terms) + scan.display() + + +scanner.py +----------:: + + from search.walker import DirectoryWalker + from javax.swing import JFrame, JTable, WindowConstants + + class ScanResults(object): + def __init__(self): + self.results = [] + + def add(self, file, line): + self.results.append((file, line)) + + def display(self): + colnames = ['file', 'line'] + table = JTable(self.results, colnames) + frame = JFrame("%i Results" % len(self.results)) + frame.getContentPane().add(table) + frame.size = 400, 300 + frame.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE + frame.visible = True + + def scan(dir, terms): + results = ScanResults() + for filename in DirectoryWalker(dir): + for line in open(filename): + for term in terms: + if term in line: + results.add(filename,line) + return results + +walker.py +---------:: + + import os + + class DirectoryWalker: + # A forward iterator that traverses a directory tree. Adapted from an + # example in the eff-bot library guide: os-path-walk-example-3.py + + def __init__(self, directory): + self.stack = [directory] + self.files = [] + self.index = 0 + + def __getitem__(self, index): + while 1: + try: + file = self.files[self.index] + self.index = self.index + 1 + except IndexError: + # pop next directory from stack + self.directory = self.stack.pop() + self.files = os.listdir(self.directory) + self.index = 0 + else: + # got a filename + fullname = os.path.join(self.directory, file) + if (os.path.isdir(fullname) and not + os.path.islink(fullname)): + self.stack.append(fullname) + else: + return fullname + + +If you run searchdir.py on it's own directory like this: + +Trying out the Example Code +---------------------------:: + + $ jython scanner.py . terms + + +You will get a swing table titled “5 Results” (possibly more if .class files are matched). Let's examine the import +statements used in this program. The module searchdir contains two import statements::: + + import search.scanner as scanner + import sys + +The first imports the module “search.scannar” and renames the module “scannar”. The second imports the builtin +module “sys” and leaves the name as “sys”. The module “search.scannar” has two import statements: :: + + from search.walker import DirectoryWalker + from javax.swing import JFrame, JTable, WindowConstants + +The first imports DirectoryWalker from the “search.walker” module. Note that we had to do this even though search.walker +is in the same package as search.scanner. The last import is interesting because it imports the java classes like +JFrame from the java package javax.swing. Jython makes this sort of import look the same as other imports. This +simple example shows how you can import code from different modules and packages to modularize your programs. + +Types of import statements +========================== + +The import statement comes in a variety of forms that allow much finer control over how importing brings named values into your current module. + +Basic import Statements +----------------------- :: + + import module + from module import submodule + from . import submodule + +I will discuss each of the import statement forms in turn starting with: :: + + import module + +This most basic type of import imports a module directly. Unlike Java, this form of import binds the leftmost module +name, so If you import a nested module like: :: + + import javax.swing.JFrame + +You would need to refer to it as “javax.swing.JFrame” in your code. In Java this would have imported “JFrame”. + + +from import Statements +---------------------- :: + + from module import name + +This form of import allows you to import modules, classes or functions nested in other modules. This allows you +to achieve the result that a typical Java import gives. To get a JFrame in your Jython code you issue: :: + + from javax.swing import JFrame + +You can also use the from style of import to import all of the names in a module directly into your current +module using a *. This form of import is discouraged in the Python community, and is particularly troublesome +when importing from Java packages (in some cases it does not work, see chapter 10 for details) so you should avoid its use. It looks like this: :: + + from module import * + +Relative import Statements +-------------------------- + +A new kind of import introduced in Python 2.5 is the explicit relative import. These import statements use dots to +indicate how far back you will walk from the current nesting of modules, with one dot meaning the current module. :: + + from . import module + from .. import module + from .module import submodule + from ..module import submodule + +Even though this style of importing has just been introduced, its use is discouraged. Explicit relative imports +are a reaction to the demand for implicit relative imports. If you look at the search.scanner package, you will see the import statement: :: + + from search.walker import DirectoryWalker + +Because search.walker sits in the same package as search.scanner, the import statement could have been: :: + + from walker import DirectoryWalker + +Some programmers like to use relative imports like this so that imports will survive module restructuring, but +these relative imports can be error prone because of the possibility of name clashes. The new syntax provides an +explicit way to use relative imports, though they too are still discouraged. The import statement above would look like this: :: + + from .walker import DirectoryWalker + + +Aliasing import Statements +-------------------------- + +Any of the above imports can add an "as" clause to change import a module but give it a new name. :: + + import module as alias + from module import submodule as alias + from . import submodule as alias + + +This gives you enormous flexibility in your imports, so to go back to the Jframe example, you could issue: :: + + import javax.swing.JFrame as Foo + +And instantiate a JFrame object with a call to Foo(), something that would surprise most Java developers coming to Jython. + +Hiding Module Names +------------------- + +Typically when a module is imported, all of the names in the module are available to the importing +module. There are a couple of ways to hide these names from importing modules. Starting any name +with an underscore (_) which is the Python convention for marking names as private is the first way. +The second way to hide module names is to define a list named __all__, which should contain only +those names that you wish to have your module to expose. As an example here is the value of __all__ at the top of Jython's os module: :: + + __all__ = ["altsep", "curdir", "pardir", "sep", "pathsep", + "linesep", "defpath", "name", "path", + "SEEK_SET", "SEEK_CUR", "SEEK_END"] + +Note that you can add to __all__ inside of a module to expand the exposed names of that module. +In fact, the os module in Jython does just this to conditionally expose names based on the operating +system that Jython is running on. + + +Module Search Path, Compilation, and Loading +============================================ + +Compilation +----------- + +Despite the popular belief that Jython is an “interpreted, not compiled”, in reality all Jython code +is turned into Java bytecodes before execution. These bytecodes are not always saved to disk, but when +you see Jython execute any code, even in an eval or an exec, you can be sure that bytecodes are getting +fed to the JVM. The sole exception to this that I know of is the experimental pycimport module that I +will describe in the section on sys.meta_path below, which interprets CPython bytecodes instead of producing Java bytecodes. + + + +Module search Path and Loading +------------------------------ + +Understanding the process of module search and loading is more complicated in Jython than in either +CPython or Java because Jython can search both Java's CLASSPATH and Python's path. We'll start by looking +at Python's path and sys.path. When you issue an import, sys.path defines the path that Jython will use +to search for the name you are trying to import. The objects within the sys.path list tell Jython where +to search for modules. Most of these objects point to directories, but there are a few special items that +can be in sys.path for Jython that are not just pointers to directories. Trying to import a file that +does not reside anywhere in the sys.path (and also cannot be found in the CLASSPATH) raises an ImportError +exception. Let's fire up a command line and look at sys.path. :: + + >>> import sys + >>> sys.path + ['', '/Users/frank/jython/Lib', '__classpath__', '__pyclasspath__/', + '/Users/frank/jython/Lib/site-packages'] + + + +The first blank entry ('') tells Jython to look in the current directory for modules. The second entry +points to Jython's Lib directory that contains the core Jython modules. The third and forth entries are +special markers that we will discuss later, and the last points to the site-packages directory where new +libraries can be installed when you issue setuptools directives from Jython (see Chapter XXX for more about setuptools). + +Import Hooks +------------ + +To understand the way that Jython imports Java classes we have to understand a bit about the Python import +protocol. I won't get into every detail, for that you would want to look at PEP 302 . + +Briefly, we first try any custom importers registered on sys.meta_path. If one of them is capable of +importing the requested module, allow that importer to handle it. Next, we try each of the entries on +sys.path. For each of these, we find the first hook registered on sys.path_hooks that can handle the +path entry. If we find an import hook and it successfully imports the module, we stop. If this did not work, +we try the builtin import logic. If that also fails, an ImportError is thrown. So let's look at Jython's path_hooks. + + +sys.path_hooks +-------------- :: + + >>> import sys + >>> sys.path_hooks + [, , + ] + +Each of these path_hooks entries specifies a path_hook that will attempt to import special fies. JavaImporter, +as it's name implies, allows the dynamic loading of Java packages and classes that are specified at runtime. +For example, if you want to include a jar at runtime you can execute the following code, which will then get +picked up by the JavaImporter hook the next time that an import is attempted: :: + + >>> import sys + >>> sys.path.append("/Users/frank/lib/mysql-connector-java-5.1.6.jar") + >>> import com.mysql + *sys-package-mgr*: processing new jar, '/Users/frank/lib/mysql-connector-java-5.1.6.jar' + >>> dir(com.mysql) + ['__name__', 'jdbc'] + +sys.meta_path +------------- + +Adding entries to sys.meta_path allows you to add import behaviors that will occur before any other import +is attempted, even the default builtin importing behavior. This can be a very powerful tool, allowing you +to do all sorts of interesting things. As an example, I will talk about an experimental module that ships +with Jython 2.5. That module is pycimport. If you start up jython and issue: :: + + >>> import pycimport + + +Jython will start scanning for .pyc files in your path and if it finds one, will use the .pyc file to load you +module. .pyc files are the files that CPython produces when it compiles Python source code. So, if you after +you have imported pycimport (which adds a hook to sys.meta_path) then issue: :: + + >>> import foo + +Jython will scan your path for a file named foo.pyc, and if it finds one it will import the foo module using the +CPython bytecodes. Here the code at the bottom of pycimport.py that makes defines the MetaImporter and adds +it to sys.meta_path: :: + + class __MetaImporter(object): + def __init__(self): + self.__importers = {} + def find_module(self, fullname, path): + if __debugging__: print "MetaImporter.find_module(%s, %s)" % ( + repr(fullname), repr(path)) + for _path in sys.path: + if _path not in self.__importers: + try: + self.__importers[_path] = __Importer(_path) + except: + self.__importers[_path] = None + importer = self.__importers[_path] + if importer is not None: + loader = importer.find_module(fullname, path) + if loader is not None: + return loader + else: + return None + + sys.meta_path.insert(0, __MetaImporter()) + +The find_module method calls into other parts of pycimport and looks for .pyc files. If it finds one, +it knows how to parse and execute those files and adds the corresponding module to the runtime. Pretty cool eh? + +Java Package Scanning +===================== + +Although you can ask the Java SDK to give you a list of all of the packages known to a ClassLoader using: :: + + java.lang.ClassLoader#getPackages() + +there is no corresponding :: + + java.lang.Package#getClasses() + +This is unfortunate for Jython, because Jython users expect to be able to introspect they code they +use in powerful ways. For example, users expect to be able to call dir() on Java objects and packages +to see what names they contain: :: + + >>> import java.util.zip + >>> dir(java.util.zip) + ['Adler32', 'CRC32', 'CheckedInputStream', 'CheckedOutputStream', 'Checksum', 'DataFormatException', 'Deflater', 'DeflaterOutputStream', 'GZIPInputStream', 'GZIPOutputStream', 'Inflater', 'InflaterInputStream', 'ZipEntry', 'ZipException', 'ZipFile', 'ZipInputStream', 'ZipOutputStream', '__name__'] + >>> dir(java.util.zip.ZipInputStream) + ['__class__', '__delattr__', '__doc__', '__eq__', '__getattribute__', '__hash__', '__init__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'available', 'class', 'close', 'closeEntry', 'equals', 'getClass', 'getNextEntry', 'hashCode', 'mark', 'markSupported', 'nextEntry', 'notify', 'notifyAll', 'read', 'reset', 'skip', 'toString', 'wait'] + + +To make this sort of introspection possible in the face of merged namespaces requires some major effort the +first time that Jython is started (and when jars or classes are added to Jython's path at runtime). If +you have ever run a new install of Jython before, you will recognize the evidence of this system at work: :: + + *sys-package-mgr*: processing new jar, '/Users/frank/jython/jython.jar' + *sys-package-mgr*: processing new jar, '/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/classes.jar' + *sys-package-mgr*: processing new jar, '/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/ui.jar' + *sys-package-mgr*: processing new jar, '/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/laf.jar' + ... + *sys-package-mgr*: processing new jar, '/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/ext/sunjce_provider.jar' + *sys-package-mgr*: processing new jar, '/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/ext/sunpkcs11.jar' + +This is Jython scanning all of the jar files that it can find to build an internal representation of the +package and classes available on your JVM. This has the unfortunate side effect of making the first startup +on a new Jython installation painfully slow. + +How Jython Finds the Jars and Classes to scan +--------------------------------------------- + +There are two properties that Jython uses to find jars and classes. These settings can be given to Jython +using commandline settings or the registry (see Chapter XXX). The two properties are: :: + + python.packages.paths + python.packagse.directories + +These properties are comma separated lists of further registry entries that actually contain the values +the scanner will use to build its listing. You probably should not change these properties. The properties +that get pointed to by these properties are more interesting. The two that potentially make sense to manipulate are: :: + + java.class.path + java.ext.dirs + + +For the java.class.path property, entries are separated as the classpath is separated on the operating +system you are on (that is, ";" on Windows and ":" on most other systems). Each of these paths are checked +for a .jar or .zip and if they have these suffixes they will be scanned. + +For the java.ext.dirs property, entries are separated in the same manner as java.class.path, but these +entries represent directories. These directories are searched for any files that end with .jar or .zip, +and if any are found they are scanned. + +To control the jars that are scanned, you need to set the values for these properties. There are a number +of ways to set these property values, see Chapter XXX for more. + +If you only use full class imports, you can skip the package scanning altogether. Set the system property +python.cachedir.skip to true or(again) pass in your own postProperties to turn it off. + +Python Modules and Packages vs. Java Packages +============================================= + +The basic semantics of importing Python modules and packages versus the semantics of importing Java packages +into Jython differ in some important respects that need to be kept carefully in mind. + +sys.path +-------- + +When Jython tries to import a module, it will look in its sys.path in the manner described in the +previous section until it finds one. If the module it finds represents a Python module or package, +this import will display a “winner take all” semantic. That is, the first python module or package that +gets imported blocks any other module or package that might subsequently get found on any lookups. This +means that if you have a module foo that contains only a name bar early in the sys.path, and then another +module also called foo that only contains a name baz, then executing “import foo” will only give you +foo.bar and not foo.baz. + +This differs from the case when Jython is importing Java packages. If you have a Java package org.foo +containing bar, and a Java package org.foo containing baz later in the path, executing “import org.foo” +will merge the two namespaces so that you will get both org.foo.bar and org.foo.baz. + +Just as important to keep in mind, if there is a Python module or package of a particular name in +your path that conflicts with a Java package in your path this will also have a winner take all effect. +If the Java package is first in the path, then that name will be bound to the merged Java packages. +If the Python module or package wins, no further searching will take place, so the Java packages +with the clashing names will never be found. + + +Naming Python Modules and Packages +---------------------------------- + +Developers coming from Java will often make the mistake of modeling their Jython package structure the +same way that they model Java packages. Do not do this. The reverse url convention of Java is a great, +I would even say a brilliant convention for Java. It works very well indeed in the world of Java where +these namespaces are merged. In the Python world however, where modules and packages display the winner +take all semantic, this is a disastrous way to organize your code. + +If you adopt this style for Python, say you are coming from “acme.com” so you would set up a package +structure like “com.acme”. If you try to use a library from your vendor xyz that is set up as “com.xyz”, +then the first of these on your path will take the “com” namespace, and you will not be able to see +the other set of packages. + + +Proper Python Naming +-------------------- + +The Python convention is to keep namespaces as shallow as you can, and make your top level namespace reasonably +unique, whether it be a module or a package. In the case of acme and company xyz above, you might start you package +structures with “acme” and “xyz” if you wanted to have these entire codebases under one namespace (not necessarily +the right way to go – better to organize by product instead of by organization, as a general rule). + +Note: There are at least two sets of names that are particularly bad choices for naming modules or packages +in Jython. The first is any top level domain like org, com, net, us, name. The second is any of the domains +that Java the language has reserved for its top level namespaces: java, javax. + +Java Import Example +------------------- + +We'll start with a Java class which is on the CLASSPATH when Jython is started: :: + + package com.foo; + public class HelloWorld { + public void hello() { + System.out.println("Hello World!"); + } + public void hello(String name) { + System.out.printf("Hello %s!", name); + } + } + +Here we manipulate that class from the Jython interactive interpreter: :: + + >>> from com.foo import HelloWorld + >>> h = HelloWorld() + >>> h.hello() + Hello World! + >>> h.hello("frank") + Hello frank! + +It's important to note that, because the HelloWorld program is located on the Java CLASSPATH, it did not go +through the sys.path process we talked about before. In this case the Java class gets loaded directly by the +ClassLoader. Discussions of Java ClassLoaders are beyond the scope of this book. To read more about ClassLoader +see (citation? Perhaps point to the Java Language Specification section) + +Conclusion +========== + +In this chapter we have learned how to divide code up into modules to for the purpose of organization and re-use. +We have learned how to write modules and packages, and how the Jython system interacts with Java classes and packages. +This ends Part I. We have now covered the basics of the Jython language and are now ready to learn how to use Jython. + + diff --git a/chapter8.rst b/chapter8.rst new file mode 100644 index 0000000..e086545 --- /dev/null +++ b/chapter8.rst @@ -0,0 +1,2 @@ +Chapter 8: Scripting with Jython +================================= \ No newline at end of file diff --git a/chapter9.rst b/chapter9.rst new file mode 100644 index 0000000..9ed498a --- /dev/null +++ b/chapter9.rst @@ -0,0 +1,2 @@ +Chapter 9: Input and Output +============================ \ No newline at end of file diff --git a/index.rst b/index.rst new file mode 100644 index 0000000..9740cd1 --- /dev/null +++ b/index.rst @@ -0,0 +1,115 @@ +.. Jython Book documentation master file, created by + sphinx-quickstart on Wed Jun 10 21:44:59 2009. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +The Definitive Guide to Jython with Django +========================================== + +:Authors: + Josh Juneau, + Jim Baker, + Victor Ng, + Leo Soto, + Frank Wierzbicki + +:Version: 0.1 of 06/13/2009 + +This book is presented in open source and licensed through Creative Commons 3.0. +You are free to copy, distribute, transmit, and/or adapt the work. This license +is based upon the following conditions: + +Attribution: + +You must attribute the work in the manner specified by the author +or licensor (but not in any way that suggests that they endorse +you or your use of the work). + +Share Alike: + +If you alter, transform, or build upon this work, you may distribute the +resulting work only under the same, similar or a compatible license. + +Any of the above conditions can be waived if you get permission from +the copyright holder. + +In no way are any of the following rights affected by the license: + + - Your fair desling or fair use rights + - The author's moral rights + - Rights other persons may have either in the work itself or in how + the work is used, such as publicity or privacy rights + +Notice: For any reuse or distribution, you must make clear to the others +the license terms of this work. The best way to do this is with a direct +link to this page: http://creativecommons.org/licenses/by-sa/3.0/ + +To be printed by Apress Fall 2009 -- + + + ISBN10: 1-4302-2527-0 + + ISBN13: 978-1-4302-2527-0 + +Source code will be available at: http://www.apress.com + +.. toctree:: + :maxdepth: 2 + +Part I: Jython Basics: Learning the Language +============================================== + +.. toctree:: + :maxdepth: 2 + + chapter1.rst + chapter2.rst + chapter3.rst + chapter5.rst + chapter6.rst + chapter7.rst + +Part II: Using the Language +=========================== + +.. toctree:: + :maxdepth: 2 + + chapter8.rst + chapter9.rst + chapter10.rst + chapter11.rst + chapter12.rst + +Part III: Developing Applications with Jython +============================================= + +.. toctree:: + :maxdepth: 2 + + chapter13.rst + chapter14.rst + chapter15.rst + chapter16.rst + chapter17.rst + chapter18.rst + +Part IV: Strategy and Technique +================================ + +.. toctree:: + :maxdepth: 2 + + chapter19.rst + chapter20.rst + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + + + diff --git a/toc.txt b/toc.txt index 4364621..877150e 100644 --- a/toc.txt +++ b/toc.txt @@ -1,3 +1,12 @@ +The Definitive Guide to Jython (with Django) +Authors: Josh Juneau + Frank Wierzbicki + Jim Baker + Leo Soto + Victor Ng + +Page Count Estimate: 419 - 473 + Table of Contents Copyright and Licensing @@ -8,39 +17,111 @@ Acknowledgements Part I: Jython Basics: Learning the Language - Chapter 1: Language and Syntax (Josh - 4/20/2009) + Chapter 1: Language and Syntax (Josh - FD COMPLETE) (pages: 20-23) + + The Difference Between Jython and Python + + Installing and Configuring Jython - Identifiers and Reserved Words + Identifiers and Declaring Variables + + Reserved Words Coding Structure - Statements - - Expresions - - Documenting Code - - Operators and Symbols - - Decorators - - Chapter 2: Data Types and Referencing (Josh - 05/2009) - - Jython Data Types + Operators + + Expressions + + Statements + + If-Else Statement + + pass Statement + + def Statement + + print Statement + + try-except-finally + + assert Statement + + raise Statement + + import Statement + + Other Python Statements + + Iteration + + While Loop + + For Loop + + continue Statement + + break Statement + + Documenting Code + + Python Help + + Summary + + Chapter 2: Data Types and Referencing (Josh - FD COMPLETE) (pages: 22-25) + + Python Data Types + + Strings and String Methods + + String Formatting + + Ranges + + Lists, Dictionaries, and Tuples + + Lists + + Traversing and Searching Lists + + List Comprehensions + + Dictionaries + + Sets + + Tuples + + Jython Specific Collections + + Files + + Iterators + + Referencing and Copies Garbage Collection - Referencing and Copies - - Chapter 3: Flow and Expressions (Josh) + Summary - Defining Expressions + Chapter 3: Operators, Expressions, and Program Flow (Josh - FD COMPLETE) (pages: 10-12) - Conditionals with Expressions + Types of Expressions + + Mathematical Operations + + Boolean Expressions + + Conversions + + Program Flow - Looping and Iteration + Example Code + + Summary - Chapter 4: Defining Functions and Using Built-Ins (Jim) + Chapter 4: Defining Functions and Using Built-Ins (Jim - 07/2009) (pages: 25-28) Function Syntax and Basics @@ -54,21 +135,31 @@ Part I: Jython Basics: Learning the Language Coroutines - Function Decorators - - Using Built-In Functions + Decorators - Chapter 5: Exception Handling in Jython (Josh - 06/2009) + Using Built-In Functions + + Using __future__ - What is Exception Handling + Chapter 5: Exception Handling and Debugging (Josh - FD COMPLETE) (pages: 12-15) - General Practice + Exception Handling Syntax and Differences with Java + + Catching Exceptions + + Raising Exeptions - Defining Exceptions + Defining Your Own Exceptions + + Issuing Warnings - Assertions + Assertions and Debugging + + Context Managers + + Summary - Chapter 6: Object Oriented Jython (Victor Ng - 04/20/2009) + Chapter 6: Object Oriented Jython (Victor Ng - FD COMPLETE) (pages: 12-15) Using Classes @@ -78,7 +169,7 @@ Part I: Jython Basics: Learning the Language Closures and Passing Objects - Chapter 7: Modules, Packages, and Reuse (Frank Wierzbicki - 04/20/2009) + Chapter 7: Modules, Packages, and Reuse (Frank Wierzbicki - FD COMPLETE) (pages: 12-15) Using imports for Reuse @@ -90,33 +181,37 @@ Part I: Jython Basics: Learning the Language Part II: Using the Language - Chapter 8: Scripting with Jython (Frank Wierzbicki) + Chapter 8: Scripting with Jython (Frank Wierzbicki - 06/2009) (pages: 20-23) Learning the Command Line Using Modules and Java Libraries - Chapter 9: Input and Output (Josh - 04/20/2009) + Chapter 9: Input and Output (Josh - 06/2009) (pages: 15-18) Input from Keyboard - File Basics - - Standard I/O + File Basics + + Serialization - Chapter 10: Java and Jython Integration (Josh - 05/2009) + Chapter 10: Java and Jython Integration (Josh - 06/2009) (pages: 27-30) - Using Jython within Java Applications + Using Jython within Java Applications + + Object Factories - JSR-223 + JSR-223 + + PythonInterpreter Using Java within Jython Applications - Java Package Scanning (Frank Wierzbicki) + Java Package Scanning (Frank Wierzbicki - 06/2009) Exploring the Java Environment with iPython (this doesn't work yet....) - Chapter 11: Using Jython in an IDE (Josh & Leo Soto - 06/2009) + Chapter 11: Using Jython in an IDE (Josh & Leo Soto - 07/2009) (pages: 25-28) Eclipse @@ -132,21 +227,35 @@ Part II: Using the Language WebSphere - Chapter 12: Databases and Jython (Josh & Jim - 05/2009) + Chapter 12: Databases and Jython (Josh & Jim - 06/2009) (pages: 20-23) + + Making Use of the dbm Module + + Using JDBC in Jython - zxJDBC Basics + zxJDBC - Using Python's DB API via JDBC + + Connections - Cursors + Cursors - Querying + Querying - DML + DML - DDL + DDL + + Object Relational Mapping + + SQLAlchemy + + Hibernate + + SQLObject (? Not yet working?) Part III: Developing Applications with Jython - Chapter 13: Simple Web Applications (Josh - 06/2009) + Chapter 13: Simple Web Applications (Josh - 07/2009) (pages: 20-23) Servlets @@ -154,7 +263,7 @@ Part III: Developing Applications with Jython WSGI and modjy - Chapter 14: Web Applications with Django (Leo Soto, Victor Ng) + Chapter 14: Web Applications with Django (Leo Soto, Victor Ng - 07/2009) (pages: 32-35) The General Picture @@ -162,11 +271,11 @@ Part III: Developing Applications with Jython Java-specific topics - Chapter 15: Web Applications with Pylons (Victor Ng) + Chapter 15: Web Applications with Pylons (Victor Ng - 07/2009) (pages: 23-25) ??? - Chapter 16: Web Services and SOA (Jim) + Chapter 16: Web Services and SOA (Jim - 07/2009) (pages: 23-25) JAX-WS @@ -174,13 +283,13 @@ Part III: Developing Applications with Jython JAXB and Legacy JAX-RPC Support - Chapter 17: GUI Applications (Frank Wierzbicki) + Chapter 17: GUI Applications (Frank Wierzbicki - 07/2009) (pages: 23-25) Writing Jython Swing Using Jython with JavaFX - Chapter 18: Deployment Targets (Josh & Jim) + Chapter 18: Deployment Targets (Josh & Jim - 07/2009) (pages: 25-28) Application Servers (Josh) @@ -188,7 +297,9 @@ Part III: Developing Applications with Jython Glassfish - JBoss + JBoss + + Google App Engine Mobile (Jim) @@ -198,13 +309,13 @@ Part III: Developing Applications with Jython Part IV: Strategy and Technique - Chapter 19: Testing and Continuous Integration (Leo Soto) + Chapter 19: Testing and Continuous Integration (Leo Soto - 08/2009) (pages: 23-25) Grinder Push-To-Test TestMaker - Chapter 20: Concurrency and Parallelism (Jim) + Chapter 20: Concurrency and Parallelism (Jim - 08/2009) (pages: 18-20) High-Level Concurrency