In many respects, most of what you work with in IronPython is some sort of object. When working with most languages, you have a vision of data types as being just that — data only. You assign a number to a variable and see that the variable has some kind of presence on the stack as simply a number. However, in IronPython, a variable holding a number is a lot more than simply a number — the variable is actually an object with callable methods and properties that tell you more about the variable. For example, try assigning a number to a variable (say i in this case), and then call i.__sizeof__(). You’ll discover that you get back the size of the variable in memory. With this bit of information in mind, it’s time to discover what other objects lurk beneath the surface of the supposed simplicity that IronPython provides. The following sections tell you more about the existing objects in IronPython.
Discovering IronPython Objects
IronPython objects are everywhere, just waiting for you to discover them. Let’s take a closer look at the number example in the introduction to this section. Go ahead and open a copy of the IronPython interpreter, type int = 5, and press Enter. Now type dir(int) and press Enter. That’s right, you can treat int as an object in IronPython. You’ll see the information shown in Figure 5-1. All these methods and properties apply to int because int is an object, not just a variable as you might suppose when using other languages.
In fact, you may have noticed in previous chapters that it appears that everything in IronPython is an object. It’s true; everything you use in IronPython is an object, so always remember to use the dir() function to display the things you can do with the objects you use. The following sections describe a few of the more common IronPython objects and how to work with them. Don’t worry — you’ll see a lot more objects before you complete the book. These sections are simply here to whet your appetite for more objects later.
Working with String Objects
Strings are one of the first objects many people use. You write that first “Hello World” application and marvel when the words appear on screen. In fact, strings are the mainstay of many applications. Without strings you can’t provide prompts to the user or ask for input. Sure, you may not do any heavy lifting with strings, but every application out there requires strings to work properly. The following sections discuss the IronPython string object in more detail.
Performing Standard Tasks with Strings
You’ve already seen a few of the things you can do with strings in previous chapters. This chapter takes a little more organized look at the methods and properties associated with strings. The following list provides an overview of the most common tasks you can perform.
- center(int width[, Char fillchar]): Centers the string within the space defined by width. The default is to use spaces to pad the left and right side of the string to center it. However, you can specify another character by specifying the optional fillchar. For example, if you want to center a string named MyString in a 40-character area using the * as a fill character, you’d type MyString.center(40, ‘*‘).
- int count(str ssub, [int start[, int end]]): Counts the number of instances of a substring, ssub, within a string. The substring can be one or more letters that you want to find within the string. You may optionally provide a starting point, start, and an ending point, end, for the count. For example, if you want to count the number of ls found in MyString, you’d type MyString.count(‘l‘, 0, len(MyString)).
- decode([object encoding[, str errors]]): Decodes an encoded string. Even though encoding is optional, you must provide a value in order to decode the string. You can find a list of standard encodings at http://www.python.org/doc/2.5.2/lib/ standard-encodings.html. The errors argument defines how decode() treats errors, with a default value of strict. You can find a list of error strings at http://www.python .org/doc/2.5.2/lib/codec-base-classes.html. For example, you might have a Unixto- Unix Encode (uuencode) string named EncodeString that you want to decode into plain text. To convert the string, you’d type EncodeString.decode(‘uu_codec‘).
- encode([object encoding[, str errors]]): Encodes a string to another format. You have the same options as when decoding a string (see the decode() entry in this list). For example, you might want to encode a string using uuencode. To perform this task, you’d type EncodeString = MyString.encode(‘uu_codec‘). After the call, EncodeString would contain the uuencoded string.
- endswith(object suffix[, int start[, int end]]): Determines whether the string ends with a particular letter or substring, suffix. You may optionally provide a starting point, start, and ending point, end, in the string. When using an end value, endswith() checks the designated endpoint, rather than the actual end of the string. For example, if you want to determine whether there’s a l at position 4 (an end point of 3 since the string count begins with 0), you’d type MyString.endswith(‘l‘, 0, 3).
- expandtabs([int tabsize]): Expands the tabs within a string using spaces. You may optionally provide the number of spaces to use for each tab using tabsize. For example, if you want to expand the tabs in a string to four spaces, you’d type MyString.expandtabs(4).
- find(str sub[, int start[, int end]]) or find(str sub, object start, object end): Locates the substring, sub, within the string and outputs an integer value defining the first occurrence of the substring. You can optionally add a starting, start, and ending, end, value to change the location that the method searches within the string (the default is to search the entire string). In this case, the starting and ending value need not be an integer value, but can be an object that defines the starting and ending point instead. For example, if you want to search for the first occurrence of l within a string, you’d type MyString.find(‘l‘). This method returns a value of –1 when the string doesn’t contain the search value.
- format(*args[, *kwargs]): Formats the string using a template (see the “Formatting String Output” section for details). The args argument contains positional information and kwargs contains a keyword argument.
- index(str sub[, int start[, int end]]) or index(str sub, object start, object end): Performs precisely the same task as find(). However, instead of returning –1 when a value isn’t found, index()raises a ValueError instead.
- isalnum(), isalpha(), isdecimal(), isdigit(), islower(), isnumeric(), isspace(), istitle(), isunicode(), and isupper(): Detects the state of the string and returns True when the specified condition exists. For example, isalnum() returns True when a string contains some combination of letters and numbers. The string must contain at least one letter, but need not necessarily contain any numbers. The isalpha() method, on the other hand, only returns True when the string contains only letters, and isnumeric() returns True when the string contains only numbers.
- join(list sequence) or join(object sequence): Appends a string to a list or a sequence. This method joins each member of the sequence to the source string. For example, if the source string contains ABC and you join 123 to it, you obtain ‘1ABC2ABC3’ as output. To obtain this output, you’d type MyString.join(‘123‘). As an alternative, you could type MyString .join([‘1‘, ‘2‘, ‘3‘]) to obtain the same output using a list.
- ljust(int width[, Char fillchar]): Left-justifies the string to a length specified by width by padding the left end with the specified number of characters. You can optionally specify a fill character other than the default of a space by providing fillchar. For example, if you want to left-justify a string to 40 spaces and fill the spaces with an *, you’d type MyString.ljust(40, ‘*‘).
- lower(): Returns the lowercase version of the string.
- lstrip([str chars]): Removes white space from the beginning of a string by default. You may also provide a chars value as input. In this case, the method removes that character from the beginning of the string when it exists. For example, to remove the leading spaces from a string, you’d type MyString.lstrip().
- partition(str sep): Divides the string into three parts based on the value of sep. The first part contains the piece of the string before sep, the second part contains sep, and the third part contains the piece of the string after sep. For example, to split a string at the first space, you’d type MyString.partition(‘ ‘).
- replace(object old, object new[, int maxsplit]): Replaces the occurrences of old with new in the target string. You may provide an optional number of replacements to make by defining maxsplit. For example, if you want to replace the spaces in a string with the newline escape code, you’d type MyString.replace(‘ ‘, ‘/n‘).
- rfind(): Performs the same task as find(), except that this method searches from the right end of the string, rather than the left. See the find() entry in the list for details.
- rindex(): Performs the same task as index(), except that this method indexes from the right end of the string, rather than the left. See the index() entry in the list for details.
- rjust(): Performs the same task as ljust(), except that this method right-justifies the string, rather than left-justifying it. See the ljust() entry in the list for details.
- rpartition(): Performs the same task as partition(), except that this method partitions the right side of the string, rather than the left side. See the partition() entry in the list for details.
- rsplit(): Performs the same task as split(), except that this method begins at the right side of the string, rather than the left. See the split() entry in the list for details.
- rstrip(): Performs the same task as lstrip(), except that this method begins at the right side of the string, rather than the left. See the lstrip entry in the list for details.
- split(str sep[, int maxsplit]): Divides the string into a list using sep as the point of division. You may provide an optional number of replacements to make by defining maxsplit. For example, if you want to divide a string into individual words, you’d type MyString.split(‘ ‘).
- splitlines([bool keepends]): Breaks a string apart by lines. The output is a list of lines within the string. Normally, the output doesn’t include the newline character. However, you can keep the newline character by setting keepends to True. For example, to break a string part into individual lines, you’d type MyString.splitlines().
- startswith(): Performs the same task as endswith(), except that this method works with the beginning of the string, rather than the end of the string. See the endswith() entry in the list for details.
- strip(): Performs the same task as lstrip(), except that this method removes spaces (or other characters) from both ends of the string, rather than just the left. See the lstrip() entry in the list for details.
- swapcase(): Sets all of the lowercase characters to uppercase and all of the uppercase characters to lowercase. For example, if you begin with ‘Hello World’, you’d receive ‘hELLO wORLD’ as output if you typed MyString.swapcase().
- title(): Returns a title-cased version of a string where the first letter of each word is capitalized and all other letters are lowercase. For example, if you begin with ‘helLo wORLD’, you’d receive ‘Hello World’ as output if you typed MyString.title().
- translate(str table, [str deletechars]) or translate(dict table): Replaces the characters in a string with the equivalents specified by table. The table argument is 256 characters long and you can create it using the MakeTrans() function found in the string module. (Remember to use from string import maketrans to make accessing the function easy.) For example, if you want to replace the first 16 lowercase letters with hexadecimal equivalents, you’d type MyString.translate(maketrans(‘abcdefghijklmnop‘, ‘0123456789ABCDEF‘)). Using this code as a starting point, ‘Hello World’ becomes ‘H4BBE WErB3’.
- upper(): Returns the uppercase version of the string.
- zfill(int width): Returns a string that has zeros placed on the left side to pad the string to the length specified by width. For example, if you typed MyString.zfill(40), you’d receive a string that is 40 characters long with as many zeros on the left side as required to produce the required length.
Formatting String Output
String formatting can become quite complex in Python and IronPython. However, if you start with the basics, you’ll find that you can usually figure out the complex elements without too much trouble. A basic format string contains one or more fields. A field is simply some text that appears within curly braces that you replace with a value. In fact, if you’ve worked with any .NET language, you’ve already used fields. Here’s a simple sentence that contains a field.
[code]
MyString = ‘Hello {0}‘
[/code]
Of course, you won’t want to print this string directly onscreen. Instead, you’ll want to replace {0} with some other value. In order to do this, you can use the format() method as shown here.
[code]
MyString.format(‘George’)
[/code]
The interpreter replaces the {0} with the name George. Consequently, you see ‘Hello George’ as output from these two lines of code. You have a number of options when working with replaceable variables in a string. The following list shows just a few of the options:
- MyString = ‘Hello {0}‘: Provides a simple replacement from a list of input arguments. The input arguments must appear in the order required in the string.
- MyString = ‘Hello {0[name]}‘: Provides a replacement from a dictionary. The corresponding format() method input is MyString.format({‘name‘:‘George‘}). Of course, you can provide additional field information if your dictionary contains arrays for each of the elements. In this case, you specify the element you want to use like this: MyString = ‘Hello {0[names][0]}‘. The resulting format() method input is MyString.format({‘names‘:[‘George‘, ‘Amy‘]}). The advantage of this method is that the input arguments can appear in any order.
- MyString = ‘The paths are {0.path}‘: Provides a means of accessing an attribute within an object. The corresponding format() method input is MyString.format(sys). If you want to access a specific path, simply include the element specifier like this: MyString = ‘The path is {0.path[0]}‘. The advantage of this technique is that you can access properties within objects without first placing the property value in a variable.
A formatting string can contain as many variables as needed to provide complete information to the user. For example, you can add a second argument like this.
[code]
MyString = ‘Hello {0} from {1}‘
[/code]
When you call the format() method, you now need to add some more information. The format() method input for this string might look like this.
[code]
MyString.format(‘George’, ‘London’)
[/code]
In many cases, you need to provide input that doesn’t translate into a string. For example, you might need to provide integer input for some strings. The interpreter won’t automatically perform a conversion in this case so you need to perform the task manually. The conversion symbol is the exclamation mark (!) and the most common conversion is string (s). You can also call the repr() conversion function by using r in place of s. Here’s an example of a conversion:
[code]
MyString = ‘{0!s} + {1!s} = {2!s}‘
MyString.format(1, 2, 1+2)
[/code]
In this case, you get an output of ‘1 + 2 = 3’. Notice that this example places the math directly in the format string. You could place the output of a function there as well.
So far, the examples haven’t done much formatting — they have simply replaced field values with information found in other sources. The format operator is the colon (:) and you can combine it with the conversion operator if you want. To see how this works, think about displaying the previous example in hexadecimal format. In that case, your code might look like this:
[code]
MyString = ‘{0:X} + {1:X} = {2:X}‘
MyString.format(10, 20, 10+20)
[/code]
The output from this code is in hexadecimal format — you’d see ‘A + 14 = 1E’. Of course, you might want all the values to take up the same space. In this case, you can tell the interpreter to add some space to the output using the following string:
[code]
MyString = ‘{0:0=4X} + {1:0=4X} = {2:0=4X}‘
[/code]
This string outputs numbers with zeros as padding. The padding appears after any sign information. In addition, each of the entries is four digits long. Consequently, the output now looks like this: ‘000A + 0014 = 001E’. The formatting has specific entries, all of which are options. It looks like this:
[code]
[[fill]align][sign][#][0][width][.precision][type]
[/code]
Fill characters determine what appears as part of the padding the interpreter uses when you specify a width, and the field value doesn’t fill the entire space. The default padding is the space, but you can specify any character other than the closing brace, which would end the formatting definition. When you specify a fill character, such as the 0 used in the previous example, you must also use one of the alignment characters found in Table 5-1.
The use of signs in the output comes next. For example, you can choose to have all positive numbers begin with a plus sign (+) so there’s no confusion about their positive value. Table 5-2 shows the sign formatting options you can use.
The pound sign (#), which is called by a host of names, such as octothorp and number sign, tells the interpreter to add a letter after numeric values to show their base — b for binary, o for octal, or x for hexadecimal (decimal values never have the letter added). For example, if you change the previous formatting string to include the # like this:
[code]
MyString = ‘{0:0=#6X} + {1:0=#6X} = {2:0=#6X}‘
[/code]
the output changes to include the correct base designation. You’ll see ‘0X000A + 0X0014 = 0X001E’ as the output.
The width and precision entries come next. If you precede the width value with a 0, then the interpreter will pad the numeric values with zeros. The precision entry tells the interpreter how many decimal places to use for the output.
The final formatting you can request is the output type. In this case, you must decide in advance what kind of value that the field will accept — integers use different type designations than floating point and decimal types. Table 5-3 shows the types you can use for integer input, while Table 5-4 shows the types for floating point and decimal.
Working with Numeric Objects
Numeric objects include a number of methods that make working with them easier. It’s important to realize that some of the methods that apply to strings also apply to numbers. For example, you can access the __format__() method when working with a number. In addition, you can easily turn a number into a string using the __str__() method. Some string-oriented methods actually revolve around numbers, such as the format typing described in Tables 5-3 and 5-4. In short, don’t think that numbers are limited to number-specific methods. The following sections consider the kinds of things you can do with numbers.
Considering Numeric Type Differences
IronPython generally splits numbers between integers and floats (decimals are included with floats). The two numeric presentations are handled differently by the interpreter and even have different representations at the hardware level, so it’s no surprise that there are differences you must consider when working with a number. However, as when working with strings, you can cause numbers to cross the divide. An integer can appear as a float using the __float__() method. Likewise, you can use __int__() or __trunc__() methods.
Numeric types have some similarities. For example, both integers and floats support the __abs__() method, which returns the absolute value of the number. In some cases, you have to look for the similarities. For example, floats provide a hex() method that performs the same task as __hex__() does for integers.
Integers have a few interesting methods that floats can’t support because of their memory representation. For example, you can use the __and__() method to “and” the value of the variable with another integer (where “anding” 5 and 4 would result in an output of 4, and “anding” 5 and 7 would result in an output of 5). In fact, here’s a list of methods that appear for integers that don’t appear for floats (you’ll notice that most of them have something to do with bit-level manipulation):
- __and__
- __cmp__
- __hex__
- __index__
- __invert__
- __lshift__
- __oct__
- __or__
- __rand__
- __rlshift__
- __ror__
- __rrshift__
- __rshift__
- __rxor__
- __xor__
- denominator
- numerator
Floats, likewise, have a few methods that apply only to them. Most of these methods deal with performing comparisons. For example, you need a special method to determine that two floats are equal to each other (the __eq__() method). The following list shows these methods:
- __eq__
- __ge__
- __getformat__
- __gt__
- __le__
- __lt__
- __ne__
- __setformat__
- as_integer_ratio
- fromhex
- hex
- is_integer
Performing Standard Tasks with Numbers
Now that you have a better idea of how numbers compare, it’s time to look at some specific methods. This section describes some of the more common methods used with numbers. You’ll see a good many of the other methods demonstrated as the book progresses.
- as_integer_ratio() (float only): Displays a tuple showing the integer ratio used to produce the float. For example, if the float value is 5.5, then this method outputs (11L, 2L). To use this method type MyFloat.as_integer_ratio().
- conjugate(): Returns the conjugate of a complex number or the identity value of a real number. You can read more about conjugation at http://en.wikipedia.org/wiki/ Complex_conjugate.
- fromhex(str input) (float only): Outputs the decimal equivalent of a hexadecimal number input as a string. For example, to determine the decimal value of the hexadecimal number A5, you’d type MyFloat.fromhex(‘A5‘). In this case, the output is 165.
- hex() (float) or __hex__() (integer): Outputs the hexadecimal value of the float or integer. For example, to find the hexadecimal value of MyInt, you’d type MyInt.__hex__().
- imag: Contains the imaginary part of a complex number. To use this attribute you’d type MyComplex.imag.
- is_integer() (float only): Determines whether the content of a floating point number is an integer in value. When using this method, a value of 5.0 would return True, while a value of 5.1 would return False. To use this method, you’d type MyFloat.is_integer().
- real: Contains the real part of a complex number. To use this attribute you’d type MyComplex.real.
Working with Boolean Objects
Boolean objects provide access to truth values about other objects and their relationships. Many objects have Boolean methods built in. For example, when working with a float, you can use the equality methods __eq__(), __ge__(), __gt__(), __le__(), __lt__(), and __ne__() to determine relationships between values. In fact, Boolean objects also include these equality methods and you might find them useful at times for comparing the truth value of two Boolean objects.
For the most part, Boolean objects have little in the way of other methods that you need when creating a typical IronPython application. For example, you could use the __long__() method to convert the Boolean to long integer, but that really wouldn’t accomplish much for most developers.