IronPython – Classes within separate files

Tonight I was asked an interesting question regarding IronPython (well, Python) and how it handles the loose files when executing.  With languages like C#, everything is compiled into an assembly and you have namespaces in order to locate the classes.  However, in Python it is a little bit different.

With python, files can be either compiled into an assembly, or left loose within a directory depending on what you want to do.  If you have your files loose, then you will need to reference the different files (modulesclasses).  In the example below, I have a Customer class in a file called CustomerFile.py.  I then have a main method which creates the Customer and calls the method within a file called main.py.  The code for the files are here:

customerfile.py
class Customer(object):
  def PrintHello(self):
    print “Hello World!”

main.py
from customerfile import Customer
def main():
    print ‘Hello World’
    c = Customer()
    print ‘Created Customer’
    c.PrintHello()

if __name__ == “__main__”:
   print ‘Hello’
   main()

In order for the main file to know about the Customer File, we use the syntax – from import .  Here we say from the customerfile, import the Customer class at the top of the class. If we execute the file, everything is wrote out as expected.

IronPython-2.0B2>ipy.exe main.py
Hello
Hello World
Created Customer
Hello World!

It’s important that both files are located in a directory defined in the path search variable, by default it is the main directory and the Lib directory.

>>> import sys
>>> print sys.path
[‘.’, ‘…IronPython-2.0B2’, ‘…IronPython-2.0B2Lib’]

If for example, CustomerFile.py was moved into a directory called Customer within the main folder, we would need to add that to the path variable in order for IronPython to look in the directory for the file. The code below does this, note that before we can import os, we need to add a reference to the location of the standard library.

import sys
sys.path.append(“C:python25Lib”)
import os
sys.path.append(os.path.abspath(“Customer”))

Now, when we call the same code, IronPython will also search the Customer directory.  The path would now look something like this:

[‘.’, ‘IronPython-2.0B2’, ‘IronPython-2.0B2Lib’, ‘C:python25Lib’, ‘IronPython-2.0B2Customer’]

Not overly difficult, but I can see new users becoming confused by this.

Technorati Tags: ,

3 thoughts on “IronPython – Classes within separate files”

  1. There is no need to mess around with sys.path for something as simple as this!

    You would normally just do this:

    * Name the directory ‘customer’ not ‘Customer’ — lowercase is the convention

    * put a file inside the directory called ‘__init__.py’. It can be empty.

    * Do: “from customer.customerfile import Customer”

    i.e. path separator (” in Windows, ‘/’ in Unix) goes to dot ‘.’

  2. Like it was said in an other comment you need to use the “__init__.py” file to change you directory “customer” into a “module” named “customer”.

    About the “how python handles the loose files when executing”, I would like to explain it “like I see it”.

    What is important to understand, is that there are 3 notions in languages like C# which are the same with python (I’m not speaking about ironpython here).

    Namespace, source, binary.

    When creating a new source, you also create “automatically” a new namespace and a new binary.

    If you create customerfile.py containing the Customer class, and then you use it from main.py, you will see a file called “customerfile.pyc” right next customerfile.py (this is the point I’m not sur about ironpython, I use C#/.Net, or classical python (also called CPython), I don’t mix both)

    customerfile.pyc is the binary. You need *either* customerfile.py or customerfile.pyc (customerfile.pyc is enouth, so python is not “just a script language”) to work.

    So customerfile is the equivalent of the assembly.

    But it’s also the namespace of the class Customer. You can write in main.py :

    import customerfile
    customer = customerfile.Customer()

    So, there is no need to refer to assemblies like in C#, the import does it.

    This is about modules (which are source, namespaces and binaries), but not about submodules.

    If you want to declare Customer into the customer.customerfile namespace, you need to put “customerfile.py” into the “customer” directory… But to prevent to transform ANY directory contained in the project into module, you need to place a __init__.py file into it, it will just change you directory into a module, which may contains submodules.

    What is this __init__.py thing ?

    Let’s suppose you’ve got a mywork.py file defining a “mywork” namespace (and some classes like MyWork).

    And now, you just want to add customer.py defining the Customer class as a submodule of mywork.

    Yuo can write right now in your main.py :

    import mywork
    mywork.MyWork()

    So want to be able to write in your main.py :

    import mywork
    import mywork.customer
    mywork.MyWork()
    mywork.customer.Customer()

    So you’ll put customer.py into a folder “mywork”, and it will be fine… will it ? Well why mywork.py is not INTO the mywork folder ? after all, it’s in the mywork namespace, it should be into the mywork folder !

    in fact, you’ll just rename “mywork.py” into “mywork/__init__.py” and put “mywork/customer.py”, and it will work as expected. Code using “mywork” (as a namespace) won’t need change (of course!)

    before :

    main.py
    mywork.py

    after :

    main.py
    mywork/__init__.py
    mywork/customer.py

    I’m not finished yet ! 🙂

    you can also create “bigger” binairies like assemblies in .Net, which are just “folder module” (folder containing a __init__.py file) which are zipped, importer will consider the zip file like a directory. It’s not very used execpt by code like “py2exe” which convert python code into a set of binaries that can be deployed on computers where python is not installed.

    Everything about binaires may not be true with ironpython.

Leave a Reply

Your email address will not be published. Required fields are marked *