Comparing Python to Other Languages
Comparing Python to Other Languages
Disclaimer: This essay was written sometime in 1997. It shows its age. It is retained here merely as a historical artifact. --Guido van Rossum
Python programs are generally expected to run slower than Java programs, but they also take much less time to develop. Python programs are typically 3-5 times shorter than equivalent Java programs. This difference can be attributed to Python's built-in high-level data types and its dynamic typing. For example, a Python programmer wastes no time declaring the types of arguments or variables, and Python's powerful polymorphic list and dictionary types, for which rich syntactic support is built straight into the language, find a use in almost every Python program. Because of the run-time typing, Python's run time must work harder than Java's. For example, when evaluating the expression a+b, it must first inspect the objects a and b to find out their type, which is not known at compile time. It then invokes the appropriate addition operation, which may be an overloaded user-defined method. Java, on the other hand, can perform an efficient integer or floating point addition, but requires variable declarations for a and b, and does not allow overloading of the + operator for instances of user-defined classes.
For these reasons, Python is much better suited as a "glue" language, while Java is better characterized as a low-level implementation language. In fact, the two together make an excellent combination. Components can be developed in Java and combined to form applications in Python; Python can also be used to prototype components until their design can be "hardened" in a Java implementation. To support this type of development, a Python implementation written in Java is under development, which allows calling Python code from Java and vice versa. In this implementation, Python source code is translated to Java bytecode (with help from a run-time library to support Python's dynamic semantics).
Python and Perl come from a similar background (Unix scripting, which both have long outgrown), and sport many similar features, but have a different philosophy. Perl emphasizes support for common application-oriented tasks, e.g. by having built-in regular expressions, file scanning and report generating features. Python emphasizes support for common programming methodologies such as data structure design and object-oriented programming, and encourages programmers to write readable (and thus maintainable) code by providing an elegant but not overly cryptic notation. As a consequence, Python comes close to Perl but rarely beats it in its original application domain; however Python has an applicability well beyond Perl's niche.
Like Python, Tcl is usable as an application extension language, as well as a stand-alone programming language. However, Tcl, which traditionally stores all data as strings, is weak on data structures, and executes typical code much slower than Python. Tcl also lacks features needed for writing large programs, such as modular namespaces. Thus, while a "typical" large application using Tcl usually contains Tcl extensions written in C or C++ that are specific to that application, an equivalent Python application can often be written in "pure Python". Of course, pure Python development is much quicker than having to write and debug a C or C++ component. It has been said that Tcl's one redeeming quality is the Tk toolkit. Python has adopted an interface to Tk as its standard GUI component library.
Tcl 8.0 addresses the speed issuse by providing a bytecode compiler with limited data type support, and adds namespaces. However, it is still a much more cumbersome programming language.
Perhaps the biggest difference between Python and Smalltalk is Python's more "mainstream" syntax, which gives it a leg up on programmer training. Like Smalltalk, Python has dynamic typing and binding, and everything in Python is an object. However, Python distinguishes built-in object types from user-defined classes, and currently doesn't allow inheritance from built-in types. Smalltalk's standard library of collection data types is more refined, while Python's library has more facilities for dealing with Internet and WWW realities such as email, HTML and FTP.
Python has a different philosophy regarding the development environment and distribution of code. Where Smalltalk traditionally has a monolithic "system image" which comprises both the environment and the user's program, Python stores both standard modules and user modules in individual files which can easily be rearranged or distributed outside the system. One consequence is that there is more than one option for attaching a Graphical User Interface (GUI) to a Python program, since the GUI is not built into the system.
Almost everything said for Java also applies for C++, just more so: where Python code is typically 3-5 times shorter than equivalent Java code, it is often 5-10 times shorter than equivalent C++ code! Anecdotal evidence suggests that one Python programmer can finish in two months what two C++ programmers can't complete in a year. Python shines as a glue language, used to combine components written in C++.
Common Lisp and Scheme
These languages are close to Python in their dynamic semantics, but so different in their approach to syntax that a comparison becomes almost a religious argument: is Lisp's lack of syntax an advantage or a disadvantage? It should be noted that Python has introspective capabilities similar to those of Lisp, and Python programs can construct and execute program fragments on the fly. Usually, real-world properties are decisive: Common Lisp is big (in every sense), and the Scheme world is fragmented between many incompatible versions, where Python has a single, free, compact implementation.
Programming with Class in Racket
Matthias Felleisen, Robert Bruce Findler
While Racket originated in the functional world of programming languages, it also is a full-fledged class-based, object-oriented programming language. Indeed, it is a singularly powerful one. A Racket program can extend a class at run-time, pass a class to a function, return a class from a method, store a class in a data structure, retrieve it from there. In short, Racket programs may manipulate classes as if they were ordinary valuesThis essay uses the word “value” in the sense of programming language semantics. Numbers, strings, structures, objects, and functions are examples. In particular, the use of “value” is unrelated to the (im)mutability of the entity. and compose and modify their class hierarchies at run-time.
The key innovation is Racket’s class expression, which evaluates to a class value. For example,
evaluates to a class value that comes with one field specification and one method definition. Like all other values, Racket provides a number of basic operations that programs can use to manipulate class values: instantiation, extension, reflection. But because class values have the same status as numbers, structures, and closures, programmers can also define methods that operate on classes.
This essay sketches how Racket programmers use classes with an emphasis on first-class classes. For illustrative purposes, it shows how to implement big-bang, a 2D graphics framework for teaching novice students design principles. The last section sketches how DrRacket itself uses mixins in its startup sequence.
The essay expects that the reader has programmed with classes in some language and ideally to have used classes to build a GUI programs. It does not assume familiarity with functional programming, functional GUI programming or big-bang; indeed it explains only what is absolutely necessary about this background to motivate the example, starting in the next section.
This next section and the third one introduce plain class programming in Racket. Classes are Values explains how Racket classes are more powerful than Java classes. Classes in Action shows how to elegantly solve a serious performance problem using first-class classes. The last section sketches another application of first-class classes in the Racket code base.
Acknowledgments We thank Stephen Chang and Matthew Flatt for comments on an early draft. Matthew is also the primary designer and implementer of Racket’s class system.
In the context of a course on programming with algebra,How to Design Programs [2nd ed] a teacher may ask a programming novice to solve the following problem:
Your game needs a circle that shrinks every time the player manipulates the mouse or presses a key. Design a program that illustrates this behavior.
Using the 2htdp/universe and 2htdp/image libraries, the student may come up with the main function in the left column:
(require 2htdp/image) (require 2htdp/universe) ; type State is Nat (define (main) (big-bang ; initial state: 10 [to-draw render 220 220] [on-mouse less1] [on-key less1]))
(require 2htdp/image) ; type State is Nat (define (main) (send (new world% [state0 10] [to-draw render] [width 220] [height 220] [on-mouse less1] [on-key less1]) start))
Roughly speaking, big-bang is a state-machine. The programmer specifies an initial state, a bunch of event handlers, and a function that renders the current state as an image. Each event handler accepts big-bang’s current state plus event-specific information; it returns a new state, which big-bang squirrels away. Here the initial state is 10, and as figure 4 shows, both the mouse and the key event handler subtract 1 from the current state, regardless of what event they process. The to-draw clause specifies render as the function that expresses big-bang’s current state as an image and that this images is supposed to be displayed on a 220 x 220 pixels canvas.
The 2htdp/universe library compiles the student’s program to (roughly) the code in the right column. Since Racket’s GUI API relies on the class system, the main function creates an instance of a class called world%,By convention, a variable with a name ending in % stands for a class. handing along big-bang’s keyword arguments (after some parsing) as keyword arguments to new; when the object is created and initialized, main calls the object’s start method.
Figure 4: Rendering and event-handling functions
The rest of this essay explains how the world% class functions. What the interested reader has to know about the big-bang mechanics is that while the initial state value and the to-draw clauses are mandatory, the on-mouse and on-key clause are optional.Another optional big-bang clause is on-tick, which runs a handler every time the clock ticks. If a student omits either clause, main does not handle mouse clicks or key presses. In contrast, the to-draw clause is mandatory and so is the specification of an initial state. The programmer may pick any set of values as the state space; students are expected to specify the state space in a comment, such as the one in the 2-column code sample above.
2Classes in Racket
At first glance, a Racket class resembles its cousins in C#, Java, or most class-based object-oriented programming languages. A program can define a plain class, instantiate it, send messages to the resulting object, and extend the class. Furthermore, Racket’s GUI API employs a classic framework of classes, organized in a hierarchy, expecting a client program to derive class extensions to implement functionality.
2.1Of Classes and Objects
Consider the definition of the world% class in figure 5. It says that the class consists of four pieces:
several fields that require initializations: state0, to-draw, width, height;
two fields that require optional initializations: on-mouse, and on-key;
one public method: start; and
some private fields: frame, visible, and editor-canvas.
Translating this definition into a conventional object-oriented language is straightforward, assuming you know the GUI API of the language because world% is all about managing a graphic user interface.
Figure 5: A plain class in Racket (public part)
Figure 6: A plain class in Racket (private part)
A second look reveals a seemingly minor difference between Racket and other object-oriented languages. While most such language mingle the act of naming a class and defining it, Racket separates the two. Here Racket’s define associates the identifier world% with the value of an expression, which happens to be to a class value. In other contexts, define may associate an identifier with a number, a function, or an object.
The class expression itself starts with a reference to object%, the root of the class hierarchy. A Java programmer may instead write extends Object (or may rely on the default specification). In Racket, this position is actually evaluated and must yield a class value.The immutability of the class value provides additional protection especially when compared to class values in Python, which are really just has tables. The rest of the class expression calls for class features: public or private field definitions, public and private methods definitions, and even arbitrary expressions. As in Java, class features have a fixed shape, and a class is an unchangeable entity for the rest of the program.
When Racket instantiates such a class, it sets up the fields and method definitions, collects the expressions, and evaluates the latter in sequence—as if they had all been written in a constructor or an initialization method. The class system requires that a (super-new) expression shows up somewhere in this sequence;In general, super-new consumes required initial values, but in this case there aren’t any. its evaluation sets up the superclass features that belong to the newly minted object.
Here is a careful deconstruction of this class instantiation used in Functional GUIs:
(define a-world (new world% [state0 10] [to-draw render] [width 220] [height 220] [on-mouse less1] [on-key less1]))
It uses define to associate the identifier a-world with a new instance of the world% class. The resulting object has its six public fields initialized to 10, the render function (from figure 4), 220, 220, and twice the less1 function, respectively.
The interface of a-world supports one method call: start, though a program may also use get-field and set-field! to retrieve and modify the values of fields. Here are some sample interactions with this object:
> (send a-world start)
... see screenshot ...
> (get-field width a-world)
> (set-field! width a-world 440) > (get-field width a-world)
Figure 7: Screen shot
Like many introductions to programming with classes, world% also relies on the world of graphical user interfaces to create an interesting example. As figure 6 shows, the private part of this class sets up three private fields:
frame, which is an instance of the top-level window class with required settings for its initial public fields;
visible, a pasteboard object, which is an editor that allows the explicit positioning of items, including images;
editor-canvas, which establishes the context for editors such as a pasteboard.
The latter two aren’t instances of Racket’s plain pasteboard% and editor-canvas% classes, respectively, but world-specific extensions of these classes.
Racket’s GUI framework is like that of many object-oriented languages. It provides a number of useful base classes with which programmers can create simple GUI programs, but for even moderately interesting interfaces, a programmer must derive a new class from one (or several) of these base classes in order to implement the desired behavior. In the simplest case, the derived class adds behavior to the base class via new public methods.
Figure 8 shows how to implement the world-specific pasteboard editor in this manner. The world-pasteboard% class extends the Racket pasteboard implementation. Hence, the class expression uses pasteboard% instead of object% in the super-class position.
Figure 8: A class extension in Racket
The derived class specifies two initial fields: to-draw and state0. Since pasteboard% does not come with mandatory initial fields, instantiating this world-specific pasteboard class requires just two values as the definition of visible in figure 6 already shows.
Besides the two new initial fields, the derived class adds two public methods to those inherited from its superclass: update! and get. While a statically typed language checks at compile time that these new public methods do not interfere with existing public methods, Racket must enforce this invariant when it evaluates the class expression. Once these checks pass, Racket creates an appropriate class value.
Similarly, Racket checks at run-time that every method call has a corresponding method definition in the targeted object, including for calls to inherited method. Thus, the world-pasteboard% class would use
(send this delete arg ...)
to delete something from the editor via an inherited method. If the superclass were to come without a delete method, Racket would signal a run-time error as it evaluates the send expression.
To accelerate the discovery of “method not found” errors for inherited methods, Racket allows programmers to specify such expectations via inherit clauses. Figure 9, which shows the private part of world-pasteboard%, starts with just such a specification. It says that world-pasteboard% expects six named methods from its superclass, and Racket checks this expectation as it evaluates the class expression. An inherit clause also simplifies the notation for invoking inherited methods; instead of using a send expression, the other class features may call these methods as if they were locally defined:
(delete arg ...)
or the call to lock in figure 9.
Figure 9: A class extension in Racket (private part)
The private part of world-pasteboard% implements reset! and show, the two methods called from the public part of the class. As figure 9 shows, the show method is the workhorse, manipulating the editor with a delicate sequence of actions. While the details are irrelevant for this essay, the interested reader may wish to explore the meaning of these methods in the documentation.
Figure 10: A second class extension in Racket
Figure 10 introduces one more element of Racket’s class language: method overriding. Like C# and unlike Java, Racket demands an explicit override specification for methods. Using this specification, it can check during the evaluation of a class expression whether the given superclass comes with the required public super method; if not, Racket can signal an error during class creation. While this check helps programmers find subtle mistakes early, it again allows Racket to specialize invocations of overridden super methods.
Concretely, figure 10 presents the world-specific canvas class. It extends Racket’s editor-canvas% class with two overriding methods: on-char and on-event. The first deals with key events, invoking the programmer-supplied on-key function on the relevant pieces of the data representation of a char event. The second processes mouse events, again with a programmer-supplied on-mouse function that receives only the relevant parts of a mouse event.
Use the complete code these world classes for to experiment with class-based object-oriented programming in Racket.
3Classes are Values
As indicated in the preceding section,
does not define a class in the sense of C++, Java or C#; it creates a class value. Technically, class is keyword that marks an expression just like begin, let, or send are keywords that mark other Racket expressions. When Racket encounters an expression, it determines its value, and that value flows into the context. Since the preceding sections use class in conjunction with define,
it is easy to imagine that class definitions in Racket have a cumbersome syntax but are otherwise like those in conventional class-based object-oriented languages.
Introducing classes in this way helps carry over intuition from traditional languages to Racket. A novice can initially ignore the long-winded define & class syntax and move on. In order to appreciate the full power of Racket’s class system, however, a programmer must eventually understand classes as values and the operations that manipulate classes. The first subsection extends ordinary operations on classes to run-time class values, the second one sketches a simple example of exploiting their power via so-called mixins, and the last one introduces one example of a reflective operation.
3.1Operations on Classes
Phrased in terms of operations on values, every programmer thinks of class instantiation and class extension, two widely used operations on classes. From a purely syntactic perspective, the key difference between Racket and conventional languages is that neither of these operations expects a class name but a class value to instantiate classes or create new ones.
Given this explanation, it is easy to see how