Languages - DevSource
DevSource: Microsoft Developer Resource DevSource Home Sponsored by Microsoft Home Add Ons Architecture Languages Techniques Using VS Forums
Home arrow Languages arrow Page 4 - A First Look at IronPython: Where Python meets .NET
A First Look at IronPython: Where Python meets .NET
By Jeff Cogswell

Rate This Article: Add This Article To:

A First Look at IronPython: Where Python meets .NET - ' Reaching '
( Page 4 of 6 )

.NET's Full Potential">

Time for a breather: Remember, IronPython is a .NET program. But just because it's an interpreter written in .NET doesn't imply that the language it's interpretting permits the creation of custom .NET types. However, IronPython does allow exactly that.

When you create a class in IronPython and derive it from System.Object, IronPython calls into the .NET framework and dynamically creates a new CLR class. IronPython assigns it a name and places it in its own IronPython.NetTypes.System class. (Of course, a second breather is in order: I'm working from a Beta, and the people creating IronPython could well change the namespace on us.) I believe Poe wrote something called a Dream within a Dream. In this case, we're doing .NET within .NET. Or something like that.

ADVERTISEMENT

Reaching .NET's Full Potential

Now that I'm convinced (and hopefully you are too) that you can use IronPython to create classes in .NET and to make use of the .NET classes, let's see what that means. How about a quick example from the System.Forms namespace. This is the namespace that lets you create programs that have Windows GUIs. I'll skip the rambling and hop to it:

import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import *

class MyForm(Form):
    def __init__(this):
        Form.__init__(this)
        this.Text = "Class Names"
        this.Click += this.ClickHandler
    def ClickHandler(this, f, a):
        t = this.GetType()
        MessageBox.Show(t.FullName)

f = MyForm()
Application.Run(f)

Did I mention that Python is one of my favorite language? Now I remember why. This code creates a window and handles the Click event for the window, popping up a message box that shows the name of the class (using the same GetType code I just talked about).

Notice how I make use of the various .NET classes. This is almost like regular Python, but with a twist: The classes I'm using in this case all live in the System.Windows.Forms.dll assembly. Before I can use the classes in that assembly, I need to tell IronPython about the assembly. Since this assembly is one of the standard assemblies found in the framework's directory on the hard drive, all I need is the name of the assembly without the .dll extension:

clr.AddReference("System.Windows.Forms")

To use the AddReference method, I had to import the clr module, which handles all the dirty work of pulling in .NET assemblies.

Now IronPython knows about the assembly and I can import anything I want from it. I want to import everything (because I want to keep the example simple):

from System.Windows.Forms import *

This line gives me the classes I need, in my class Form, Application, and MessageBox.

Next, I declare my own class for the form. I derive the class from the Form class, and provide an constructor and a click event handler. The constructor sets the Text property to a string name, and then specifies that my own ClickHandler method will be called in response to the form's Click event. Can't get much easier than that. Notice the syntax:

this.Click += this.ClickHandler

My ClickHandler method grabs the type of the class and displays the full name of the type in a message box. Easy!

Builtin Python Types and .NET

Some of the builtin Python types are either implemented as or make use of the .NET framework types. String is one such type. If you call import System, your strings in Python can make use of the members of the .NET String class. Start by importing the System namespace to pick up the String class. Then try creating a string in the IronPython console:

>>> import System
>>> a = 'hello there everybody'

If you type dir(a), you can find out the methods available:

>>> dir(a)
['Capitalize', 'Center', 'Clone', 'Compare', 'CompareOrdinal', 'CompareTo',
 'Concat', 'Contains', 'Copy', 'CopyTo', 'Count', 'Decode', 'Empty', 'Encode', 'EndsWith',
 'Equals', 'EqualsRetBool', 'ExpandTabs', 'Finalize', 'Find', 'Format', 'GetEnumerator',
 'GetHashCode', 'GetLength', 'GetSlice', 'GetType', 'GetTypeCode', 'Index', 'IndexOf',
 'IndexOfAny', 'Insert', 'Intern', 'IsAlnum', 'IsAlpha', 'IsDecimal', 'IsDigit',
 'IsInterned', 'IsLower', 'IsNormalized', 'IsNullOrEmpty', 'IsNumeric', 'IsSpace',
 'IsTitle', 'IsUpper', 'Join', 'LJust', 'LStrip', 'LastIndexOf', 'LastIndexOfAny',
 'Length', 'Lower', 'Make', 'MemberwiseClone', 'Modulus', 'Multiply', 'Normalize',
 'NotEquals', 'PadLeft', 'PadRight', 'RFind', 'RIndex', 'RJust', 'RSplit', 'RStrip',
 'Remove', 'Replace', 'Split', 'SplitLines', 'StartsWith', 'Strip', 'Substring',
 'SwapCase', 'Title', 'ToBoolean', 'ToByte', 'ToChar', 'ToCharArray', 'ToDateTime',
 'ToDecimal', 'ToDouble', 'ToInt16', 'ToInt32', 'ToInt64', 'ToLower', 'ToLowerInvariant',
 'ToSByte', 'ToSingle', 'ToString', 'ToType', 'ToUInt16', 'ToUInt32', 'ToUInt64',
 'ToUpper', 'ToUpperInvariant', 'Translate', 'Trim', 'TrimEnd', 'TrimStart',
 'Upper', 'ZFill', '__class__', '__cmp__', '__contains__', '__eq__', '__getitem__',
 '__getslice__', '__init__', '__len__', '__mod__', '__module__', '__mul__',
 '__ne__', '__new__', '__repr__', 'capitalize', 'center', 'count', 'decode', 'encode',
 'endswith', 'expandtabs', 'find', 'index', 'isalnum', 'isalpha', 'isdecimal',
 'isdigit', 'islower', 'isnumeric', 'isspace', 'istitle', 'isupper', 'join', 'ljust',
 'lower', 'lstrip', 'replace', 'rfind', 'rindex', 'rjust', 'rsplit', 'rstrip', 'split',
 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper',
 'zfill']
>>>

This list includes both the Python methods (the ones starting with lowercase letters or underscores) and the .NET String class methods (the ones starting with uppercase letters). Here's a call to a .NET String class method:

>>> a.Capitalize()
'Hello there everybody'

And here's a call to a Python method:

>>> a.title()
'Hello There Everybody'

If you call GetType(), you can find out the .NET type of the variable:

>>> a.GetType()
System.String

Note that although the GetType() method returns a .NET object of class Type, the IronPython console called the Type object's ToString() method to get the string version of the name.)

Numbers also make use of the .NET classes, as the following code demonstrates:

>>> n = 100
>>> n.GetType()
System.Int32
>>> x = 3.1415
>>> x.GetType()
System.Double

The native Python types can often be used in calls into the .NET framework. For example, the Form class's Controls property includes a function called AddRange. Take a look at this example to see how I use a native Python list as a parameter for AddRange, shown in bold:

import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import *

class MyForm(Form):
    def __init__(self):
        Form.__init__(self)
        
        Button1 = Button()
        Button1.Top = 10
        Button1.Left = 10
        Button1.Text = "One"
        
        Button2 = Button()
        Button2.Top = 50
        Button2.Left = 10
        Button2.Text = "Two"

        ctrls = [Button1, Button2]
        self.Controls.AddRange(ctrls)
        
f = MyForm()
Application.Run(f)

This code runs fine. If you look at the online help for the AddRange function, you'll see that the function takes an array of Control objects. The Python list type (such as the ctrls variable in this code) is implemented as an internal type unique to IronPython called IronPython.Runtime.List. This is quite different from an array of controls.

How are you able to pass a Python list to the AddRange function, then? Simple: IronPython has an extremely powerful mechanism that checks what the function is expecting (in this case, the AddRange function is expecting an array of Control objects) and then converts the list into the expected type. That saves you an awful lot of work. (And, I imagine the creators of IronPython have spent a lot of work adding this feature, so thank them for it!)

However, if you do need to create a .NET array, IronPython does include a syntax that lets you create one. Here's a line that you can replace in the ctrls declaration in the preceding code:

ctrls = System.Array[System.Windows.Forms.Control]( (Button1, Button2) )

This creates an actual array of Control objects (as opposed to a Python list) that you can instead pass into the AddRange function, if you prefer. Notice the syntax; you specify System.Array, followed by the type the array holds in brackets. Then I initialize the array with my two items, Button1 and Button2.

For completeness, I should give a quick explanation here of how this array is, in fact, a different entity from the Python list. The CLR includes native support for arrays. This support is tied closely to the .NET framework class called Array. In native .NET languages (specifically C# and VB.NET), you don't directly create instances of Array. Instead, they use their own native constructs that let you create CLR arrays. In VB.NET, for example, you can create an array of Control objects as follows:

Dim c(2) As Control

This is an array of size 2. If you check the type using c.GetType().FullName, you'll see the name System.Windows.Forms.Control[]. This is a CLR array, and is derived from the .NET class Array; this is the type of array required by the AddRange method I demonstrated earlier, something completely different from the Python list type.

Unfortunately, as of the Beta version I'm using, the array conversion isn't perfect. I wanted to find out how IronPython would handle calling a .NET Framework method that requires an array of characters. The function I use here is the Split function found in the .NET String class. (This has a slight caveat, however; Python's own string type includes its own split function that you can use to get around the shortcoming, in this particular case.)

Here's the code I tried:

>>> a1 = [ "." , "," ]
>>> "a.b,c".Split(a1, 2)
Traceback (most recent call last):
  File , line 0, in <stdin>##39
  File , line 0, in Split##40
TypeError: bad args for method

Fortunately, in addition to the presence of the native Python split function, I found a little trick to create a character array by making use of the String class. Check this out:

>>> a1 = ".,".ToCharArray()
>>> "a.b,c".Split(a1, 2)
System.String[]('a', 'b,c')

The Split method worked, and I got back an array of two strings.

Python All the Way

If you're really into Python, you're likely aware of such features as dynamically obtaining member values of objects through the use of the getattr function. This function is still present in IronPython, and it even works on member data in the .NET classes. Take a look at this:

>>> import System
>>> import clr
>>> clr.AddReference("System.Windows.Forms")
>>> from System.Windows.Forms import *
>>>
>>> btn = Button()
>>> btn.Height = 30
>>> hasattr(btn, 'Height')
True
>>> getattr(btn, 'Height')
30
>>> setattr(btn, 'Height', 50)
>>> btn.Height
50

Of course, the delattr function, although available, fails on the attributes inherited from the .NET classes. It wouldn't make sense, after all, to delete an attribute from a pre-existing class without causing serious damage to the universe and its inhabitants.



 
 
>>> More Languages Articles          >>> More By Jeff Cogswell
 



Microsoft's Future: A Chat With Their CTO, Barry Briggs

Play Video >

All Videos >

Julia explores the Robotics Studio!

Read now >

Messages to Bill Gates!

Read now >

View Now
DevSource RSS FEEDS
XML Want an easy way to keep up with breaking tech news? And the Get DevSource headlines delivered to your desktop with RSS.