Ian Bicking: the old part of his blog

Self and Prototype-based Programming

When comparing languages, I often feel that Python is closer to Self than Smalltalk. I was reminded of that again with this blog entry by Hans Nowak. Python has classes, but Self had classes too, even if they weren't part of the core language. Python classes are just a pattern of object creation, and a syntax to go with it. Python is entirely concrete. Classes aren't strongly named (at least not enough to allow reloading). They aren't global. They have a special syntax for definition, but none for instantiation. Python almost goes out of its way to keep you from overemphasizing instantiation by making it an innocuous call, identical to a function or method call.

So it's not that hard to do Self-like things in Python. A simple pattern I've been using feels this way to me:

class SelfLike(object):

    def __init__(self, **kw):
        for name, value in kw.items():
            setattr(self, name, value)

    def __call__(self, **kw):
        if not kw: return self
        newdict = self.__dict__.copy()
        newdict.update(kw)
        return self.__class__(**newdict)

Then, when using SelfLike objects I usually call them right away, so that classes can be used in the place of instances. A metaclass solution would probably be even more elegant, but I haven't figured out quite how that would work -- the metaclass could return an instance, but how could you inherit from an instance?

I've been using this pattern in places where people often use configuration files. It allows both a brief or longwinded way of specifying some interface, for instance. For instance (taken from FormEncode:

class FullName(Schema):
    firstName = String(notEmpty=True)
    lastName = String(notEmpty=True)
    mi = String(maxLength=1)

# Equivalent to:
NotEmpty = String(notEmpty=True)
FullName = Schema(firstName=NotEmpty, lastName=NotEmpty,
                  mi=String(maxLength=1))

# Equivalent to:
class FullName(Schema):
    class firstName(String):
        notEmpty = True
    class lastName(String):
        notEmpty = True
    class mi(String):
        maxLength = 1

Especially as these become nested (e.g., an address that includes a FullName entry) the class syntax can be a useful way to organize all the settings. But you can also express things very briefly, like age = Int. And specifying a single field can turn into a category of specifications that can be reused and further specialized, like NotEmpty.

Unfortunately, the simple SelfLike pattern doesn't quite live up to this. You can't inherit from NotEmpty in this example, because it is an instance, not a subclass of String. Perhaps this calls for a metaclass that implemented __call__ to create a subclass instead of creating an instance, and then turns all methods into class methods [code]:

from types import MethodType

class SelfLikeMeta(type):

    def __call__(cls, **kw):
        newdict = cls.__dict__.copy()
        newdict.update(kw)
        return SelfLikeMeta('Anonymous%s' % cls.__name__,
                            (cls,), newdict)

    def __getattribute__(cls, attr):
        v = type.__getattribute__(cls, attr)
        if type(v) is MethodType:
            # Here we turn instance methods to class methods:
            def newv(*args, **kw):
                return v.im_func(cls, *args, **kw)
            return newv
        return v

class Test(object):
    __metaclass__ = SelfLikeMeta
    value1 = 'from Test'
Test2 = Test(value1='from Test2')
# Doesn't have a real name, unfortunately:
print repr(Test2)
# >>>
class Test3(Test2):
    def returnValue(self):
        return self.value1
print Test.value1
# >>> from Test
print Test2.value1
# >>> from Test2
print Test3.returnValue()
# >>> from Test2

Unfortunately it breaks some new-style features, like properties. It will take some thought to figure out just how those features should interact with this (distorted) class/instance fuzzy system.

Created 25 Sep '03
Modified 14 Dec '04

Comments:

Yes, python looks more like prototype based language.
But, Guido said it's not a prototype based language.
#

I've had a lot of fun with Io, a prototype-oriented language that reminds me of Self on its good days; I would love to use a more prototype-based metaphor for programming in Python, but Python really hasn't had any good facilities for writing anonymous functions for assignment. This makes it difficult to incrementally expand the functionality of a prototype.

Simply copying the dictionary from one object to another also won't permit a derived instance from picking up the addition of new attributes to its prototype. An override of the getattr method that would check the local dictionary for an item, then proceed to checking the prototype, would be a bit better for this.
# Scott Dunlop

Yes, but how do you FEEL?

# Abby

Scott: in some new thinking along these lines I've used classes instead of instances, and __call__ creates a new anonymous class. This way you get the parent/child relationship. I haven't tested the performance of this -- it could mean a lot of new classes, and I don't know how fast that is compared to instance creation.

The other issue with using classes is that you can't really use them. You really want to use a singleton associated with the class (which I create upon class creation). Unfortunately this is annoying. Maybe I could put a __getattr__ in the metaclass and delegate all the calls to the metaclass to the instance, but I don't know how well that will work.
# Ian Bicking