Article
· May 23, 2023 5m read

Embedded Python, Pass-by-Reference, and Classic Rock & Roll

Methods written in ObjectScript can use pass-by-reference arguments to return information to the caller. Python doesn’t support pass-by-reference arguments, so Embedded Python in IRIS doesn’t support them either. That's it, that's the end of the post, hope you liked it. 😉 But wait, what about the Classic Rock & Roll?

Actually, since returning values in method arguments can be useful, this post demonstrates several ways to do this, between ObjectScript and Embedded Python. So let’s start with the simplest example: calling an ObjectScript method that already has a pass-by-reference argument from Python. Here’s the method:

ClassMethod Sum(a as %Integer, b as %Integer, Output sum as %Integer)
{
	set sum = a + b
}

Calling it from ObjectScript is easy. You just have to remember to put a “.” before the 3rd argument.

USER>set x = 4, y = 5
USER>do ##class(Simple.Demo).Sum(x, y, .z)
USER>write z
9

But that's not what you came here for. What about calling it from Python? You have to create a special reference object using iris.ref() and supply that as the 3rd argument. You do not put a “.” before the 3rd argument. (Note that I'm starting the Python shell using :py, an alias for do $system.Python.Shell(), a feature that deserves a Developer Community post of its own.)

USER>:py 

Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type quit() or Ctrl-D to exit this shell.
>>> x = 4
>>> y = 5
>>> z = iris.ref(0)
>>> z
<iris.ref object at 0xffff164ca3b0>
>>> iris.cls("Simple.Demo").Sum(x, y, z)
>>> z.value                           
9

Here's some documentation for iris.ref().

Next, let’s look at a couple of ways to allow an Embedded Python method to return values in its arguments. For these examples, we'll use JSON structures that can be passed into the method and changed. After returning from the method, since these structures are object references, any changes are visible to the caller. For JSON, Python supports the dict and list structures, and IRIS supports %DynamicObject and %DynamicArray structures. Important:  there is no automatic conversion from the ObjectScript JSON structures to the Python JSON structures (or vice versa) when they are passed around.

These options lead to several possible combinations:

  • From Python, create a Python dict/list and pass it into the method.
  • From ObjectScript, create a Python dict/list and pass it into the method.
  • From Python, create an IRIS %DynamicObject/%DynamicArray and pass it into the method.
  • From ObjectScript, create an IRIS %DynamicObject/%DynamicArray and pass it into the method.

So let's rock & roll!

Beatles() is a Python method that returns a dict and list.

/// modify and return a dict and a list
ClassMethod Beatles(membersdict As %SYS.Python, memberslist As %SYS.Python) [ Language = python ]
{
	membersdict['member1'] = 'John'
	memberslist.append('John')
	membersdict['member2'] = 'Paul'
	memberslist.append('Paul')
	membersdict['member3'] = 'George'
	memberslist.append('George')
	membersdict['member4'] = 'Ringo'
	memberslist.append('Ringo')
}

Here's an example of calling it from Python:

USER>:py 

Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type quit() or Ctrl-D to exit this shell.
>>> d = {}
>>> l = []
>>> iris.cls("Simple.Demo").Beatles(d, l)
>>> d
{'member1': 'John', 'member2': 'Paul', 'member3': 'George', 'member4': 'Ringo'}
>>> l
['John', 'Paul', 'George', 'Ringo']

Here's an example of calling it from ObjectScript, passing in an empty dict and an empty list created by calling methods of Builtins():

USER>set d = ##class(%SYS.Python).Builtins().dict()        
USER>set l = ##class(%SYS.Python).Builtins().list()
USER>do ##class(Simple.Demo).Beatles(d, l)
USER>zwrite d
d=7@%SYS.Python  ; {'member1': 'John', 'member2': 'Paul', 'member3': 'George', 'member4': 'Ringo'}  ; 
USER>zwrite l
l=2@%SYS.Python  ; ['John', 'Paul', 'George', 'Ringo']  ; 

Here's some documentation for using dicts and lists from ObjectScript.

Who() is a Python method that returns a %DynamicObject and a %DynamicArray.

/// modify and return a %DynamicObject and a %DynamicArray
ClassMethod Who(membersObject As %DynamicObject, membersArray As %DynamicArray) [ Language = python ]
{
	import iris

	membersObject._Set('member1','Roger')
	membersArray._Push('Roger')
	membersObject._Set('member2','Pete')
	membersArray._Push('Pete')
	membersObject._Set('member3','John')
	membersArray._Push('John')
	membersObject._Set('member4','Keith')
	membersArray._Push('Keith')
}

Here's an example of calling it from Python, passing in an empty %DynamicObject and an empty %DynamicArray created by using iris.cls() to call _New(), and displaying their contents using the %JSON.Formatter:

USER>:py 

Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type quit() or Ctrl-D to exit this shell.
>>> f = iris.cls("%JSON.Formatter")._New()
>>> o = iris.cls("%DynamicObject")._New()
>>> a = iris.cls("%DynamicArray")._New()
>>> o
<iris.%Library.DynamicObject object at 0xffff165c0210>
>>> a
<iris.%Library.DynamicArray object at 0xffff165c0360>
>>> f.Format(o)
{
}
>>> f.Format(a)
[
]
>>> iris.cls("Simple.Demo").Who(o, a)
>>> f.Format(o)
{
  "member1":"Roger",
  "member2":"Pete",
  "member3":"John",
  "member4":"Keith"
}
>>> f.Format(a)
[
  "Roger",
  "Pete",
  "John",
  "Keith"
]

Here's an example of calling it from ObjectScript:

USER>set o = {}, a = []
USER>do ##class(Simple.Demo).Who(o, a)
USER>zwrite o
o={"member1":"Roger","member2":"Pete","member3":"John","member4":"Keith"}  ; 
USER>zwrite a
a=["Roger","Pete","John","Keith"]  ; 

Here's some dynamic objects/arrays documentation.

Since I see some of you are waving your cell phone lights back and forth, asking for an encore, I'll tell you one more thing: passing a %DynamicObject into a Python method is useful for another reason, unrelated to pass-by-reference. It allows you to call a Python method that accepts named arguments. Read all about that here!

Discussion (1)1
Log in or sign up to continue