Dynamic Programming with Python and C#

Previously, I was expressing how excited I was when I discovered Python, C#, and Visual Studio integration. I wanted to save a couple examples regarding dynamic code for a follow up article... and here it is! (And yes... there is code you can copy and paste or download).

EDIT: Wait! Before you head to far, you might want to check out this more recent article on Python and C#!

What does it mean to be dynamic? As with most things, wikipedia provides a great start. Essentially, much of the work done for type checking and signatures is performed at runtime for a dynamic language. This could mean that you can write code that calls a non-existent method and you wont get any compilation errors.

However, once execution hits that line of code, you might get an exception thrown. This Stack Overflow post's top answer does a great job of explaining it as well, so I'd recommend checking that out if you need a bit more clarification. So we have statically bound and dynamic languages. Great stuff!

Which is Dynamic?

So does that mean Python is dynamic? What about C#?

Well Python is certainly dynamic. The code is interpreted and functions and types are verified at run time. You won't know about type exceptions or missing method exceptions until you go to execute the code. For what it's worth, this isn't to be confused with a loosely typed language. Ol' faithful Stack Overflow has another great answer about this. The type of the variable is determined at runtime, but the variable type doesn't magically change. If you set a variable to be an integer, it will be an integer. If you set it immediately after to be a string, it will be a string. (Dynamic, but strongly typed!)

As for C#, in C# 4 the dynamic keyword was introduced. By using the dynamic keyword, you can essentially get similar behavior to Python. If you declare a variable of type dynamic, it will take on the type of whatever you assign to it. If I assign a string value to my dynamic variable, it will be a string. I can't perform operations like pre/post increment (++) on the variable when it's been assigned a string value without getting an exception. If I assign an integer value immediately after having assigned a string value, my variable will take on the integer type and my numeric operators become available.

Where does this get us with C# and Python working together then?

Example 1: A Simple Class

After trying to get some functions to execute between C# and Python, I thought I needed to take it to the next level. I know I can declare classes in Python, but how does that look when I want to access it from C#? Am I limited to only calling functions from Python with no concept of classes?

The answer to the last question is no. Most definitely not. You can do some pretty awesome things with IronPython. In this example, I wanted to show how I can instantiate an instance of a class defined within a Python script from C#. This script doesn't have to be created in code (you can use an external file), so if you need more clarification on this check out my last Python/C# posting, but I chose to do it this way to have all the code in one spot. I figured it might be easier to show for an example.

We'll be defining a class in Python called "MyClass" (I know, I'm not very creative, am I?). It's going to have a single method on it called "go" that will take one input parameter and print it to the console. It's also going to return the input string so that we can consume it in C# and use it to validate that things are actually going as planned. Here's the code:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Scripting.Hosting;

using IronPython.Hosting;

namespace DynamicScript
{
	internal class Program
	{
		private static void Main(string[] args)
		{
			Console.WriteLine("Enter the text you would like the script to print!");
			var input = Console.ReadLine();

			var script =
			"class MyClass:rn" +
			"    def __init__(self):rn" +
			"        passrn" +
			"    def go(self, input):rn" +
			"        print('From dynamic python: ' + input)rn" +
			"        return input";

			try
			{
				var engine = Python.CreateEngine();
				var scope = engine.CreateScope();
				var ops = engine.Operations;

				engine.Execute(script, scope);
				var pythonType = scope.GetVariable("MyClass");
				dynamic instance = ops.CreateInstance(pythonType);
				var value = instance.go(input);

				if (!input.Equals(value))
				{
				throw new InvalidOperationException("Odd... The return value wasn't the same as what we input!");
				}
			}
			catch (Exception ex)
			{
				Console.WriteLine("Oops! There was an exception while running the script: " + ex.Message);
			}

			Console.WriteLine("Press enter to exit...");
			Console.ReadLine();
		}
	}
}

Not too bad, right? The first block of code just takes some user input. It's what we're going to have our Python script output to the console. The next chunk of code is our Python script declaration. As I said, this script can be loaded from an external file and doesn't necessarily have to exist entirely within our C# code files.

Within our try block, we're going to setup our Python engine and "execute" our script. From there, we can ask Python for the type definition of "MyClass" and then ask the engine to create a new instance of it. Here's where the magic happens though! How can we declare our variable type in C# if Python actually has the variable declaration? Well, we don't have to worry about it! If we make it the dynamic type, then our variable will take on whatever type is assigned to it. In this case, it will be of type "MyClass".

Afterwards, I use the return value from calling "go" so that we can verify the variable we passed in is the same as what we got back out... and it definitely is! Our C# string was passed into a Python function on a custom Python class and spat back out to C# just as it went in. How cool is that?

Some food for thought:

  • What happens if we change the C# code to call "go1" instead of "go"? Do we expect it to work? If it's not supposed to work, will it fail at compile time or runtime?
  • Notice how our Python method "go" doesn't have any type parameters specified for the argument "input"? How and why does all of this work then?!

Example 2: Dynamically Adding Properties

I was pretty excited after getting the first example working. This meant I'd be able to create my own types in Python and then leverage them directly in C#. Pretty fancy stuff. I didn't want to stop there though. The dynamic keyword is still new to me, and so is integrating Python and C#. What more could I do?

Well, I remembered something from my earlier Python days about dynamically modifying types at run-time. To give you an example, in C# if I declare a class with method X and property Y, instances of this class are always going to have method X and property Y. In Python, I have the ability to dynamically add a property to my class. This means that if I create a Python class that has method X but is missing property Y, at runtime I can go right ahead and add property Y.

That's some pretty powerful stuff right there. Now I don't know of any situations off the top of my head where this would be really beneficial, but the fact that it's doable had me really interested.

So if Python lets me modify methods and properties available to instances of my type at runtime, how does C# handle this? Does the dynamic keyword support this kind of stuff?

You bet. Here's the code for my sample application:

using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.CSharp.RuntimeBinder;

using IronPython.Hosting;

namespace DynamicClass
{
	internal class Program
	{
		private static void Main(string[] args)
		{
			Console.WriteLine(
				"Press enter to read the value of 'MyProperty' " +
				"from a Python object before we actually add " +
				"the dynamic property.");
			Console.ReadLine();

			// this script was taken from this blog post:
			// http://znasibov.info/blog/html/2010/03/10/python-classes-dynamic-properties.html
			var script =
			"class Properties(object):rn" +
			"    def add_property(self, name, value):rn" +
			"        # create local fget and fset functionsrn" +
			"        fget = lambda self: self._get_property(name)rn" +
			"        fset = lambda self, value: self._set_property(name, value)rn" +
			"rn" +
			"        # add property to selfrn" +
			"        setattr(self.__class__, name, property(fget, fset))rn" +
			"        # add corresponding local variablern" +
			"        setattr(self, '_' + name, value)rn" +
			"rn" +
			"    def _set_property(self, name, value):rn" +
			"        setattr(self, '_' + name, value)rn" +
			"rn" +
			"    def _get_property(self, name):rn" +
			"        return getattr(self, '_' + name)rn";

			try
			{
				var engine = Python.CreateEngine();
				var scope = engine.CreateScope();
				var ops = engine.Operations;

				engine.Execute(script, scope);
				var pythonType = scope.GetVariable("Properties");
				dynamic instance = ops.CreateInstance(pythonType);

				try
				{
					Console.WriteLine(instance.MyProperty);
					throw new InvalidOperationException(
						"This class doesn't have the property we "+
						"want, so this should be impossible!");
				}
				catch (RuntimeBinderException)
				{
					Console.WriteLine("We got the exception as expected!");
				}

				Console.WriteLine();
				Console.WriteLine(
					"Press enter to add the property 'MyProperty' "+
					"to our Python object and then try to read " +
					"the value.");
				Console.ReadLine();

				instance.add_property(
					"MyProperty",
					"Expected value of MyProperty!");
				Console.WriteLine(instance.MyProperty);
			}
			catch (Exception ex)
			{
				Console.WriteLine(
					"Oops! There was an exception while " +
					"running the script: " + ex.Message);
			}

			Console.WriteLine("Press enter to exit...");
			Console.ReadLine();
		}
	}
}

Analyzing The Example

Let's start by comparing this to the first example, because some parts of the code are similar. We start off my telling  the user what's going to happen and wait for them to press enter. Nothing special here. Next, we declare our Python script (again, you can have this as an external file) which I pulled form this blog. It was one of the first hits when searching for dynamically adding properties to classes in Python, and despite having limited Python knowledge, it worked exactly as I had hoped. So thank you, Zaur Nasibov.

Inside our try block, we have the Python engine creation just like our first example. We execute our script right after too and create an instance of our type defined in Python. Again, this is all just like the first example so far. At this point, we have a reference in C# to a type declared in Python called "Properties". I then try to print to the console the value stored inside my instances property called "MyProperty". If you were paying attention to what's written in the code, you'll notice we don't have a property called "MyProperty"! Doh! Obviously that's going to throw an exception, so I show that in the code as well.

So where does that leave us then? Well, let's add the property "MyProperty" ourselves! Once we add it, we should be able to ask our C# instance for the value of "MyProperty". And... voila!

Some food for thought:

  • When we added our property in Python, we never specified a type. What would happen if we tried to increment "MyProperty" after we added it? What would happen if we tried to assign an integer value of 4 to "MyProperty"?
  • When might it be useful to have types in C# dynamically get new methods or properties?

Summary on Dynamic Integration

With this post, we're still just scratching the surface of what's doable when integrating Python and C#. Historically, these languages have been viewed as very different where C# is statically bound and Python is a dynamic language. However, it's pretty clear with a bit of IronPython magic that we can quite easily marry the two languages together. Using the "dynamic" keyword within C# really lets us get away with a lot!

Source code for these projects is available at the following locations:

Python, Visual Studio, and C#... So. Sweet.

IronPython: A Quick WinForms Introduction

Pythonnet - A Simple Union of .NET Core and Python You'll Love

An error has occurred. This application may no longer respond until reloaded. Reload x