My work in the area of NLP requires me to work with several frameworks across multiple languages such as Java, Python and Lisp. Sometime ago I got a chance to experiment with CLPython, an open-source implementation of Python in Common Lisp. Although CLPython is not under active development now, I found it quite usable. In fact it worked pretty well with both Allegro CL and LispWorks.
In today’s post, I would like to show some examples of this implementation.
You can install CLPython through Quicklisp:
(ql:quickload “clpython”)
For convenience, I created a package and imported symbols from this and standard packages.
(defpackage :pytest
(:use :cl :clos :clpython))
(in-package :pytest)
The first thing I did was to create a habitat – a runtime environment within which expression values are saved across Python calls. Although this is strictly not necessary, I felt that the interaction would be a bit tedious without it.
(setf *habitat* (make-habitat))
We are all set now. We can execute any Python code by enclosing it in double quotes and passing it to the Lisp function run. The following function assigns to a variable and modifies it:
(defun test1 ()
(run “x = 100”)
(run “x = x + 200”))
This is what you get when you run this function:
PYTEST 4 > (test1)
300
The answer is as expected. Note that if we had not initialized the default habitat, the variable x would not have been shared across the two different run statements. The sharing happens through the habitat.
OK. Let us define a Python function dynamically.
(defun test2 (k)
(run
(format nil
“def myfun (arg):
return arg * ~d” k)
))
The interesting thing about the above Lisp function is that it creates a Python function to make use of the argument supplied to it. So, for instance, the following call:
PYTEST 30 > (test2 50)
#<python-function myfun (interpreted) (__main__/myfun) 4030001CA4>
actually defines the following Python function:
def myfun(arg):
return arg * 50
That is, the argument supplied to test2 is embedded in the generated Python function!
The other neat thing is that we can call the generated Python function as if it is a Lisp function!
PYTEST 31 > (funcall (test2 50) 25)
1250
Isn’t that interesting?
You can do the same thing on a Python lambda too.
PYTEST 32 > (funcall (run “lambda x: x * x”) 50)
2500
What sbout Python functions that return multiple values? CLPython makes the result available as a list. Consider this example:
(defun test4 ()
(run
“def myfun ():
return 10, 20, 30″))
PYTEST 14 > (setf fn (test4))
#<python-function myfun (interpreted) (__main__/myfun) 4030000F14>
PYTEST 15 > (funcall fn)
(10 20 30)
Python lists can be accessed as vectors in Lisp.
PYTEST 33 > (setf res (run “[1, 2, 3, 4]”))
#(1 2 3 4)
We can use aref to access the individual elements of the vector.
The last feature we will look at is Python Dictionary.
PYTEST 34 > (setf table (run “dict = {‘a’: 30, ‘b’: 40}”))
#<PY-==->LISP-VAL Hash Table{2} 4020088B1B>
As you would have guessed, CLPython converts this to a hashtable in Lisp. This means we can access the Python dictionary elements as hashtable elements in Lisp.
PYTEST 7 > (setf (gethash “b” table) 60)
60
PYTEST 8 > (run “print dict”)
{‘a’: 30, ‘b’: 60}
PYTEST 9 > (setf (gethash “c” table) 123)
123
PYTEST 10 > (run “print dict”)
{‘a’: 30, ‘b’: 60, ‘c’: 123}
Not only are we able to modify the values in the dictionary, but we can also insert new elements!
That is it for today’s post. Hope you enjoyed it.
Have a great day!
Recent Comments