IronPython
Written by Mike James   
Thursday, 15 July 2010
Article Index
IronPython
Duck Typing
.NET interop

No typing

The key thing about dynamic languages is that they treat variable type in as flexible way as possible. You can generally assign anything to anything and hope it makes sense. Python always attempt to work out what type of data a variable is storing and match the operation to this.

This is often called "duck typing" from the old saying "if it looks like a duck, quacks like a duck, etc, then it is a duck".

If you have been schooled in strong typing and brought up on C++ or C# then duck typing will seem like an error waiting to happen and it is true. Strong typing was invented to try and make programming more error free, but it takes time and effort and dynamic or duck typing is quick and makes for efficient code generation.

To define a function you use the def command, complete with parameters if needed, and a return statement to return a value. For example:

def add(x,y):
return x+y
print(add(1,2))

If you are using IronPython Visual Studio Tools you don't  have to actually enter the tab to start the body of the function - the system does that for you. In fact you have to remember to remove the indent to mark the end of the function.

Notice that parameters are typeless and this is both a convenience and a possible problem.

For example, with the above definition of add the following works perfectly:

print(add('lumber','jack'))

and prints "lumberjack"

If you find this strange and worrying then it is perhaps because you have been in the grip of strongly typed languages for too long!

When you write the add function you are thinking about “adding” together two objects and it is up to the system to work out what you mean by “+” for any two objects you care to pass.

Notice that strongly typed languages such as C# have had to invent the idea of "generics" to allow the general concept of adding two things together to be used in methods irrespective of the exact type of the object passed to the method. If you aren't being too fussy about typing then generics come for free and quite naturally - the only problem is that you might write a function that does a little more than you ever intended. That is, if you only intended to write a function which adds to numbers together then you have more than you bargained for.

It even works for lists - in fact it works for any two objects that makes sense of the + operator:

print(add([1,2],[3,4]))

which prints[1, 2, 3, 4]

Notice that Python doesn't have arrays - the list seems to be enough for any purpose - but you can make use of .NET array objects.

Exactly how is our next subject.

One more complication is that we need to know the scope of a function or a variable definition.

The basic idea is that a name belongs to the namespace that is appropriate to where it is first used – local, global or built in.

If you want to make a variable name global from within a function you have to declare it as such using “global”. Arguments are also influenced by the “object” idea. These are passed into functions by assigning them to local names, i.e. the objects that are passed are simply given new references to them within the function. This results in a behaviour which many programmers will regard as strange.

For example suppose you write:

def test(a):
a=1

Then when you use this function with a call like:

x=2
test(x)

the value of x is unchanged by the assignment within the function.

Ah, you might say, this is pass by value, but not so. What is happening is that when the function is called the local variable “a” is set to “point” to the same object that “x” is “pointing” at. When the object 1 is assigned to “a” the variable “x” is still pointing at the object 2.

You might think that this is indeed pass by value but it isn’t because if you try:

def test(a):
a[0]= “spam”
x=[2,3,4]
test(x)
print x

you will discover that the list in the calling program, x, has its first item changed to “spam”.

What is going on here is, of course, that a reference to an object is being passed by value and any changes to the reference are ignored but any changes to the object are permanent.  This is how most object oriented languages pass objects in functions but ... most don't regard a simple integer as an object as Python does.

There are also facilities for passing arguments by name, variable numbers of parameters and parameter defaults.

For functional programming enthusiasts Python has a lambda expression facility, which allows any expression to be treated essentially as a data object.

Objects

Moving on to more traditional object-oriented facilities brings us to the class statement.

You can define a class complete with variables and functions and create instances of the class by assignment. This sounds all too easy and I have to admit that it really is easy. The only minor complication is that you have to use the keyword “self” as a reference to the particular instance of the class. Consider, for example:

class point:
 def setpos(self,x,y):
self.position=(x,y)
def display(self):
print self.position

 

This defines a class called point which has two methods – setpos and display. Each of these has the default parameter “self” included. The methods use “self” to qualify any variables that are specific to the instance of the class.

If you now create an instance of the class using:

a=point()

you can refer to methods using the usual dot a.method()  notation. For example,

a.setpos(1,2)

and

a.display()

Notice that the brackets are all-important – without them you create variables pointing to the class, not the instance. This is a big difference between Python and other object-oriented languages - the class is actually an object that is created on an almost equal footing with its instances. You can create class data, for example, by using variables outside of methods. This is very similar to the way that Javascript works.

If you use a class definition of the form:

class name(superclass):

then the new class inherits all of the methods defined in superclass. For example:

class complexpoint(point)

defines a new class called complexpoint and inherits all of the methods defined in point.

You can, of course, define new methods and override existing methods.

If a method cannot be found in the class’s definition the superclass definition is searched. If the method cannot be found in the superclass then any super-superclass that might exist is searched and so on until a definition is found or an error is generated. You can also arrange to call the inherited method directly using the class name as the first parameter of the call. In fact all method calls are translated from:

instance.method(parameters)

to

class.method(instance,parameters)

and the first parameter is “self”. You can call instance methods directly using this form. For example:

point.setpos(x,1,2)

is entirely equivalent to:

x.setpos(1,2)

Finally it is worth knowing that operator overloading is supported using a range of standard method names.

For example, if you want to define an addition for points then you need the following method:

def __add__(self,other):
self.ans=(self.pos[0]+other.pos[0],
self.pos[1]+other.pos[1])
return self.ans

Now you can write:

z=x+y

where x and y are point objects. Notice that z isn’t a point object but a tuple a special sort of list. If you want to return a point object you have to use:

ans=point()
ans.setpos(self.pos[0]+other.pos[0],
self.pos[1]+other.pos[1])
return ans

Overriding operators might not seem like something you want to do but after using Python for a while I can assure you that you will – if only to write constructors.

The __init__ operator is the objects constructor and if you overwrite it you can define a non-default constructor. For example, if you include:

def __init(self,x=0,y=0):
self.pos=x
self.pos=y

in the point definition you can now initialise a point using:

x=point(1,2)

and if you don’t specify a value then a (0,0) point is constructed by default. There is also a destructor operator, indexing operator, string slicing operator and so on, all of which can be overloaded.



Last Updated ( Tuesday, 15 November 2022 )