This document was written in 1995, so the information may not be entirely correct with respect to current versions of Python and Tcl.

Comparison of Tcl and Python

Sjoerd Mullender
CWI
Email: Sjoerd.Mullender@cwi.nl

Table of Contents

1. Introduction
2. Tcl
3. Python
4. Graphical User Interfaces
5. Embeddability
6. Packages
7. Comparing Tcl and Python
8. Conclusion
9. References


Abstract

Large applications can often profit from the use of a high level language in which extensions can be written (extension language). Today several more-or-less standard extension languages are available. Some languages were designed to be extension languages from the start, whereas others were designed for more general use, but happened to be easily made into extension languages. In this document we compare two such languages, Tcl and Python.

1. Introduction

Large software packages often need some way to configure and extend the application. This is easily done using a so-called extension language. There are many extension languages, and it is also possible to design one for the specific application. Important reasons to use a standard language are that the language is likely to be more complete than an ad-hoc language, and if more applications use the same language, it is more likely that a prospective user already knows the extension language.

Important considerations when deciding which extension language to use include:

In this document we compare Tcl and Python for the purpose of using them as extension languages.

The following sections introduce Tcl and Python, followed by several sections in which more detailed comparisons are made. We end with the conclusion.

1.1. Typographical Conventions

Language constructs are given in Courier, answers from the interpreter are given in Courier-Oblique.

2. Tcl

Tcl[1][3] (Tool Command Language; pronounce "tickle") is a string-based scripting language and an interpreter for that language that is designed to be easy to embed in other applications. Tcl was not designed to be a general purpose programming language, but. The philosophy behind Tcl is that two languages are used for a large software system: one, such as C or C++, for manipulating the complex internal data structures where performance is key, and another, such as Tcl, for writing small-ish scripts that tie together the C pieces and are used for extensions[2]. Tcl was designed and implemented by John Ousterhout, then at U.C. Berkeley, now at Sun Microsystems Laboratories.

Tcl is portable to any Unix system. At Sun Microsystems Laboratories work is underway to port Tcl to the Macintosh and PC. Several ports by others are available.

Tcl has quite a large and active user community. The Tcl newsgroup comp.lang.tcl averages a few hundred messages per day. Since Ousterhout moved to Sun, Sun Labs started a Tcl/Tk project. See <URL:http://www.smli.com/research/tcl/> for details.

A simple program to calculate factorials in Tcl could look like this:

proc fact x {
  if {$x <= 1} {
    return 1
  }
  return [expr $x * [fact [expr $x-1]]]
}
fact 4
24
fact 17
-288522240

This example shows a few important features of the language. To use the value of a variable the name must be prepended with $. The result of a function call is substituted by enclosing the call in [ ]. Grouping of statements is done using { }. Expressions are introduced by the command expr. Tcl does not check for integer overflow.

In fact, statement grouping is not necessarily done using { }. Braces are used for quoting strings, just like double quotes, but there is a difference. When a string is quoted using double quotes, variable substitution occurs there and then, whereas it is delayed when the string is quoted with braces. The distinction is important, as can be seen in the following example.

proc print x "puts $x"
can't read "x": no such variable
proc print x {puts $x}
print "Hello World"
Hello World

The first try to define the procedure print gives an error because the variable x is not set when $x is substituted, whereas the second try works because the substitution is delayed. When the command print is executed, the body (which is just a string) is evaluated. At this time, the value of x is substituted. This works, because calling the procedure instantiates the formal parameter with the value of the argument, hence there now exists a variable x.

Tcl commands have a very simple syntax:

command arg1 arg2 arg3 ...

The command is either a built-in command or the name of a procedure. Arguments are separated by white space, and the command is terminated by either a newline or a semicolon. Commands and arguments are string-valued. Like in the shell, variable and command substitution is performed. Variable substitution occurs when a variable is prepended with $, command substitution occurs when a command is enclosed by [ ]. These substitution occur when a command and its arguments are evaluated. Programming constructs such as if and while are implemented as commands that determine whether and how often to evaluate their arguments.

Tcl supports several types, but all types are implemented as strings. Supported types are strings, integers, floating point numbers, lists, and associative arrays. Integers and floating point numbers are supported in as far it is possible to do arithmetic with them. However, the values are always converted to and from strings. Lists are strings with embedded spaces. The spaces separate list elements. The following example illustrates this.

set pi 3.14159265359
3.14159265359
string length $pi
13
set pi2 [expr $pi*$pi]
9.8696
string length $pi2
6
set x {a b {c d e} f}
a b {c d e} f
lindex $x 2
c d e
string length $x
13

The command set assigns the second argument to the first argument, i.e. the first statement assigns a value that is close to pi to the variable pi. The command string has several subcommands that deal with strings. The length subcommand returns the length of the argument string. The command lindex returns the specified element from a list. As can be seen, it is possible to treat variables both as strings and as numbers or lists.

This is not completely true for associative arrays:

set phone(Sjoerd) 4127
4127
puts $phone
can't read "phone": no such variable
puts $phone(Sjoerd)
4127

When Tcl commands fail for some reason, they abort the script. It is possible to catch these errors using the catch command. It is also possible to generate an error from a Tcl script using the error command. For example:

if {[catch {#execute some code and find an error
      error "an error occured"
    } result]} {
  # command failed, result is the error message
  puts stderr $result
} else {
  # command was ok, result is its return value
}

Any errors that occur in the statement after the catch command are caught. The error message that is put in result can be used to find out what went wrong.

3. Python

Python[4] (named after Monty Python's Flying Circus) is an object-oriented, interpreted, extensible programming language and interpreter for that language. Python was designed and implemented by Guido van Rossum from CWI, Amsterdam. Python is both a language and an interpreter for that language.

Python is available for at least Unix (any modern version), PC (MS-DOS, MS-Windows, OS/2), and Macintosh.

Python has a sizable user community. Python development is organized in the Python Software Activity. Its purpose is to structure and maintain the Python software environment, promote its use by new people in new ways, represent the Python community at large to other interests, and help coordinate the Python community. See <URL:http://www.python.org> for more information on the Python Software Activity.

A simple program to calculate factorials in Python could look like this:

def fact(x):
  if x <= 1:
    return 1
  return x * fact(x-1)
fact(4)
24
fact(17)
Traceback (innermost last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 4, in fact
  File "<stdin>", line 4, in fact
  File "<stdin>", line 4, in fact
  File "<stdin>", line 4, in fact
  File "<stdin>", line 4, in fact
OverflowError: integer multiplication
fact(17L)
355687428096000L

This example shows a few key features of the language. Statement grouping is done using indentation. Integer overflow is detected and reported. Python also understands infinite precision integers (called long integers). Although Python is strongly-typed, the types of arguments are not predetermined. The use that is made of the arguments determines what argument types are acceptable.

Python supports many types, and it is possible to define new types by extending the interpreter. Types that are supported by the standard interpreter include integers, infinite precision integers, floating point numbers, strings, lists, tuples, and dictionaries. Python is object-oriented with multiple inheritance. Python objects can be made to look and behave like built-in types by defining special methods.

When an error occurs, execution of the Python script is aborted. Errors can be caught using a try/except clause. An error can be generated by the script using the raise statement. For example:

try:
  # execute some code and find an error
  raise RuntimeError, "an error occured"
except RuntimeError, message:
  # command failed, message is the error message
  print message
else:
  # command was ok
  pass

There are many error categories, and it is possible to define new ones. It is possible to only catch specific errors and let any other errors go undetected (or have other levels of the code catch those errors). Using try and except, we could have prevented the interpreter from printing a complete stack trace when the call to fact above failed:

try:
  fact(17)
except OverflowError:
  print "overflow detected when executing fact"
overflow detected when executing fact

Python integers are guaranteed to be at least 32 bits wide, and they are checked for overflow on all arithmetic operations. If larger integers are needed, long integers can be used. Long integers provide infinite precision but are more expensive to use.

Python uses modules to manage name spaces. Each module has its own name space that is independent of all other modules. By importing a module, the names of the imported module become available by prefixing the names with the module name. There is also one global name space that contains standard functions such as len, open, and range. These names are available everywhere, except where a module or function uses the name for its own purposes.

Python has built-in support for documentation. If a module, function definition or class definition starts with a string, it is taken as documentation for the module, function or class. This documentation is available programmatically, which is especially helpful for interactive use.

In Python names are bound to objects. You can change the object, or you can change the binding. An assignment is a change of binding, a method on an object may change the object itself. For example:

a = 'some object'        # bind a to an object
b = a                    # bind b to the same object
a = 'some other object'  # change the binding of a

After this, b is still bound to the original object, but a is bound to the new object.

a = []                   # bind a to an object
b = a                    # bind b to the same object
a.append(1)              # change the object

The third statement changes the object itself. Both a and b are still bound to the same object.

4. Graphical User Interfaces

An important aspect of modern-day scripting languages is the availability of a Graphical User Interface (GUI). The standard GUI for Tcl is the Tk Toolkit (pronounce tee-kay). For Python there are several options. There is an interface to Tk called Tkinter, an interface to Motif, an interface to Stdwin, and there are interfaces to Macintosh and Microsoft Windows window systems. There is also an interface to the Silicon Graphics GL and FORMS libraries. A new object-oriented GUI for Python is being developed. This GUI is called Wpy and it is supposed to be a portable, object-oriented interface that can be implemented on top of a system's native window system. There are currently implementations for Tkinter and Microsoft Windows but other implementations are planned.

The power of any GUI lies in the available high-level graphical objects (widgets), and the ease with which these widgets can be combined and configured. Of less importance is the ease with which a minimal program can be written, although it may give an indication of the ease of programming the GUI. It is much more important how versatile the GUI is and how easy it is to create complicated systems.

4.1. Tk Toolkit

One reason for the popularity of Tcl is Tk, the Tcl Toolkit. Tk provides a fairly high level and easy to use interface to the window system. Tk was originally written for the X Window System, but has subsequently been ported to other platforms, such as Microsoft Windows. A simple example of the use of Tk follows. This program will display a window with a button. Pressing the button will cause the application to exit.

button .b -text "Hello, World!" -command exit
pack .b

The first statement creates a button with the name .b and the text "Hello, World!" on the label. When the button is pressed, the command exit is executed. The second statement calls upon the geometry manager to place the button in a frame (window) on the screen. The interpreter automatically enters its mainloop at the end of the source file.

The Python version is slightly more complicated, because we have to import the Tkinter module, and we have to define an exit function without arguments. Callback functions in Tkinter are not allowed to have any arguments.

from Tkinter import Button
def exit():
  import sys
  sys.exit(0)
b = Button(None, {'text': 'Hello, World!', 'command': exit})
b.pack()
b.mainloop()

The from statement imports the name Button from the Tkinter module, thus making that name available to the current module. Next a function with the name exit is defined. Next, the button is created and the resulting object is bound to the name b. The text on the button label is "Hello, World!", and the function exit is called when the button is pressed. The following statement calls upon the geometry manager to place the button in a frame on the screen. And finally the Tk mainloop is entered.

4.2. Motif

The Motif toolkit is only available in Python[7]. Using Motif, the same program would look like this:

def exit(widget, client_data, call_data):
  import sys
  sys.exit(0)
def main():
  import Xt, Xm
  top = Xt.Initialize()
  button = top.CreateManagedWidget('button', Xm.PushButton,
            {'labelString': 'Hello, World!'})
  button.AddCallback('activateCallback', exit, None)
  top.RealizeWidget()
  Xt.MainLoop()
main()

In this example, two functions are defined. The first is called exit and is used as the callback function for the button that we are going to create. In Motif, callback functions have three arguments, the Motif widget on whose behalf the callback is called, some user-supplied data, and some system-supplied data. We don't use any of the arguments. The function main does the real work. In it the Xt and Xm modules are first imported to make them available for use. Then the window system is initialized. The result of Xt.Initialize() is the top-level widget of which all other widgets in the application are children. In the next statement a button widget is defined. The label of the button is "Hello, World!". In the following statement a callback function is attached to the button. The first argument to AddCallback is the type of callback, the second argument is the function to be called, and the last argument is the user-supplied data (the client_data in the function exit). The last two statements create the window with the button and start the X Toolkit main loop. On the last line, the function main is called.

Obviously, Motif is only available on systems that run the X Window System. This excludes systems such as Macintoshes and PC's running Microsoft Windows. There are many standard Motif widgets, some of which are actually compound widgets that deal with common tasks such as file selection.

5. Embeddability

Both Tcl and Python can easily be embedded in another application. Tcl was designed for this, and Python was adapted for this. It is also possible to extend the interpreters with code written in C or C++. There is a subtle but important difference between the two. When an interpreter is embedded in another program, the other program calls upon the interpreter to perform certain tasks, such as interacting with the user, of for configuring the application. When the interpreter is extended with code written in another language, the flow of control is in the reverse direction. The interpreter calls upon the extension to perform tasks, often tasks that are impossible to do or too slow when done in the interpreted language.

5.1. Tcl

The interface between Tcl and C/C++ consists of a set of functions. Tcl commands are given to the interpreter and results are obtained from the interpreter as strings. Using the Tcl interpreter from C is easy. The following example shows how to create an interpreter and how to execute code.

Tcl_Interp *interp; /* the interpreter */
char cmd[] = "set answer 42"; /* the command to execute */
int code; /* the result code */
interp = Tcl_CreateInterp(); /* create an interpreter */
code = Tcl_Eval(interp, cmd); /* execute the command */

Since the Tcl interpreter may write into the string passed to it in Tcl_Eval, it is not possible to pass it a string as in Tcl_Eval(interp, "set answer 42").

All interaction between the Tcl interpreter and C code that is used to extend the interpreter or in which the interpreter is embedded is done using strings. For instance, setting a Tcl variable is done as follows.

Tcl_SetVar(interp, "answer", "42", 0);

In Tcl it is possible to link C variables to Tcl variables. This means that the same variable is accessible both from C and Tcl. This is done as follows.

int answer = 42;
Tcl_LinkVar(interp, "answer", (char *) &answer, TCL_LINK_INT);

Extending Tcl with code written in C involves adding commands which are bound to the code to be executed to the interpreter.

extern int EqCmd(ClientData clientData, Tcl_Interp *interp,
      int argc, char *argv[]);
Tcl_CreateCommand(interp, "eq", EqCmd, (ClientData) NULL,
      (Tcl_CmdDeleteProc *) NULL);

5.2. Python

Extending Python with modules written in C (or C++) is easy. There are tools available to create all the boilerplate code, so that only the code that does the real work needs to be written. Often this real code merely interfaces to an existing library, and for this case there is even more support. Once the new module is written, a new interpreter must be created that incorporates the module. On many operating systems (all modern ones) it is possible to load extension modules dynamically, thus obviating the need to create a new executable.

Embedding the Python interpreter in another application is also easy. The interpreter must be initialized by calling Py_Initialize(), after which Python commands can be sent to the interpreter with PyRun_SimpleString() or PyRun_SimpleFile(). The following example shows how to initialize the interpreter, and how to execute code.

int code;
Py_Initialize(); /* initialize the interpreter */
code = PyRun_SimpleString("answer = 42"); /* the command to execute */

PyRun_SimpleString() takes a string as argument which is parsed as Python code. It is also possible to call Python functions using a lower level interface that circumvents the need for parsing Python code.

PyObject *arglist, *result, *my_callback;
my_callback = ...; /* somehow get a pointer to the function */
arglist = Py_BuildValue("(i)", 42); /* create the argument list */
result = PyEval_CallObject(my_callback, arglist); /* do the call */
Py_DECREF(arglist); /* destroy the argument */

Python objects are reference counted. This means that when a reference to an object is maintained, the reference count must be incremented, and when a reference to an object is released, the reference count must be decremented. The macros Py_INCREF and Py_DECREF perform these tasks.

The function Py_BuildValue makes it easy to convert C types to Python objects. The function PyArg_ParseTuple is its counterpart. It converts Python tuple objects to C types.

How to extend the Python interpreter or to embed the interpreter in an application is more fully described in [6].

6. Packages

For both Tcl and Python there are many packages available. For Tcl, it is worthwhile to check <URL:http://web.cs.ualberta.ca/~wade/HyperTcl/>. For Python, the place to check is <URL:http://www.cwi.nl/ftp/python/contrib/>, but also the Tools and Demo subdirectories of the Python distribution. Many useful packages are a standard part of the distribution[5].

Currently there is much interest in the World-Wide Web among Python users, so there is quite a lot of support for that. Python grew up in an environment where there was much interest in multimedia, so there is also quite a lot of support for that.

7. Comparing Tcl and Python

7.1. The Language

Tcl is a simplistic language. It is easy to write simplistic programs in Tcl, but it is extremely difficult to write complex programs. There is no support for complicated data structures, there is no check on arithmetic overflow. Global variables are often necessary.

Tcl replies on dynamic scope. Dynamic scope is when a procedure can get to the local variables of its caller. Relying on this feature makes it hard to re-use code since two programs invariably use different variable names, and dynamic scope relies on the names matching exactly. Also, reading programs that use dynamic scope is more difficult, since you can't tell where a variable comes from: it depends on the caller. It is hard to circumvent the use of dynamic scope since Tcl doesn't use call by reference. Instead, you pass the name of a variable and use dynamic scope to get to the value.

Python is a full-fledged programming language. Beginners in Python often complain about the fact that block structure is indicated by indentation, but one gets used to this. Beginners also often get confused by the fact that assignment in Python is a binding of an object to a name, and not, as in conventional programming languages, a changing of an object. Python is object-oriented, but it is also possible to program in Python without using any object-orientation.

The mechanism to catch and deal with errors is more primitive in Tcl than it is in Python. In Tcl it is not possible to choose which errors to catch and which to let go through. When an error is caught, the error message must be checked to see if the error can be handled, and if not, the error must be re-raised. In Python it is possible to specify error categories that must be caught.

7.2. Name spaces

In Tcl, all names of variables and functions are in one name space. Multiple name spaces can be created by creating multiple interpreters in one program, each interpreter having its own name space. Creating multiple interpreters is easy, but it is expensive to transfer values from one interpreter to the other[8]. Also, multiple interpreters can only be used when Tcl is used embedded in an application; the stand-alone Tcl interpreter, tclsh, does not support multiple interpreters, and hence only a single name space.

In Python, there is a private name space for each module. Also, by using classes, there is much less of a need to use global variables. It is relatively easy to define a class in such a way that it can be re-used in unrelated programs.

Importing code from elsewhere is harder in Tcl than it is in Python because of possible name collisions. In Tcl, there is no guarantee that names used in imported software do not collide with names used in the main code. There is not even a mechanism to find out whether there is a clash of names. In Python software is always imported in the form of modules, and modules have their own, private name space.

7.3. Speed

7.3.1 Pluses and minuses

In Tcl, everything is a string. This means that much time is spent interpreting and converting strings. List indexing is done by searching the list for unquoted spaces and braces; when doing arithmetic, strings must be converted to integers and floating point numbers to do the calculation and then they must be converted back to strings; when executing code such as function bodies, the string must be parsed every time. Some operations are done very efficiently. For instance, adding an item to the end of a list is done in constant time (i.e. independent on the length of the list).

Tcl does not convert code to an internal format. This means that code has to be parsed every time it is executed.

Python compiles code to an internal form which is then executed. Compilation happens when a module is first read, after which the compiled code is cached on file for future use. Integers and strings in the input are converted to internal form at this stage also.

Because Python name spaces are highly dynamic, the interpreter must always search the various name spaces to find the value of a variable. Searching for a name that is used inside a function involves looking at the dictionary of local variable names, the dictionary of module-global names, and the dictionary of global names. The compiler optimizes this search by noting which variables get assigned to in a function. Accesses to these variables then only involve subscripting into an internal list, and, by the same token, there is no need to look in the dictionary of local variables for any other names. Accessing an instance method or variable also involves looking in several dictionaries. Each class instance has a private dictionary which is searched first. Then the dictionary of the class is searched, and finally the dictionaries of all base classes. In all cases, searching stops as soon as a match is found.

7.3.2 Benchmarking

It is hard to compare the speeds of different language/interpreter combinations. It is possible to write benchmarks, but there is always the danger that a benchmark is not fair because it performs tasks that are biased towards one language or implementation. It is also possible to write benchmarks that overstress certain weak points in a language. The following simple benchmark gives the result that for this case Python is 1.9 times faster than Tcl. This is, in fact, a fairly typical result[9].

The Tcl test is as follows. The definition of fact is as given previously. Tcl has a built-in command time to time the execution of a script.

Note the many levels of braces and angle brackets in this code. This is typical for Tcl programs.

proc timing niter{
  return [expr {[lindex [time {
    for {set n 0} {$n < $niter} {incr n} {
      set x [fact 12]
    }

  }] 0] / $niter}]
}
puts [timing 1000]
4008

The Python test is as follows. The definition of fact is as given previously. Python does not have a built-in command to time the execution of a script, so we have to do it ourselves.

def timing(niter):
  from time import time

  r = range(niter)
  t0 = time()
  n = 0
  while n < niter:
    x = fact(12)
    n = n + 1
  t1 = time()
  return int(1000000.0*((t1-t0))/niter)
print timing(1000)
2070

8. Conclusion

For all but the simplest of scripts Python is a better language than Tcl. On the other hand, Tcl is slightly easier to embed in an application. Python is faster and provides better data structures and programming constructs. Tcl has a simplistic syntax which is easy to learn, but it is sometimes difficult to determine how to quote text so that it does what it is supposed to do. Python is also easy to learn, but there is a lot more to learn. On the other hand, one can learn the language in stages: first the language without classes, then classes, then operator overloading.

Python and Tcl are both portable to multiple platforms. Both can be used with Tk, although the Python version is slightly more complicated to use. For Python there are several other GUIs available.

Both Python and Tcl have a sizable user community, although Tcl's is larger than Python's. Since John Ousterhout moved to Sun Microsystems Laboratories, Tcl is a Sun project. Ousterhout has said on various occasions that there will always be a free Tcl interpreter. Python development is organized in the Python Software Activity.

It is hard to say which language offers more or better packages. Both Tcl and Python have active user communities and for both many packages are available. In Tcl, these usually take the form of extensions to Tcl; in Python, extensions are in the form of Python modules, sometimes written in C.

Tcl and Python are both easy to embed in another application. When the Python interpreter is only called upon to interpret strings of Python code, Tcl and Python are comparable in complexity. However, the use of the Python interpreter can be made more efficient by using the low-level Python calls.

9. References

1
John K. Ousterhout, Tcl and the Tk Toolkit, Professional Computing Series. Addison-Wesley, Reading Mass. and London, April 1994.
2
John Ousterhout, Re: Why you should not use Tcl. Usenet message <367307$1un@engnews2.Eng.Sun.COM>, September 1994.
3
Brent Welch, Practical Programming in Tcl and Tk. Prentice Hall.
4
Guido van Rossum, Python Reference Manual. Report CS-R9525, CWI, Amsterdam, April 1995.
5
Guido van Rossum, Python Library Reference. Report CS-R9524, CWI, Amsterdam, April 1995.
6
Guido van Rossum, Extending and Embedding the Python Interpreter. Report CS-R9527, CWI, Amsterdam, April 1995.
7
Sjoerd Mullender, X Python Reference Manual. Report CS-R9541, CWI, Amsterdam, June 1995.
8
Adam Sah, Tcl Considered Harmful. <URL:ftp://ginsberg.cs.berkely.edu/pub/papers/asah/tcl-fear.html>, 1994-5.
9
Kenneth Manheimer, Re: Opinions? Tcl Python Perl, Virtues/failings. Usenet message <klmznfvzgxae68b@coil.nist.gov>, March 1995.