HelloIronPythonDemo1.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IronPython" Version="3.4.1" />
</ItemGroup>
<ItemGroup>
<None Update="customers.py">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Consider the following array of tuples in Python :
customers.py
customers = [
('Jenna', 42, 165),
('Thor', 40, 174),
('Christopher', 18, 170),
('Liz', 16, 168),
]
Python code is very compact and you declare variables without specifying type such as in C#, Python uses a simple way of creating variables and while C# got support in C# 7 in 2017, Python has had support for tuples since its early days. In the Python 1.4 version, we find it documented here: https://docs.python.org/release/1.4/tut/node37.html#SECTION00630000000000000000.
Bear in mind, this is way back in 1996, C# was over 20 years later with its tuple support. If you install IronPython, you get a terminal where you can enter Python code (plus more functionality with .NET) such as shown below, where tuples are created and tuples may be composed or 'packed' and also 'unpacked', which is called deconstructed in .NET tuples. To execute code to retrieve this array of tuples, first create a ScriptEngine and then create a ScriptScope, which we will use to retrieve the Python-declared variable customers. We create a ScriptSource, where we use the ScriptEngine to load up either a string or a file. A dynamic variable will be used to get the array of tuples and we can loop through this array with a foreach loop and output its content.
Program.cs
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
using static System.Console;
IronPythonDemo1.OutputSomeExternallyLoadedTuples();
public class IronPythonDemo1
{
public static void OutputSomeExternallyLoadedTuples()
{
var engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
//ScriptSource source = engine.CreateScriptSourceFromString(tupleStatement);
ScriptSource source = engine.CreateScriptSourceFromFile("customers.py");
source.Execute(scope);
dynamic customers = scope.GetVariable("customers");
foreach (var customer in customers)
{
Console.WriteLine($"(Name = {StringExtensions.FixedLength(customer[0], 20)}, Age = {StringExtensions.FixedLength(customer[1].ToString(), 8)}, Height={StringExtensions.FixedLength(customer[2].ToString(), 8)})");
}
}
}
Documentation for named tuples are available here: https://docs.python.org/3/library/collections.html#collections.namedtuple
Here is sample coding showing script that although it is more verbose, shows more readability of which field is which for a named tuple. In an ordinary tuple, you use indexes to retrieve the nth field (0-based). But with named tuples, you use a field name instead.
from collections import namedtuple
Customer = namedtuple('Customer', ['Name', 'Age', 'Height'])
customers2 = [
Customer(Name = 'Jenna', Age = 42, Height = 165),
Customer(Name = 'Thor', Age = 38, Height = 174),
Customer(Name = 'Christopher', Age = 42, Height = 170),
Customer(Name = 'Liz', Age = 42, Height = 168),
]
for cust in customers2:
print(f"{cust.Name} with a height of {cust.Height}(cm)")
This outputs:
Jenna with a height of 165(cm)
Thor with a height of 174(cm)
Christopher with a height of 170(cm)
Liz with a height of 168(cm)
When your tuple gets many fields, having this readability should reduce bugs. Also, if you add more fields to your tuple, you do not have to fix up indexes in your script. So code is a bit more verbose, but it is also more open for change and readable.
The FixedLength extension method is a simple method to output text to a fixed width.
public static class StringExtensions
{
public static string FixedLength(this string input, int length, char paddingchar = ' ')
{
if (string.IsNullOrWhiteSpace(input))
{
return input;
}
if (input.Length > length)
return input.Substring(0, length);
else
return input.PadRight(length, paddingchar);
}
}