Prothon Prototypes vs Python Classes
Joe Mason
joe at notcharles.ca
Sat Mar 27 22:37:25 EST 2004
In article <569c605ld1cj8fc1emolk08ete0s1prls1 at 4ax.com>, David MacQuigg wrote:
> Playing with Prothon today, I am fascinated by the idea of eliminating
> classes in Python. I'm trying to figure out what fundamental benefit
> there is to having classes. Is all this complexity unecessary?
Complexity's ok if it's in the right place - down deep in the heart of
things where you only have to get it right once. If you simplify the
language, it can push the complexity out to the API level where
everybody has to deal with it. (However, see
http://www.ai.mit.edu/docs/articles/good-news/subsection3.2.1.html for a
counterargument.)
Ed Suominen just posted a situation where classes and instances need to
be treated separately. He has a class, "AutoText", with various
subclasses (which I'll call "AutoA", "AutoB", etc. because I forget the
details). He wants to notice whenever somebody creates a subclass of
AutoText and add it to a list so that he can have an AutoAll function
which creates just one instance of each. (Or something like that.)
# all examples below will use the same AutoAll()
autoall = []
def AutoAll():
"Return an instance of each 'class'"
l = []
for i in autoall:
l.append(i())
return l
In Python, the easiest way to do this is with metaclasses, which is a
really advanced topic. In Prothon, it's simple to "notice" when a
subclass is created, because __init__ gets called! So you just say:
# This is library code - complexity goes here
AutoText = Base()
with AutoText:
def .__init__():
Base.__init__()
autoall.append(self)
# Users of the library create new subclasses
AutoA = AutoText()
AutoB = AutoText()
with AutoB:
def .__init__(someparams):
# do stuff with someparams
AutoText.__init__()
The problem with this is that it doesn't work. In AutoAll(), when you
call i() and create a new instance of AutoText, it adds it to autoall,
so now you have an infinite loop as autoall keeps growing.
So you need to build in some way to distinguish classes to add to
autoall from ones you don't. Say, a register() method:
# library code
AutoText = Base()
with AutoText:
# __init__ no longer needed
def .register():
autoall.append(self)
# user code
AutoA = AutoText()
with AutoA: .register()
AutoB = AutoText()
with AutoB:
def .__init__(someparams):
# do stuff with someparams
AutoText.__init__()
.register()
That's not all that bad, really - if you ignore metaclasses, the only
way I can think of to do this in standard Python is the same, since you
only have a hook when an object is created, not a class:
# library code
class AutoText(Base):
def register(klass):
autoall.append(klass)
register = classmethod(register)
AutoText.register()
# user code
class AutoA(AutoText): pass
AutoA.register()
class AutoB(AutoText):
def __init__(self, someparams):
# do stuff with someparams
AutoText.__init__(self)
AutoB.register()
But with metaclasses, you can set a metaclass on AutoText which
automatically registers each subclass as it's created, so that the
explicit register() method can be skipped.
# library code - the most complex yet
class AutoRegister(type):
def __init__(klass, name, bases, dict):
autoall.append(klass)
class AutoText(Base):
__metaclass__ = AutoRegister
def __init__(self, text):
Base.__init__(self, tet)
# user code - the simplist yet
class AutoA(AutoText): pass
class AutoB(AutoText):
def __init__(self, someparams):
AutoText.__init__(self)
So here's one point where the simplification of prototypes actually ends
up making the user code more complicated than the full Python version.
As a user of that code, you have no reason at all to care about
__metaclass__ and what it does - you just need to know that AutoAll()
magically creates an instance of AutoText and all it's subclasses.
If you ever need to actually look at the definition of AutoText, though,
you'll see this __metaclass__ thing which is a little harder to
understand. In the simpler version, the basics of the library are
easier, which means if you have to debug it you'll have an easier time,
but you have to keep calling register(). Which complexity is better is
a matter of philosophy. (In this particular case, I think they're both
pretty good tradeoffs.)
This is obviously pretty specific to Python - most prototype based OO
languages don't have the option of metaclasses. But someone else said
that prototype-based languages "tend to grow features that mimic
class-based ones", or soemthing like that. I think this is a good
example.
Joe
More information about the Python-list
mailing list