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)  ; call "rand" method
IDL> print, np.mean(arr)
IDL> print, np.std(arr, dtype='float32') ; pass keyword

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() ; call a numpy.ndarray method
     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:

; Define some IDL variables
labels = ['Matlab', 'Python', 'IDL', 'Other']
sizes = [20, 30, 40, 10]
colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral']
explode = [0, 0, 0.1, 0] ; "explode" the 3rd slice
 
; Import matplotlib.  Make sure it is installed by running 'pip install matplotlib
pyplot = Python.Import('matplotlib.pyplot')
 
; Call methods on the Python modules
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
; Create an instance and retrieve the Python variable.
Python.Run, 'myvar = MyClass()'
myvar = Python.myvar
 
; Now retrieve the attribute value and then modify it.
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) ; invoke the "rand" method
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) ; invoke the "rand" method
IDL> HELP, x
X      DOUBLE    = Array[50]

You could also use the __call__ magic method to invoke the method:

IDL> x = randMethod.__call__(50) ; invoke the "rand" method

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) ; pass by value (makes a copy)
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 ; pass by value (makes a copy)
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 ; retrieves the first variable
  1
IDL> ; Retrieve the second variable by name from Python __main__
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); return 'Hello'
  • 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 ; retrieve the array from Python

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


8.5

Introduced

See Also


!NULL, HASH, LIST, Python to IDL Bridge, Python Bridge Parameter Passing and Data Conversion