The PYTHON class provides a bridge from IDL to Python. The class contains a set of static methods that allow you to initialize and interact with the Python interpreter. In addition, each object passed back from Python is wrapped in an instance of the PYTHON class; you can then call methods on the underlying Python object, or set and retrieve attributes on the object. The PYTHON class also makes use of operator overloading, so that many of the built-in IDL routines and mathematical operations will work seamlessly with the wrapped Python object.
See Python Bridge for installation instructions.
Methods and Additional Information
See also Python Bridge: Parameter Passing and Data Conversion.
Examples
Call Python methods directly from IDL code, as if it was an IDL object:
IDL> np = Python.Import('numpy')
IDL> arr = np.random.rand(100)
IDL> print, np.mean(arr)
IDL> print, np.std(arr, dtype='float32')
Run the same commands directly within the Python interpreter:
IDL> Python.Run('import numpy.random as ran')
IDL> Python.Run('arr = ran.rand(100)')
IDL> Python.Run('print(arr.mean())')
IDL> Python.Run('print(arr.std(dtype="float32"))')
As a shortcut for Python.Run, you can type three ">>>" characters before each statement:
IDL> >>>import numpy.random as ran
IDL> >>>arr = ran.rand(100)
IDL> >>>print(arr.mean())
IDL> >>>print(arr.std(dtype='float32'))
You can also type three ">>>" characters and press the Enter key to enter Python Command Mode:
IDL> >>>
>>> import numpy.random as ran
>>> arr = ran.rand(100)
>>> print(arr.mean())
>>> print(arr.std(dtype='float32'))
>>>
IDL>
At the end, press the Enter key to re-enter the normal IDL command mode.
Python Class
Syntax
Result = PYTHON( [PyID])
Note: You do not need to directly call the PYTHON function. Instead, use Python.Import() or Python.Wrap() to return references to Python objects. This syntax is provided here merely for completeness.
Return Value
Returns a reference to a newly-created PYTHON object.
Arguments
PyID
An integer giving the identifier of the object within the Python interpreter. If PyID is not supplied then a reference is returned to the Python __main__ object.
Keywords
None
Python::Import
The Python::Import static method imports a module into the Python interpreter. This also starts the Python interpreter if it has not yet been started.
Syntax
Result = Python.Import( Module )
or
Python.Import, Module
Return Value
If Python.Import is called as a function, then the result is a PYTHON object that wraps the Python module. You can then call functions and get or set attributes using the "dot" notation on that object. This is equivalent to the Python statement import Module as Result.
If Python.Import is called as a procedure, then all of the functions and attributes within Module are imported into the Python __main__. This is equivalent to the Python statement from Module import *. In this case all of the functions and attributes will be available from the static Python class.
Note: In Python, it is generally considered bad practice to use from module import *, as this pollutes the Python __main__ namespace and can lead to name conflicts. To avoid this, use the Python.Import function method, and access all of your methods and attributes off of the returned PYTHON object.
Arguments
Module
A string giving the name of the Python module to import. To import a module within a package of modules you can use dots to separate the names, such as module.submodule.subsubmodule.
Keywords
None
Python::Run
The Python::Run static method executes Python commands within the Python interpreter. This also starts the Python interpreter if it has not yet been started.
Examples
Execute some arbitrary Python code and print the result:
IDL> PRINT, Python.Run('[x*x for x in range(1,11)]')
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Construct a Python "generator", retrieve the generator, and print the values using foreach:
IDL> Python.Run, 'mygen = (x*x for x in range(1,11))'
IDL> mygen = Python.mygen
IDL> HELP, mygen
MYGEN PYTHON <ID=3618> <class 'generator'>
IDL> foreach val, mygen do PRINT, val
1
4
...
100
Create a new Python function using a multi-line statement, then use it:
IDL> Python.Run, "def hello(x):\n" + $
" 'Prints out a friendly message'\n" + $
" return 'Hello, ' + x + '!'"
IDL> Python.Help(Python.hello)
Python Library Documentation: function hello in module_main_
hello(x)
Prints out a friendly message
IDL> PRINT, Python.hello("World")
Hello, World!
Syntax
Result = Python.Run( Command )
or
Python.Run, Command
Return Value
If Python.Run is called as a function method, then the result is an IDL scalar string or a string array containing the Python output from executing the command. If the command produced no output then an empty string is returned.
If Python.Run is called as a procedure method, then the Python output is redirected to the IDL console.
Arguments
Command
An IDL string or string array containing the Python statements to execute in the Python interpreter. If Command is a string array then IDL will send each element to Python as a separate statement. You can also send a multi-line statement (such as a function definition) by using a single IDL string and separating the lines with the "\n" escape code.
Keywords
None.
Python::Wrap
The Python::Wrap static method converts an IDL variable to a Python object and returns a wrapped PYTHON object. This also starts the Python interpreter if it has not yet been started.
Examples
Wrap an IDL floating-point array and call a Python method on it:
IDL> a = Python.Wrap(RANDOMU(seed,10000))
IDL> HELP, a
A PYTHON <ID=399043> <class 'numpy.ndarray'>
IDL> PRINT, a.mean()
0.497082
Syntax
Result = Python.Wrap( Value )
Return Value
The result is a PYTHON object containing a copy of the IDL data. You can then call functions and get or set attributes using the "dot" notation on that object.
Note: The data is a copy of the original data, not a reference. For example, changing values in the IDL array will not affect the wrapped array.
Arguments
Value
An IDL variable of any type except pointer.
Keywords
None
Calling Python Methods
Once you have a PYTHON object (say from calling Python::Import), you can call Python methods on that object by using the "dot" notation. Arguments and keywords can be passed to the method, just like calling an IDL method. For example:
labels = ['Matlab', 'Python', 'IDL', 'Other']
sizes = [20, 30, 40, 10]
colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral']
explode = [0, 0, 0.1, 0]
pyplot = Python.Import('matplotlib.pyplot')
pie = pyplot.pie(sizes, explode=explode, $
labels=labels, colors=colors, $
autopct='%1.1f%%', /shadow, startangle=90)
void = pyplot.axis('equal')
void = pyplot.savefig("myplot.png", dpi = 96)
void = pyplot.show()
This creates the figure shown to the right. Note that when calling methods, you can pass in IDL variables using standard syntax for arguments and keywords.
Get and Set Python Attributes
Once you have a PYTHON object (say from calling Python::Import or Python::Wrap), you can retrieve or set attributes on that object by using the "dot" notation. As an example, many Python objects have a __doc__ attribute that contains a brief description of the class. We can access this using the following code:
IDL> rand = Python.Import('numpy.random')
IDL> rand.__doc__
========================
Random Number Generation
========================
...
You can also set attribute values using the "dot" notation. For example, we can define our own Python class, and then get and set attributes on an instance of that class:
Python.Run, "class MyClass:\n" + $
" myattr = 'world?'\n" + $
" def hello(self):\n" + $
" 'Prints out a friendly message'\n" + $
" return 'Hello, ' + self.myattr
Python.Run, 'myvar = MyClass()'
myvar = Python.myvar
PRINT, myvar.myattr
myvar.myattr = 'World!'
PRINT, myvar.hello()
IDL prints:
world?
Hello, World!
Get the List of Methods and Attributes
You can call the Python built-in dir method to retrieve an IDL list containing the available methods and attributes. For example:
IDL> pyplot = Python.Import('matplotlib.pyplot')
IDL> attr = Python.dir(pyplot)
IDL> attr.ToArray()
Annotation
Arrow
Artist
...
ylim
yscale
yticks
Case Sensitivity
IDL variable and method names are case insensitive while Python names are case sensitive. When you call a Python method such as the savefig method on the matplotlib.pyplot module, the Python bridge first looks for a lowercase method with that name. If it does not find a match, the bridge then uses the internal dictionary of methods and attributes (__dict__) to try to find a case-insensitive match. The first match is assumed to be correct.
If your module happens to have two methods or attributes with the same name that differ only in case, you can use the built-in Python.getattr to retrieve the correct one.
Built-in Functions
Python has a number of "builtin" methods such as abs(), cmp(), dir(), map(), or reduce(). These can be invoked as static methods on the Python class. For example:
IDL> a = -5
IDL> Python.abs(a)
5
IDL> Python.dir()
[
"_builtins_",
"_doc_",
"_name_",
"_package_",
"mygen",
"myvar"
]
Here is a partial list of built-in functions that you may find useful:
Python.dir( [object] )
With no argument, return the list of names in the current local scope. With a Python object, return a list of valid attributes for that object.
Python.getattr( object, attr [, default] )
Retrieve the attribute attr from the Python object. attr must be a string. If the attribute does not exist then default is returned if provided, otherwise an error is thrown.
Python.hasattr( object, attr )
Return true if the object has the attribute attr, or false otherwise.
Python.help( object )
Returns a string array containing the help documentation for the specified object.
Python.id( object )
Returns an integer giving the Python identifier number.
Python.isinstance( object, classinfo )
Returns 1 (true) if the Python object is an instance of the classinfo Python object, or 0 (false) otherwise.
Python.len( object )
Returns an integer giving the number of items within an object.
Python.repr( object )
Returns the string representation of the object. This is the same as what IDL implied print will return.
Python.setattr(object, attr, value)
Set the attribute attr on the Python object to the provided value. attr must be a string.
Python.str( object )
Returns the string representation of the object. This is the same as what normal IDL print will return.
Python.vars( object )
Returns an IDL hash containing the __dict__ attribute for the object. The __dict__ attribute contains all of the attributes and methods of the object.
Operator Overloading and Magic Methods
In IDL, PYTHON objects can be used in most mathematical, logical, and bitwise expressions. When a PYTHON object is used in an expression, any "normal" IDL variables (such as integers, floats, or strings) are first converted to PYTHON objects. Then, a call is made to the appropriate Python "magic method" and the result is returned. The result is converted back to a normal IDL variable if possible, otherwise a PYTHON object is returned.
The following table lists the Python magic method that is invoked for each IDL operator:
Operator |
IDL Syntax |
Python Call |
Plus |
a + b |
__add__ |
Minus |
a – b |
__sub__ |
Asterisk |
a * b |
__mul__ |
Slash |
a / b |
__div__ |
MinusUnary |
–a |
__neg__ |
Caret |
a^b |
__pow__ |
Modulus |
a mod b |
__mod__ |
Equal |
a EQ b |
__eq__ |
Not equal |
a NE b |
__ne__ |
Greater than or equal
|
a GE b |
__ge__ |
Less than or equal
|
a LE b |
__le__ |
Greater than |
a GT b |
__gt__ |
Less than |
a LT b |
__lt__ |
Bitwise and |
a AND b |
__and__ |
Bitwise or |
a OR b |
__or__ |
Bitwise xor |
a XOR b |
__xor__ |
Bitwise not |
NOT b |
__invert__ |
Is true |
if (a) |
bool( ) |
Logical negation
|
~a |
~bool(a) |
Accessing Python List, Tuple, Dictionary, or Array Elements
In many cases, you can access elements of a PYTHON object using standard IDL array syntax, as if the object were a "normal" IDL array. When you access a Python object and retrieve an element or a set of elements, IDL will convert the result back into a "normal" IDL variable. Here are some examples:
Using a Python List or Tuple
IDL> var = Python.Wrap(List(3.14, 1234, 'hello'))
IDL> HELP, var
VAR PYTHON <ID=202> <class 'list'>
IDL> PRINT, var[2]
hello
IDL> var[0] = 3.14159
IDL> PRINT, var
[3.1415901, 1234, 'hello']
Using a Python Dictionary
IDL> var = Python.Wrap(HASH('Mercury', 1, 'Venus', 2, 'Earth', 3))
IDL> HELP, var
VAR PYTHON <ID=248> <class 'dict'>
IDL> PRINT, var['Venus']
2
IDL> var['Mars'] = 4
IDL> PRINT, var
{'Mercury': 1, 'Earth': 3, 'Mars': 4, 'Venus': 2}
Using a Python Array
IDL> var = Python.Wrap(INDGEN(10))
IDL> HELP, var
VAR PYTHON <ID=258> <class 'numpy.ndarray'>
IDL> PRINT, var[3:5]
3 4 5
IDL> var[-2:*] = -1
IDL> PRINT, var
[ 0 1 2 3 4 5 6 7 -1 -1]
Using Foreach to Iterate
You can use the IDL FOREACH statement on a Python list, tuple, dict, ndarray, or any other Python class that implements the iterator protocol. For example:
IDL> a = Python.Wrap(LIST('a',2,'c',3.14))
IDL> foreach value,a,index do print, value, index
a 0
2 1
c 2
3.14000 3
For Python dict (or other classes with an iterator) the FOREACH "index" variable (the third argument) will be set equal to the Python iterator. Avoid modifying this variable. For example:
IDL> a = Python.Wrap(HASH('Mercury',1,'Venus',2,'Earth',3))
IDL> HELP,a
A PYTHON <ID=71> <class 'dict'>
IDL> foreach planet,a,index do HELP,planet,index
PLANET STRING = 'Venus'
INDEX PYTHON <ID=74> <class 'dict_keyiterator'>
PLANET STRING = 'Mercury'
INDEX PYTHON <ID=74> <class 'dict_keyiterator'>
PLANET STRING = 'Earth'
INDEX PYTHON <ID=74> <class 'dict_keyiterator'>
Using Python Callable Objects
In most cases you invoke methods by using the "dot" notation on a Python module or object. For example:
IDL> randomModule = Python.Import("numpy.random")
IDL> x = randomModule.rand(50)
IDL> HELP, x
X DOUBLE = Array[50]
However, in certain cases you may end up with a reference to an actual Python method object. For example, if you used Python.getattr to retrieve the method object by name:
IDL> randMethod = Python.getattr(randomModule, 'rand')
IDL> HELP, randMethod
RANDMETHOD PYTHON <ID=2252> <class 'builtin_function_or_method'>
IDL> Python.callable(randMethod)
1
In these cases, you can call the method using parentheses:
IDL> x = randMethod(50)
IDL> HELP, x
X DOUBLE = Array[50]
You could also use the __call__ magic method to invoke the method:
IDL> x = randMethod.__call__(50)
Passing Variables to the Main Python Level
Normally, you will pass IDL variables into Python method calls as input arguments or keywords, and receive the result back as an IDL variable. However, if you use the Python.Run method, you may want to pass variables to or from Python directly. To do this, you can use the standard IDL "dot" notation on the PYTHON static class. For example:
Set a Python variable
IDL> Python.myvar = FINDGEN(10)
IDL> Python.Run('myvar')
'array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.], dtype=float32)'
Retrieve a Python variable
IDL> myvar2 = Python.myvar
IDL> HELP, myvar2
MYVAR2 FLOAT = Array[10]
Note: All variables that are passed to Python will be given a lowercase name, regardless of the original case. When retrieving variables from Python, the lowercase version will be tried first. If it does not find a match, then the bridge uses Python's internal dictionary of attributes (__dict__) to try to find a case-insensitive match. The first match is assumed to be correct. If you happen to have two variables with the same name that differ only in case, you can use the built-in Python.getattr to retrieve the correct one. For example:
IDL> void = Python.Run('var = 1')
IDL> void = Python.Run('Var = 2')
IDL> PRINT, Python.var
1
IDL>
IDL> PRINT, Python.getattr(Python(), 'Var')
2
Tip: For more information on passing variables see Python Bridge: Parameter Passing and Data Conversion.
Python Command Mode
In most cases you will want to interact with Python by using Python.Import to retrieve a module, and then calling methods on the returned PYTHON object. However, you can also execute Python statements directly within the Python interpreter using the Python.Run method. As a shortcut to typing Python.Run, you can also type three ">>>" characters:
IDL> >>>import matplotlib.pyplot as plt
IDL> >>>import numpy.random as ran
IDL> >>>arr = ran.rand(100)
IDL> >>>p = plt.plot(arr)
IDL> >>>plt.show()
Note that you need to type three ">>>" characters before each Python statement.
As an alternative, you can type three ">>>" characters and press press the Enter key to enter "Python Command Mode":
IDL> >>>
>>> import matplotlib.pyplot as plt
>>> import numpy.random as ran
>>> arr = ran.rand(100)
>>> p = plt.plot(arr)
>>> plt.show()
>>>
At the end, press press the Enter key to re-enter the normal IDL command mode.
There are a few limitations in Python Command Mode:
- Unlike the regular Python console, you cannot enter a multi-line construct on separate lines. To enter a multi-statement construct (such as a function definition), all of the statements must be on the same line, separated by semicolons. For example:
>>> def hello(x): print(x)
- Any variables or objects created using Python.Run or Python Command Mode will exist only within the Python interpreter. To work with these objects within IDL, you will need to retrieve a reference. For example:
IDL> >>>
>>> import matplotlib.pyplot as plt
>>> import numpy.random as ran
>>> arr = ran.rand(100)
>>>
IDL> myarr = Python.arr
Information about Python Objects
N_ELEMENTS, DIM, LENGTH, NDIM
The N_ELEMENTS function and the LENGTH variable attribute return the number of elements within the Python object. For Python ndarray objects, the shape attribute is used to compute the total number of elements. For all other Python objects, IDL calls Python.len() and returns the result.
The DIM variable attribute returns a scalar or array giving the dimensions of the object. For Python ndarray objects, DIM is set equal to the shape attribute in reverse order (since Python is "row major"). For all other Python objects, IDL calls Python.len() on the object and returns the result.
The NDIM variable attribute returns an integer giving the number of dimensions of the object. For Python ndarray objects, NDIM is set equal to the number of elements in the shape attribute. For all other Python objects, IDL returns NDIM=1 for Python objects that have multiple items (such as lists, tuples, or dicts), or 0 for scalar Python objects.
HELP
The HELP procedure provides a brief description of the PYTHON object:
IDL> np = Python.Import('numpy')
IDL> HELP, np
NP PYTHON <ID=327> <class 'module'>
IDL> HELP, np.ndarray
<Expression> PYTHON <ID=330> <class 'type'>
For more detailed information, call Python.help() on your object.
ISA
The IDL ISA function uses the following rules:
- ISA(pyObj, 'OBJREF') always returns true
- ISA(pyObj, /ARRAY) returns true if pyObj is considered to be an "array", or false otherwise. See N_ELEMENTS above for the rules.
- ISA(pyObj, /SCALAR) returns true if pyObj is considered to be a "scalar", or false otherwise. See N_ELEMENTS above for the rules.
All other ISA keywords will return false.
TYPECODE, TNAME, TYPENAME, TYPESIZE
PYTHON objects always return the standard values for an IDL object reference: TYPECODE=11, TNAME='OBJREF', TYPENAME='PYTHON', and TYPESIZE=4.
PRINT and Implied Print
The PRINT procedure will call Python.str() on the object, while using Implied Print will call Python.repr(). For example:
IDL> arr = Python.Wrap(INDGEN(5))
IDL> PRINT, arr
[0 1 2 3 4]
IDL> arr
array([0, 1, 2, 3, 4], dtype=int16)
Version History
See Also
!NULL, HASH, LIST, Python to IDL Bridge, Python Bridge Parameter Passing and Data Conversion