IronPython: Passing Arguments for a Call

After getting a CLR type, we can play it like a python type. We can get the class/static methods using the dot operator ("attribute reference") on the type, or create an instance of it and then get the instance methods. In Python’s word, these methods are callable objects, and we can make "calls" on them using the call operator ("()"). I will write about the method call in the next couple of posts, this one is about passing arguments for calls in IronPython.

Python function definition allows several different parameter styles:

  • parameter which accepts excess positional arguments,
  • parameter with default value,
  • parameter which accepts excess keyword arguments.

Here are some examples showing the common parameter style, and each of 3 styles listed above.

def f1(x, y):   print x, y
def f2(x, *y):  print x, len(y)
def f3(x, y=5): print x, y
def f4(x, **y): print x, y

When making calls (on these functions), Python allows many different argument styles too. The following type (in C#) defines some methods, each with interesting parameter list.

// sample.cs
public class ArgumentList
  public void M1(int x, int y) { Console.WriteLine("{0} {1}", x, y); }
  public void M2(int x, params int[] y) { Console.WriteLine("{0} {1}", x, y.Length); }
  public void M3(int x, [DefaultParameterValue(5)] int y) { Console.WriteLine("{0} {1}", x, y); }
}

In terms of the parameter style, we will see that M1 is equivalent as pure python function f1, same for M2/f2, M3/f3 (I was unable to find similar parameter kind in C# for f4). When calling these .NET methods, IronPython supports all argument styles as Python does.

M1 is the simplest in the parameter definition, but we can call it with different argument lists. Each call below prints "1, 2", same as if the python function "f1" is called.

import clr
clr.AddReference("sample")

import ArgumentList
o = ArgumentList()
f1 = o.M1

f1(1, 2)                 # positional arguments
f1(1, y = 2)             # positional & keyword arguments
f1(y = 2, x = 1)         # keyword arguments
f1(*(1, 2))              # unpack the sequence as positional args
f1(1, *(2,))             # positional & unpack
f1(**{'x': 1, 'y': 2})   # unpack the dictionary as keyword args

C# parameter array (parameter with "params", or exactly ParamArrayAttribute) is treated like "*y" in f2. The user are allowed to pass 0 or more arguments to the parameter "y".

f2 = o.M2
f2(1)              # print: 1 0
f2(1, 2)           # 1 1
f2(*(1, 2, 3))     # 1 2
f2(1, 2, 3, 4, 5)  # 1 4

The C# language does not have built-in support for the default value argument (IL does, with the .param directive inside the method body); even if the parameter is decorated with System.Runtime.InteropServices.DefaultParameterValueAttribute, C# calls on such methods still require argument passed in for that parameter. However IronPython (like Python) honors such default value parameters.

f3 = o.M3
f2(1)            # print: 1 5
f2(x = 1)        # 1 5
f2(1, 2)         # 1 2

Another parameter attribute (OptionalAttribute) received similar treatment from IronPython: you do not need pass in values either; IronPython assign it a special value (the .NET run-time default value for most of primitive types, System.Type.Missing.Value for "object" type, null otherwise). Such supports are useful when using IronPython to call into the COM libraries (such as Office automation). The user can avoid typing in every arguments.

 public void M6([Optional] int x, [Optional] object y) {
   Console.WriteLine("{0} {1}", x, y);
}
 public void M7([Optional] string x, [Optional] Type y) {
   Console.WriteLine("{0} {1}", x == null, y == null);
}

o.M6()           # print: 0 System.Type.Missing
o.M6(10)         # 10 System.Type.Missing
o.M6(20, "hi")   # 20 hi
o.M7()           # True True