Simple prototyping in Python

D

Dave Benjamin

The recent conversation on prototype-based OOP and the Prothon project has
been interesting. I've been playing around with the idea for awhile now,
actually, since I do a lot of programming in JavaScript/ActionScript. I feel
like people are focusing too much on the "prototype chain", which to me is
mainly an attempt at reintroducing inheritance. I almost never use
inheritance anymore, preferring delegation instead in most cases. What I
think is much more interesting about the prototype-based style is the
ability to create and pass around anonymous objects. JavaScript has a syntax
for this:

var o = {a: 5, b: 6}

which is roughly equivalent to Python's:

class Whatever: pass
o = Whatever()
o.a = 5
o.b = 6

If you can tolerate some Lispism, you can actually create anonymous objects
in straight Python. This is liable to get some adverse reactions, but I just
want to demonstrate that it *can* be done. Here's an example:

class obj:
def __init__(self, **kwds):
self.__dict__.update(kwds)

def set(self, name, value):
setattr(self, name, value)

def do(*args):
return args[-1]

def counter(start):
ref = obj(value=start)
return obj(
current = lambda: ref.value,
next = lambda: do(
ref.set('value', ref.value + 1),
ref.value))

c = counter(0)
print c.current()
print c.next()
print c.next()
print c.current()

This outputs:

0
1
2
2
 
H

has

Dave Benjamin said:
The recent conversation on prototype-based OOP and the Prothon project has
been interesting. I've been playing around with the idea for awhile now,
actually, since I do a lot of programming in JavaScript/ActionScript. I feel
like people are focusing too much on the "prototype chain", which to me is
mainly an attempt at reintroducing inheritance.

It doesn't even do that; at least not in the sense that class-based OO
defines it, where inheritance merely describes the initial state for
every object created from a class.

To me, the way Prothon and Io do "prototypes" feels more like a leaky
abstraction, where a common internal optimisation for proto-OO systems
has somehow leaked out into user-space. See if I ever find that
Lieberman fellow, think I'll be having a word or two with 'im. ;)

I almost never use
inheritance anymore, preferring delegation instead in most cases. What I
think is much more interesting about the prototype-based style is the
ability to create and pass around anonymous objects.

In a proto-OO system all objects are _completely_ anonymous*, having
no 'class' and all being of the same type, 'object'. In this respect,
they're just like strings, lists, dicts, or any other 'anonymous'
type; a list is a list is a list, for example, regardless of how it is
used.


(* To the user, anyway. Of course, they still have internal ids so the
runtime can track 'em, but that's not something the user ever needs to
know about.)
JavaScript has a syntax
for this:

var o = {a: 5, b: 6}

Looks on the surface like AppleScript's record type, which is roughly
analogous to C structs... and useless for anything more than
struct-style usage. While I did read about JS programming in the
dim-n-distant past - it was one of a number of languages I looked at
when learning proto-OO on AS, which lacked any decent learning
material on the topic - I've pretty much forgotten everything since.
So can I ask: is the JS structure more flexible; e.g. can one add
functions to it to operate like methods on the other data in the
structure? Or does it have to provide a second structure for proto-OO
use, as AS does?
 
D

Dave Benjamin

It doesn't even do that; at least not in the sense that class-based OO
defines it, where inheritance merely describes the initial state for
every object created from a class.

But that is how it is used, anyway. See any textbook describing OO
programming in JavaScript. The caveats are also typically described, and
other hacks like the "__proto__" attribute (which is the object's prototype
object, not to be confused with the constructor's prototype object,
"prototype") are used to exercise more control (or confusion) over the matter.
In a proto-OO system all objects are _completely_ anonymous*, having
no 'class' and all being of the same type, 'object'. In this respect,
they're just like strings, lists, dicts, or any other 'anonymous'
type; a list is a list is a list, for example, regardless of how it is
used.

Not necessarily. In JavaScript, you can still do "instanceof", for example.
Looks on the surface like AppleScript's record type, which is roughly
analogous to C structs... and useless for anything more than
struct-style usage. While I did read about JS programming in the
dim-n-distant past - it was one of a number of languages I looked at
when learning proto-OO on AS, which lacked any decent learning
material on the topic - I've pretty much forgotten everything since.
So can I ask: is the JS structure more flexible; e.g. can one add
functions to it to operate like methods on the other data in the
structure? Or does it have to provide a second structure for proto-OO
use, as AS does?

Sure! JavaScript supports function literals that are true closures. In fact,
this feature can be used to support private data, which has been described
in some detail by Douglas Crockford, here:

http://www.crockford.com/javascript/private.html

The same technique can be accomplished by Python, though it'd be really nice
to have code blocks or function literals that accept statements.
 
M

Michael Geary

Looks on the surface like AppleScript's record type, which
is roughly analogous to C structs... and useless for anything
more than struct-style usage. While I did read about JS
programming in the dim-n-distant past - it was one of a
number of languages I looked at when learning proto-OO
on AS, which lacked any decent learning material on the
topic - I've pretty much forgotten everything since. So can
I ask: is the JS structure more flexible; e.g. can one add
functions to it to operate like methods on the other data in
the structure? Or does it have to provide a second structure
for proto-OO use, as AS does?

A JavaScript object literal is simply a convenient way to construct an
object. You can put anything in that object that you can put in any other
object.

This object literal:

var o = { a: 5, b: 6 }

is just a shorthand for:

var o = new Object
o.a = 5
o.b = 6

In fact, if you do o.toSource() after either of those, you'll get the same
result:

({a:5, b:6})

As with any other object, you can put functions as well as data in an object
literal, e.g.:

var o =
{
a: 5,
incr: function() { return ++this.a },
}

o.incr() will return 6, 7, 8, etc. if you call it repeatedly.

As Dave mentioned, you can also use closures, as in this example which uses
both a closure and a property in the object literal:

function F()
{
var c = 105;

return {
a: 5,
incr: function() { return [ ++this.a, ++c ] },
}
}

var x = new F
var y = new F
x.incr()
y.incr()

-Mike
 
D

Dave Benjamin

As with any other object, you can put functions as well as data in an object
literal, e.g.:

var o =
{
a: 5,
incr: function() { return ++this.a },
}

o.incr() will return 6, 7, 8, etc. if you call it repeatedly.

Hey, I never knew that "this" could be used inside of an anonymous object
in JavaScript. Thanks for pointing that out!

In Python, you'd have to give the object a name, since there's no "self" to
refer to. For instance (using the "obj" and "do" functions I defined earlier):

def f():
o = obj(a=5, incr=lambda: do(o.set('a', o.a + 1), o.a))
return o
7

Not exactly elegant, but not totally atrocious either...
 
G

Greg Ewing

Dave said:
Hey, I never knew that "this" could be used inside of an anonymous object
in JavaScript. Thanks for pointing that out!

In Python, you'd have to give the object a name, since there's no "self" to
refer to.

No, you wouldn't, because the Python equivalent would
be something like


def F():
c = [105]

class C:
a = 5

def incr(self):
self.a += 1
c[0] += 1
return [self.a, c[0]]

return C()


It's a bit more awkward in Python due to the inability to
directly rebind a name in an outer scope.

BTW, IMO this is a seriously warped technique that I
would never use, even in Javascript. I can't see any
benefit in it that's anywhere near worth the obfuscation.
 
D

Dave Benjamin

Dave said:
Hey, I never knew that "this" could be used inside of an anonymous object
in JavaScript. Thanks for pointing that out!

In Python, you'd have to give the object a name, since there's no "self" to
refer to.

No, you wouldn't, because the Python equivalent would
be something like

def F():
c = [105]

class C:
a = 5

def incr(self):
self.a += 1
c[0] += 1
return [self.a, c[0]]

return C()

I don't know if I'd call it equivalent. Similar in effect, naybe, but the
idea was to create an anonymous, classless object.
It's a bit more awkward in Python due to the inability to
directly rebind a name in an outer scope.

This is an issue, but not a serious one. In my original example, I used a
pretty simple workaround (an anonymous object).
BTW, IMO this is a seriously warped technique that I
would never use, even in Javascript. I can't see any
benefit in it that's anywhere near worth the obfuscation.

One benefit, which applies equally to both languages, is that this technique
gets you closer to enforced private attributes than the standard ways of
making objects. I'm sure you could hack frames or something, but "c" is
pretty much inaccessible to the casual programmer in your example above.
 
B

Bengt Richter

Dave said:
Hey, I never knew that "this" could be used inside of an anonymous object
in JavaScript. Thanks for pointing that out!

In Python, you'd have to give the object a name, since there's no "self" to
refer to.

No, you wouldn't, because the Python equivalent would
be something like

def F():
c = [105]

class C:
a = 5

def incr(self):
self.a += 1
c[0] += 1
return [self.a, c[0]]

return C()

I don't know if I'd call it equivalent. Similar in effect, naybe, but the
idea was to create an anonymous, classless object.

Anonymous follows, though not classless ;-)
8

Or perhaps better, using the class variable as initial value as Greg is doing above:
8

You could also concoct a bound method operating on the variable held in a list
>>> oincr = (lambda s:s.__setitem__(0,s[0]+1) or s[0]).__get__([5], list)
>>> oincr() 6
>>> oincr() 7
>>> oincr()
8

Or perhaps more cleanly, just subclass list and instantiate with initial value:
>>> o = type('',(list,),{'incr':lambda s:s.__setitem__(0,s[0]+1) or s[0]})([5])
>>> o.incr() 6
>>> o.incr()
7

This stuff is fun for exploring ins and outs of the language, but
readable is best, if there's a readable alternative ;-)

Yes, I wouldn't mind more and cleaner control of binding and evaluation,
as to when and where (e.g. read time, def time, call time and
local binding vs find-and-rebind in lexically nested or mro name spaces).
This is an issue, but not a serious one. In my original example, I used a
pretty simple workaround (an anonymous object).
OTOH, if something strikes you as a workaround, it's not something you want
to be struck with regularly ;-)
One benefit, which applies equally to both languages, is that this technique
gets you closer to enforced private attributes than the standard ways of
making objects. I'm sure you could hack frames or something, but "c" is
pretty much inaccessible to the casual programmer in your example above.
Gotta go.

Regards,
Bengt Richter
 
M

Michael Geary

Greg said:
BTW, IMO [the closure] is a seriously warped technique
that I would never use, even in Javascript. I can't see any
benefit in it that's anywhere near worth the obfuscation.

I think part of the problem is the contrived examples that we all use to
illustrate closures. They show the technique, but the closure doesn't
provide any apparent benefit.

At the risk of overstaying my welcome, here is a real life example, a bit of
Acrobat 6 multimedia JavaScript code that uses closures. I may have posted
this before (or maybe I'm thinking of the Prothon list), but this time I
translated the closure code into a version that uses objects and no
closures, so we can compare the two.

Here's what the code does. The reportScriptEvents function opens a media
player and starts it playing, and it provides an event listener that handles
two events, onScript and afterClose. Each time a script event occurs in the
media clip, the onScript event method appends the script event's command
name and parameter to a log string. After the media clip finishes playing,
the afterClose event method takes that accumulated log string and passes it
to a reporter function which was provided in the call to reportScriptEvents.

The testReport function calls reportScriptEvents and provides a reporter
function which displays the accumulated log in an alert box. testReport has
a title parameter which is used for the title of the alert box.

Note that the media player opens and plays asynchronously.
reportScriptEvents and testReport return immediately upon opening the
player, and the event methods and reporter function are called later on, as
the media clip plays and closes.

Here is the code using closures:

function reportScriptEvents( reporter )
{
var log = "";

app.media.openPlayer(
{
events:
{
onScript: function( e )
{
log += e.media.command + ": " + e.media.param + "\n";
},

afterClose: function()
{
reporter( log );
},
},
});
}

function testReport( title )
{
reportScriptEvents( function( log )
{
app.alert({ cTitle: title, cMsg: log });
});
}

Now for a version with no closures, using object properties to hold the
persistent state instead. I had to change the definition of
reportScriptEvents slightly. Instead of using a reporter function, it takes
a reporter object with a report method. In this version, the events object
has reporter and log properties which replace the closure variables in the
previous version, and the testReport function creates a reporter object with
a title property which it passes in to reportScriptEvents.

function reportScriptEvents( reporter )
{
app.media.openPlayer(
{
events:
{
reporter: reporter,
log: "",

onScript: function( e )
{
this.log +=
e.media.command + ": " + e.media.param + "\n";
},

afterClose: function()
{
this.reporter.report( this.log );
},
},
});
}

function testReport( title )
{
reportScriptEvents(
{
title: title,

report: function( log )
{
app.alert({ cTitle: this.title, cMsg: log });
},
});
}

The two versions are fairly similar, but the closure version of the code is
shorter and simpler, and to me it's easier to understand as well. I don't
have to create any objects or properties to hold persistent state. I just
use ordinary variables, and I don't have to think about the fact that the
event methods and reporter function are called long after the
reportScriptEvents and testReport return. The variables just work.

In the object version, I have to create the machinery to hold persistent
state myself, setting up the reporter and log properties in the events
object and the title property in the reporter object.

Sometimes objects and properties are a better solution, but to my eyes the
closure version is the winner in this particular case.

(Sorry to be posting so much JavaScript code in the Python group! :) But
it's a good example of real world code that benefits from using closures.)

-Mike
 
G

Greg Ewing

Michael said:
Greg said:
BTW, IMO [the closure] is a seriously warped technique
that I would never use, even in Javascript. I can't see any
benefit in it that's anywhere near worth the obfuscation.

I think part of the problem is the contrived examples that we all use to
illustrate closures.

Just in case it wasn't clear, I was talking about that
particular use of a closure, i.e. simulating an object
with a private instance variable. I'm well aware that
there are other, better uses for closures.

The example you posted is a reasonable use of
closures (although the in-line-constructed object
passed as an argument to a function made my brain
hurt a bit while trying to parse it!)
 
M

Michael Geary

Greg said:
BTW, IMO [the closure] is a seriously warped technique
that I would never use, even in Javascript. I can't see any
benefit in it that's anywhere near worth the obfuscation.
Just in case it wasn't clear, I was talking about that
particular use of a closure, i.e. simulating an object
with a private instance variable. I'm well aware that
there are other, better uses for closures.

The example you posted is a reasonable use of
closures (although the in-line-constructed object
passed as an argument to a function made my brain
hurt a bit while trying to parse it!)

Ah, roger that. Naturally, it's also possible to write that code by creating
the events object first and then just passing a reference to it into the
function call--it sounds like that would make it easier to read.

-Mike
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top