Python Treasure

The following is our yield from the Python treasure hunt.

Dangling else

The indentation level in Python determines what the else refers to, so there is no ambiguity.

Overloading

Define a function called __add__. C++ would call it operator+; Sather would call it plus.

Reuse

The class can be fixed by using self.__class__ instead of complex.

We will return to the meta-question in the Sather treasure hunt.

Flatten

We noticed a caveat: superflattening is undefined for strings, because Python strings are infinitely nested sequences (this stems from the fact that Python has no character type; perhaps a disadvantage).
def flatten(s):
	r = []
	for elt in s:
		for subelt in elt:
			r.append(subelt)
	return r

# Flatten 1 level, not assuming that s is a sequence of sequences.
def flatten2(s):
	r = []
	for elt in s:
		try:
			for subelt in elt:
				r.append(subelt)
		except TypeError:
			r.append(elt)
	return r


def superflatten(s):
	result = []
	for i in s:
		try:
			result = result + superflatten(i)
		except TypeError:
			result.append(i)
	return result


Nested Scopes

def make_mult(a):
	return lambda b, aa=a: lambda x, aaa=aa, bb=b: aaa*x + bb

Shadow inheritance

The result of the first example is 4. If we had set x.i to 3, the result would be 3, i.e. the assignment is not a no-op. Deleting x.i would then make x.i equal to 4.

Based on this behavior, Python's inheritance model can be described as a chain of responsibility. Instances are initially empty, and pass the buck on to their classes, which may pass the buck on to their superclasses. Assignment always creates a local "shadow", which is removed by del. This is different from most other object-oriented languages, which either (1) do not propagate class changes or (2) share class variables among all instances.

The suggested unification can be implemented by tables which have a programmer-controlled parent pointer. The pointer provides the link in the chain of responsibility. This is the approach taken by our next language, Self.

More scoping

Almost all non-scripting languages use a "local" statement instead of a global statement. That is, all variable writes are global unless a local variable is declared. Awk and Perl follow suit. Python and Tcl instead assume that most variable accesses are local, hence the use of "global" statements.

However, this seems at odds with security, since a typo in an lvalue will go unnoticed. In the non-scripting languages, this error can happen too, but only if there is a matching global, and globals tend to be rare in those languages. (Perhaps for precisely this reason.)

Using assign-global/assign-local interchangeably could be implemented by allowing globals to be renamed within a block which used a local with the same name. Tcl's upvar command allows this; scant other languages support it. upvar is the linguistic analogue of UNIX symbolic links. Tcl also allows you to traverse up the block stack similar to UNIX's "..".

Python not only defaults to local assignment in a block but also in a class. Let's explore the connection between class inheritance and block nesting. An assign-global statement for a class member would amount to skipping this class and performing the assignment in the superclass, which can be done by writing self.__class__.x instead of self.x. Thus we see that classes already have the flexibility we want from blocks. Classes can access their parents, obviating the local/global distinction, while code blocks cannot. Indeed, classes were invented in Simula 67 precisely for the purpose of generalizing lexical blocks. Strange how few languages exploit this.


PLE Home page
Last modified: Tue Jul 9 18:09:36 EDT 1996