Tuesday, December 01, 2009

Symbols in Python

Wikipedia defines a Symbol (as used in Lisp):

A symbol in the programming language Lisp is a primitive data structure that has a name. Symbols can be used as identifiers. Symbols are unique in a namespace (called package in Common Lisp). Symbols can be tested for equality with the function EQ. Lisp programs can generate new symbols at runtime. When Lisp reads data that contains textual represented symbols, existing symbols are referenced. If a symbol is unknown, the Lisp reader creates a new symbol.



I'm not overly familiar with Lisp, however I have done some work with Scheme; and an explicit Symbol type is something that I sometimes miss in Python. Do you ever find yourself defining classes to be used as global constants, so that you can use the identity operator? Perhaps something like this:

class START: pass
class QUIT: pass

if something() is QUIT: exit()

Symbols offer a better alternative to this, so I decided to implement a Symbol type in Python, which offered some of the features of a Lisp Symbol.
import sys 

class Symbols(object):
def __init__(self, name):
self.name = name
self._symbols = {}

def __repr__(self):
return "<SYM %s>"%(self.name)

def __getattr__(self, name):
s = self._symbols[name] = self.__class__(name)
self.__dict__[name] = s
return s

def __getitem__(self, name):
return getattr(self, name)

s = sys.modules["symbols"] = Symbols("Root")

How is this module intended to be used? Try this:
>>> import symbols
>>> symbols.START is symbols.QUIT
False
>>> symbols.START is symbols.START
True
>>>
The symbols module contains every possible Symbol, and can be used as simple set of constants... but it can also do more than that.
>>> symbols["START"] is symbols.START
True
>>>
Symbols can be accessed by a string name, which lets you use different characters in a Symbol name, and provides a convenient method for creating new Symbols at runtime.

The last requirement is for Symbols to be unique within their own namespace. Our symbols module can support this too.
>>> symbols.Foo.Bar.Foo is symbols.Foo
False
>>> symbols.Foo.Bar.Foo is symbols.Foo.Bar.Foo
True
>>>
This feature allows you to have an nested set of Symbols which retain their identity wherever they're used in your program.

7 comments:

Michael Hudson-Doyle said...

One of the nice things in lisp is that your program is made of symbols, not strings -- and that would be a bit hard to transplant into Python :-)

k0wax said...

class DotDict(dict):
def __getattr__(self, attr):
return self.get(attr, None)
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__

I use the DotDict for the same purpose.

http://code.google.com/p/gnafu/source/browse/src/xtypes.py

j_king said...

I think your implementation is missing a few things.

On top of identity, symbols can be bound to a function, have a value, and store a plist (essentially in a way, a dictionary).

They're also aware of the package they originate in and can be exported to other packages.

For a full definition according to the Common Lisp spec, see:

http://www.lispworks.com/documentation/HyperSpec/Body/t_symbol.htm#symbol

Matt said...

IMHO defining symbol-like classes, as with START and QUIT above, is a code smell. When I find myself doing this, I look at why those aren't real classes. That is, instead of representing a value that a "master program" uses to switch code paths, if you put the implementation of the different code paths in those classes, your code is cleaner/more object oriented.

Crispin Wellington said...

This is something that has long griped me about python, and I do like your implementation. Simple but also powerful. Well done, Simon.

Krys Lawrence said...

Just FYI, Phillip J. Eby created a SymbolType quite a while a go and it's listed on PyPi:

http://pypi.python.org/pypi/SymbolType

It seems like a pretty complete implementation.

George said...

For identity checks you can use plain objects instead of classes:

START = object()
QUIT = object()

Popular Posts