Simple prototyping in Python

Discussion in 'Python' started by Dave Benjamin, Apr 30, 2004.

  1. 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

    --
    ..:[ dave benjamin: ramen/[sp00] -:- spoomusic.com -:- ramenfest.com ]:.
    : please talk to your son or daughter about parametric polymorphism. :
     
    Dave Benjamin, Apr 30, 2004
    #1
    1. Advertising

  2. Dave Benjamin

    has Guest

    Dave Benjamin <> wrote in message news:<>...
    > 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?
     
    has, Apr 30, 2004
    #2
    1. Advertising

  3. In article <>, has wrote:
    > Dave Benjamin <> wrote in message news:<>...
    >> 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.


    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.

    >> 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?


    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.

    --
    ..:[ dave benjamin: ramen/[sp00] -:- spoomusic.com -:- ramenfest.com ]:.
    : please talk to your son or daughter about parametric polymorphism. :
     
    Dave Benjamin, Apr 30, 2004
    #3
  4. > Dave Benjamin wrote:
    > > JavaScript has a syntax for this:
    > >
    > > var o = {a: 5, b: 6}


    has wrote:
    > 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
     
    Michael Geary, Apr 30, 2004
    #4
  5. In article <>, Michael Geary wrote:
    > 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

    >>> o = f()
    >>> o.a

    5
    >>> o.incr()

    6
    >>> o.incr()

    7

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

    --
    ..:[ dave benjamin: ramen/[sp00] -:- spoomusic.com -:- ramenfest.com ]:.
    : please talk to your son or daughter about parametric polymorphism. :
     
    Dave Benjamin, Apr 30, 2004
    #5
  6. Dave Benjamin

    Greg Ewing Guest

    Dave Benjamin wrote:
    > 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.

    --
    Greg Ewing, Computer Science Dept,
    University of Canterbury,
    Christchurch, New Zealand
    http://www.cosc.canterbury.ac.nz/~greg
     
    Greg Ewing, May 3, 2004
    #6
  7. In article <c74830$hrfbu$-berlin.de>, Greg Ewing wrote:
    > Dave Benjamin wrote:
    >> 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.

    --
    ..:[ dave benjamin: ramen/[sp00] -:- spoomusic.com -:- ramenfest.com ]:.
    : please talk to your son or daughter about parametric polymorphism. :
     
    Dave Benjamin, May 3, 2004
    #7
  8. On Mon, 03 May 2004 02:31:53 -0000, Dave Benjamin <> wrote:

    >In article <c74830$hrfbu$-berlin.de>, Greg Ewing wrote:
    >> Dave Benjamin wrote:
    >>> 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 ;-)

    >>> o=type('',(),{'incr':lambda s: setattr(s,'a',getattr(s,'a',5)+1) or getattr(s,'a')})()
    >>> o.incr()

    6
    >>> o.incr()

    7
    >>> o.incr()

    8

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

    >>> o=type('',(),{'a':5,'incr':lambda s: setattr(s,'a',getattr(s,'a')+1) or getattr(s,'a')})()
    >>> o.incr()

    6
    >>> o.incr()

    7
    >>> o.incr()

    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 ;-)


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

    >

    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 ;-)

    >> 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.
    >

    Gotta go.

    Regards,
    Bengt Richter
     
    Bengt Richter, May 3, 2004
    #8
  9. Greg Ewing wrote:
    > 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
     
    Michael Geary, May 4, 2004
    #9
  10. Dave Benjamin

    Greg Ewing Guest

    Michael Geary wrote:
    > Greg Ewing wrote:
    >
    >>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!)

    --
    Greg Ewing, Computer Science Dept,
    University of Canterbury,
    Christchurch, New Zealand
    http://www.cosc.canterbury.ac.nz/~greg
     
    Greg Ewing, May 5, 2004
    #10
  11. > > Greg Ewing wrote:
    > >>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.


    > Michael Geary wrote:
    > > I think part of the problem is the contrived examples that
    > > we all use to illustrate closures.


    Greg wrote:
    > 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
     
    Michael Geary, May 5, 2004
    #11
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Dave Allison
    Replies:
    37
    Views:
    814
    Warren W. Gay VE3WWG
    Mar 4, 2004
  2. Ludovic Brenta
    Replies:
    86
    Views:
    1,415
    Jan C. =?iso-8859-1?Q?Vorbr=FCggen?=
    Feb 19, 2004
  3. rs
    Replies:
    13
    Views:
    517
    Mark Gordon
    Dec 1, 2003
  4. Carl
    Replies:
    9
    Views:
    358
    Fernando Perez
    Jan 6, 2004
  5. Replies:
    10
    Views:
    1,316
    Sebastian \lunar\ Wiesner
    Jun 23, 2008
Loading...

Share This Page