Python’s namedtuple… for .Net!

Share this:

One of the things I have seen people applaud about Python is its “namedtuple” class. If you could describe this in terms of .Net’s Tuple (and <T1, T2, etc>) it would be to basically say that it’s the same as Tuple, but if instead of “Item1” on Tuple you got “Name” or “Age” or whatever other meaningful name for the property (instead of ‘Item1’). And, of course, if you weren’t limited to at most 8 items on the Tuple object.

That being said, let’s have a look at how namedtuple works in practice:

Let’s go over what you’re seeing in detail as it relates to what you may, or may not know, about languages and .Net in particular:

The call to ‘namedtuple’ creates a new class in the type system called ‘person’ with properties ‘name’, ‘age’, and ‘gender’ on it. The return value from this call is assigned to ‘PersonClass’ variable which effectively becomes an alias for the new class within the type system. When you want to use this newly created class, you reference it with ‘PersonClass’ – a usual practice in this area is to assign the return value to the same name as the class you created. In other words ‘person’ instead of ‘PersonClass’. I did this here to illustrate the difference.

After you have an alias to this new type, you get to use it just like you would any other type. I create a new instance of this type assigned to variable ‘p’ by simply using the constructor that is created for this type. The default constructor for types created by ‘namedtuple’ is one that takes a value for each of the properties you gave the new type. So in this example, it’s a constructor that takes values for ‘name’, ‘age’, and ‘gender’. If you try to create an instance without specifying them all, you’ll get an error.
After creating my new instance with its values, you get to reference those values by the names you assigned to the properties when you created the new class in the type system with the call to ‘namedtuple’.

Indeed it is pretty slick. So… let’s see what we can do with .Net shall we?

My first thought here was that Dynamic objects in general somewhat “solve” this issue. What they don’t provide, however, is the “definition” and the ability to add properties on the fly. At least, not at first glance.

I knew about ExpandoObject from a few years back, and decided to see what it could do for me. If you haven’t seen this before, it’s pretty mind blowing when you first see what you can do with it. Here’s a quick example:

1: dynamic coolStuff = new System.Dynamic.ExpandoObject();

2: coolStuff.name = "Brandon";

3: coolStuff.age = 32;

4: coolStuff.gender = 'M';

5:

6: Console.WriteLine("name: " + coolStuff.name);

And the output:

name: Brandon

“Whoa, what?” Yeah – the beauty of .Net’s DLR (Dynamic Language Runtime). It’s pretty awesome. ExpandoObject is a special type that says “if you assign something to a property and I don’t have it already, I’ll just create the property for it on myself and let you get at it later”. It’s a property bag on steroids. The biggest key to it, though, is that you have to cast it to a ‘dynamic’ object in order to really use it.

So this is pretty close to namedtuple, but not quite there. We want the properties there for usage at creation time, and we’d also like the other niceties that Python provides like _make, _replace, and _fields to do some special things.

Unfortunately, ExpandoObject can’t be inherited from (sealed type) but there is another type that the DLR provides that lets you do these cool dynamic typing things; DynamicObject.

This type is one that hooks in to what the DLR does at runtime and, more importantly, lets you react to them. In the case of namedtuple, what you’re reacting to are the Get/Set calls used in property fetching and assignment.

But let’s roll back a second. At face value what *really* are we playing with here? It’s a Dictionary. More specifically, Dictionary<string, object=””>. It’s a collection of values that you can look up by a string. The only difference is you get to do it in good old-fashioned OO ways, by using property calls. That being said, if you were to create a class that used DynamicObject to react to type system calls, what would you back it with? A Dictionary.

6:/// Provides the implementation for operations that get member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject" /> class can override this method to specify dynamic behavior for operations such as getting a value for a property.

7:/// </summary>

8:///Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.

9:///The result of the get operation. For example, if the method is called for a property, you can assign the property value to <paramref name="result" />.

10:/// <returns>

11:/// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a run-time exception is thrown.)

20:/// Provides the implementation for operations that set member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject" /> class can override this method to specify dynamic behavior for operations such as setting a value for a property.

21:/// </summary>

22:///Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.

23:///The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test".

24:/// <returns>

25:/// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.)

And boom. By inheriting from DynamicObject we’re letting .Net know that we intend on intercepting the DLR’s calls while it’s resolving the code at run time. Specifically the Get and Set Member calls.

And how do we use this gem?

dynamic nt = new NamedTuple();
nt.name = "Brandon";

I know, it looks a lot like ExpandoObject. But the important thing to know here is that I can’t change how ExpandoObject works, is constructed, other functionality on it, nothing. But I can change my new NamedTuple. When the ‘name’ property is assigned, the code that’s executed is the code in TrySetMember above. You can see that all that does is store the value in my underlying dictionary. Similarly when it’s retrieved, TryGetMember is executed and the value’s pulled back out.
Interestingly enough, if you look at the documentation of Python’s namedtuple, you’ll see it provides methods to convert a namedtuple in to a dictionary – it’s now obvious to see why.

So let’s look at providing the exact functionality of Python’s variant in to ours. The first thing you see is that there’s a nice constructor to specify the property names. Easy enough:

/// <summary>/// Makes the tuple by populating the fields with the given contents/// in the order they are specified and the order the fields were added/// to this instance/// </summary>/// <param name="contents">The contents with which to populate the fields</param>publicvoid Make(IEnumerable<object> contents)
{
for (int i = 0; i < contents.Count(); i++)
{
_contained[_contained.Keys.ElementAt(i)] = contents.ElementAt(i);
}
}

/// <summary>/// Replaces the specified field with the given value/// </summary>/// <param name="field">The field of which to replace the value</param>/// <param name="value">The new value.</param>/// A new instance of NamedTuple with the 's value replaced with /// ArgumentException">Field not found in tuple;fieldpublic NamedTuple Replace(string field, objectvalue)
{
if (!_contained.ContainsKey(field))
{
thrownew ArgumentException("Field not found in tuple", "field");
}
var newDict = new Dictionary<string, object>(_contained);
newDict[field] = value;
returnnew NamedTuple(newDict);
}

The important note about _replace is that you get a new instance back. Same as Python.

The only couple of things I didn’t implement here are a few of the more funky constructors that Python’s has – I didn’t see them as too valuable past what we have here, but if you find you would like them or make changes to this class to add them don’t hesitate to contact me so I can update the NuGet package with your changes!