Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Introducing IronPython : Page 3

IronPython is an easy-to-learn yet surprisingly powerful language for .NET development. Find out how it differs from C# and Visual Basic while still leveraging your existing .NET knowledge.

.NET Interoperability
One of the challenges in designing IronPython lies in attempting to satisfy the two separate audiences the language is intended to serve. On the one hand, it needs to work as much like the standard C-based Python implementation as possible. On the other, it has to have high fidelity interop with the .NET Framework. Sometimes, the needs of those two priorities clash. For example, what should be the result of this code?

s = 'hello, world!' s.ToUpper()

In Python, the string type does not have a ToUpper method, so this code should throw an exception. However, in .NET, calling the ToUpper function on this string should return "HELLO, WORLD!" These are obviously contradictory requirements.

IronPython handles this by being a good Python implementation by default, but allowing developer to indicate they want high fidelity .NET interop. In Python, code is organized into modules and namespaces, similar to .NET. IronPython includes a special module called clr. By importing that module, developers indicate they want to use .NET interop. Here's an interactive IronPython session that demonstrates the use of import clr:

>>> s = 'hello, world' >>> s.ToUpper() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'str' object has no attribute 'ToUpper' >>> import clr >>> s.ToUpper() 'HELLO, WORLD'

Before the call to import clr, you can't call the ToUpper method on a string. String objects in Python don't have a ToUpper method so IronPython hides that method, among others. However, after the call to import clr, all of the String object's methods—both Python and native .NET—are available.

You also use the clr module to load external assemblies. For example, to use .NET XML processing classes such as XmlReader and XmlDocument, you need to add a reference to System.Xml.dll. In C#, you add that reference declaratively at compile time. But because there is no compile time in IronPython, you need to add the reference imperatively via code. Subsequently, you can import classes into your current scope and use them just like any other Python object.

import clr clr.AddReference('System.Xml') from System.Xml import XmlDocument xml = XmlDocument() xml.Load('http://devhawk.net/rss.aspx')

It should be pretty obvious what this code does, but there are a few things I want to note. First off, there is no new statement in Python; you create type instances by calling the type like a function. Second, in C#, importing classes from namespaces (via the using statement) is optional, designed to save typing. In Python, it's mandatory. In other words, writing the code this way wouldn't work:

import clr clr.AddReference('System.Xml') # the following line doesn't work xml = System.Xml.XmlDocument()

The from...import form of the statement imports the short name. To use the fully namespace-scoped name, write import System.Xml.XmlDocument. Import also supports renaming types to avoid collisions using the syntax import <real name> as <new name>.

You can make nearly the entire .NET Framework available to IronPython simply by adding references to the relevant assemblies and importing the needed types. Besides being able to create instances of .NET types, you can also consume .NET events, implement .NET interfaces, and inherit from .NET types. For example, here's some code from the IronPython tutorial that uses Windows Forms.

# the winforms module in the tutorial directory import winforms from System.Windows.Forms import * from System.Drawing import * def click(*args): print args f = Form() f.Text = "My First Interactive Application" f.Click += click f.Show()

However, one thing you can't do from IronPython is adorn your code with attributes. Any part of the .NET framework that requires attributes, such as WCF contracts or XML serialization, won't work with IronPython. Those libraries depend on custom attributes that act like custom metadata to extend the existing static type. Python objects don't have a static type, so there's nothing to attach the custom attribute to.

The other thing about .NET interop is that it's essentially one way. It's easy for Python to call into .NET classes written in statically typed languages. However, there's no easy way (yet) for statically typed languages to call into dynamically typed objects. Static languages depend on the compile-time type metadata to dispatch method calls, but that metadata just doesn't exist in dynamically typed languages such as Python. Obviously, given the multi-language nature of the CLR, enabling statically typed languages to call into dynamically typed code is a scenario we would like to enable in the future.

Embedding IronPython
Python has significant traction in the industry as an easily-embeddable language. Likewise, IronPython can be easily embedded inside your .NET applications to provide a scripting or macro development experience.

IronPython 1.x handled embedding entirely thru the PythonEngine type. Here's an example that accesses the PythonEngine from the interactive console.

>>> import clr >>> clr.AddReference("IronPython.dll") >>> from IronPython.Hosting import PythonEngine >>> pe = PythonEngine() >>> pe.Evaluate('2+2') 4

This example doesn't give the hosted Python environment any hooks into the host, so the code it can execute is fairly limited. However, PythonEngine provides a Globals collection that you can use to expose your application object model to the Python environment.

In IronPython 2.0, the hosting code gets a bit more complicated:

import clr clr.AddReference("IronPython.dll") from IronPython.Hosting import PythonEngine pe = PythonEngine.CurrentEngine scope = pe.CreateScope() source = pe.CreateScriptSourceFromString('2+2') result = source.Execute(scope)

The Dynamic Language Runtime (DLR) is one reason the IronPython 2.0 code is more complicated. The DLR is an extension to the CLR that provides common capabilities needed for dynamic languages. But it also provides a common hosting API that allows any application hosting the DLR to support any language that targets the DLR. In other words, if your application uses the DLR-hosting API, it can support not only IronPython, but also IronRuby and Managed JavaScript—or any third-party language that gets built on the DLR (see the hosting API)

Between significant whitespace and dynamic typing, there's no question Python is a wholly different development experience from C# or VB. I'm a recent convert to Python from C#, so I know exactly how strange it can feel. But once you get past that feeling of unfamiliarity, you'll start to see just how productive Python can be.

Harry Pierson is the IronPython program manager. A ten year Microsoft veteran, Harry has also spent a significant amount of his career focused on architecture and services. He writes a blog on technology, programming, architecture, and occasionally, ice hockey .
Comment and Contribute








