Gutless classes

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

Gutless classes

Kirby Urner

I'm thinking about how, in Ruby, class definitions are never closed, meaning
you can add methods to a script in various locations.  You don't supersede
the old class def when you reopen it for more stuff.  There's also this idea
of defining a module full of methods and then binding those to a class -- I
haven't really learned this fully yet.

In Python, we have the ability to define methods externally to classes, as
ordinary functions, except we need the implicit 'self' variable as a first
argument.  For example:

 >>> def __init__(self, a,b):
  self.a = a
         self.b = b

 >>> def __repr__(self):
         return 'Duh object (%s, %s)' % (self.a, self.b)

 >>> class Duh(object):
         pass

 >>> Duh.__init__ = __init__
 >>> Duh.__repr__ = __repr__
 >>> o = Duh(1,2)
 >>> o
 Duh object (1, 2)

Or we could do it this way:

 >>> class Duh2(object):
         __init__ = __init__
         __repr__ = __repr__

       
 >>> o = Duh2(3,4)
 >>> o
 Duh object (3, 4)

Why do it this way?  I suppose you might have several classes that all use
some method.  Define the method once, then shove it into more than one
gutless class.

In the above case, I guess there's no reason to use underunder syntax in the
external function name, given the rebinding to a local attribute at runtime.
I.e. we might as well go:

 >>> def representer(self):
         return 'Duh object (%s, %s)' % (self.a, self.b)

 >>> class Duh(object):
         pass

 >>> Duh.__init__ = constructor
 >>> Duh.__repr__ = representer
 >>> o = Duh(9,10)
 Duh object (9, 10)

Kirby


_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Gutless classes

Dethe Elza
You can do the Ruby-style class extension, but it takes a little more  
setup-up in Python.  See this Python Cookbook article for one  
approach.  This works on existing classes, not just "gutless" or stub  
classes.

Extending Classes
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/412717

--Dethe

Art is either plagiarism or revolution. --Paul Gauguin


_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Gutless classes

Kirby Urner
> Extending Classes
> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/412717
>
> --Dethe
>

Thanks for showing me that.

I guess I'm just fascinated by the mutability of classes at runtime even
without __metaclass__ magic, e.g.:


 >>> class Alter(object):  
        "stub class"
   Pass

 >>> def meth1(self): return "Meth 1"   # mindless method 1

 >>> def meth2(self): return "Meth 2"   # I'm mindless too

 >>> class Alter(object):
        pass

 >>> Alter.m1 = meth1
 >>> oa = Alter()  # create objects, go wild
 >>> oa.m1()
 'Meth 1'
 >>> ob = Alter()

Sometime later, rebind the method m1 in the class itself:

 >>> Alter.m1 = meth2
 >>> oa.m1()
 'Meth 2'
 >>> ob.m1()
 'Meth 2'

Honey, the kids seem a little different this morning, don'tcha think?

Pretty lightweight stuff, but "not every language can do it" (tm).

Kirby



_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Gutless classes

david-2
On Thu, Sep 08, 2005 at 03:57:47PM -0700, Kirby Urner wrote:

> > Extending Classes
> > http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/412717
> >
> > --Dethe
> >
>
> Thanks for showing me that.
>
> I guess I'm just fascinated by the mutability of classes at runtime even
> without __metaclass__ magic, e.g.:
>
>
>  >>> class Alter(object):  
>         "stub class"
>    Pass
>
>  >>> def meth1(self): return "Meth 1"   # mindless method 1
>
>  >>> def meth2(self): return "Meth 2"   # I'm mindless too
>
>  >>> class Alter(object):
> pass
>
>  >>> Alter.m1 = meth1
>  >>> oa = Alter()  # create objects, go wild
>  >>> oa.m1()
>  'Meth 1'
>  >>> ob = Alter()
>
> Sometime later, rebind the method m1 in the class itself:
>
>  >>> Alter.m1 = meth2
>  >>> oa.m1()
>  'Meth 2'
>  >>> ob.m1()
>  'Meth 2'

I haven't yet found any need to change the method on a class, but I have
often written code that changes a method on an instance. Here's a class that
"runs" something, but it is an error to run it more than once:

class RunOnce:

    def run(self):
        self.run = _noMoreRunning
        # do other stuff that you only want to happen once

    def _noMoreRunning(self):
        raise Exception("Can only call run() once.")

I prefer the pattern above to the alternative:

class RunOnce:

    def __init__(self):
        self.__has_been_run_before = False

    def run(self):
        if self.__has_been_run_before:
            raise Exception("Can only call run() once.")
        self.__has_been_run_before = True
        # do other stuff that you only want to happen once

David H
_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Gutless classes

Dethe Elza
On 9-Sep-05, at 10:04 AM, David Handy wrote:

> I haven't yet found any need to change the method on a class, but I  
> have
> often written code that changes a method on an instance. Here's a  
> class that
> "runs" something, but it is an error to run it more than once:
>
> class RunOnce:
>
>     def run(self):
>         self.run = _noMoreRunning
>         # do other stuff that you only want to happen once
>
>     def _noMoreRunning(self):
>         raise Exception("Can only call run() once.")
>
> I prefer the pattern above to the alternative:
>
> class RunOnce:
>
>     def __init__(self):
>         self.__has_been_run_before = False
>
>     def run(self):
>         if self.__has_been_run_before:
>             raise Exception("Can only call run() once.")
>         self.__has_been_run_before = True
>         # do other stuff that you only want to happen once
>
> David H
Yes, this is called "trampoline style."  It can make code hard to  
read if it's over-used, but it's quite nice once in a while.  I tend  
to use this style more in Javascript than in Python, but not for any  
obvious reason.

I think the idea of changing a class after it has been defined is  
more for extending the class with additional methods, rather than  
changing the methods it has.  In Objective-C this type of extension  
is called a Category and it is explicitly supported by the language.  
The idea is that sometimes instead of subclassing, all you really  
want to do is add a couple of methods to an existing class.  In  
Objective-C you can only add methods, not properties, but in Python  
you can do both.

--Dethe

"No lesson seems to be so deeply inculcated by experience of life as  
that you should never trust experts.  If you believe doctors, nothing  
is wholesome; if you believe theologians, nothing is innocent; if you  
believe soldiers, nothing is safe."

         --Lord Salisbury, 19th century British prime minister



_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig

smime.p7s (3K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Gutless classes

Kirby Urner
In reply to this post by david-2
> class RunOnce:
>
>     def run(self):
>         self.run = _noMoreRunning
>         # do other stuff that you only want to happen once
>
>     def _noMoreRunning(self):
>         raise Exception("Can only call run() once.")
>

Yeah, this is clever, I like it.  You slam the door from the inside,
interpose a "door closed" next time run() is called -- likely in error.

A mutable runtime strategy might be to force oneself to create and run all
the instances needed, then shut 'em all down at the class level:

 class Runner (object):
    def run(self):
       print "Yo!"

 >>> obja = Runner()
 >>> objb = Runner()
 >>> obja.run()
 Yo!

 >>> def newrun(self):  raise Exception("Sorry, too late")

 >>> Runner.run = newrun
 >>> objb.run()

 Traceback (most recent call last):
   File "<pyshell#60>", line 1, in -toplevel-
     objb.run()
   File "<pyshell#58>", line 1, in newrun
     def newrun(self):  raise Exception("Sorry, too late")
 Exception: Sorry, too late

Kirby


_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Gutless classes

Kirby Urner
In reply to this post by Kirby Urner
Here's another example of defining a class by stuffing an empty shell with
externally defined methods.  It's really just another way of organizing
code, perhaps to bring out some aspect of the knowledge domain.

Here, we're in geometry, talking about how a given polyhedron (tetrahedron,
cube, you name it), when scaled, changes its edge, surface area, and volume.
The scale factor is here applied directly to the edge, i.e. a scale factor
of 3 trebles every edge in size (like zooming in by 3x).  This has 2nd and
3rd powering side effects on area and volume, as these functions make clear:

 IDLE 1.1      
 >>> class Shape (object):  pass

 >>> def build(self, edge = 1, area = 1, volume = 1):
         self.edge = edge
         self.surface = area
         self.volume = volume

       
 >>> def scale(self, factor):
         self.edge *= factor
         self.surface *= factor**2
         self.volume *= factor**3

       
 >>> def reporter(self):
         return "Shape <%s, %s, %s>" % (self.edge, self.surface,
self.volume)

 >>> Shape.__init__ = build
 >>> Shape.__mul__ = scale
 >>> Shape.__repr__ = reporter
 >>> myshape = Shape()
 >>> myshape
 Shape <1, 1, 1>
 >>> myshape * 3
 >>> myshape
 Shape <3, 9, 27>
 >>> myshape * 0.125
 >>> myshape
 Shape <0.375, 0.140625, 0.052734375>

Kirby


_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Gutless classes

Dethe Elza
In reply to this post by Kirby Urner
On 9-Sep-05, at 10:54 AM, Kirby Urner wrote:

> Yeah, this is clever, I like it.  You slam the door from the inside,
> interpose a "door closed" next time run() is called -- likely in  
> error.

It's usful for things where a decision needs to be made *once* rather  
than testing a condition every time the code is run, so instead of:

class MyUsualThing(object):

     def doYourThing(self, x,y,z):
         if isThisAMac():
             # mac code here
         elif isThisWindows():
             # windows code here
         elif isThisLinux():
             # linux code here

you can do:

class MyTrampolinedThing(object):

     def _doMacThing(self, x,y,z):
         # mac code here

     def _doWindowThing(self, x,y,z):
         # windows code here

     def _doLinuxThing(self, x,y,z):
         # linux code here

     def doYourThing(self, x,y,z):
         klas = self.__class__
         if isThisAMac():
             klas.doYourThing = klas._doMacThing
         elif isThisWindows():
             klas.doYourThing = klas._doWindowsThing
         elif isThisLinux():
             klas.doYourThing = klas._doLinuxThing
         del klas._doMacThing
         del klas._doWindowsThing
         del klas._doLinuxThing
         return self.doYourThing(x,y,z)

You could of course define your specific handlers inline in the  
doYourThing method before it is replaced, but I think it's clearer to  
define them seperately and then delete them to clean up the object  
namespace.  As you can see, this could be confusing if you get a  
stacktrace which lists doYourThing, because this method will only  
ever be called once, and after that the doYourThing will be one of  
the other methods.  Still, it avoids a test on subsequent calls and  
possibly a function call or two.

Just remember that premature optimization is the root of all evil.  
If trampolining make the code flow *more* readable, use it.  Or, if  
your code is too slow, and you've profiled it and found that the  
tests are the culprit, then use it.  Otherwise, it can make your  
objects into a jumbled, untraceable mess.

--Dethe

"All spiritual paths have four steps: show up, pay attention, tell the
truth, and don't be attached to the results."  Angeles Arien



_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig

smime.p7s (3K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Gutless classes

Scott David Daniels
Dethe Elza wrote:
   <lots of reasonable stuff on trampolining>

>
> class MyTrampolinedThing(object):
>     ...
>
>     def doYourThing(self, x,y,z):
>         klas = self.__class__
>         if isThisAMac():
>             klas.doYourThing = klas._doMacThing
>         elif isThisWindows():
>             klas.doYourThing = klas._doWindowsThing
>         elif isThisLinux():
>             klas.doYourThing = klas._doLinuxThing

_but_, I have a problem with the next three lines:
>         del klas._doMacThing
>         del klas._doWindowsThing
>         del klas._doLinuxThing

There is no reason to destroy the source of the method,
nor even the other (presumably unused) methods.  The Pythonic
way is to leave them alone with the underscores in front, and
expect programmers not to use them.

>         return self.doYourThing(x,y,z)


--Scott David Daniels
[hidden email]

_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Gutless classes

Catherine Letondal
In reply to this post by Kirby Urner
Hi,

On Sep 8, 2005, at 7:42 PM, Kirby Urner wrote:

>
> I'm thinking about how, in Ruby, class definitions are never closed,
> meaning
> you can add methods to a script in various locations.  You don't
> supersede
> the old class def when you reopen it for more stuff.  There's also
> this idea
> of defining a module full of methods and then binding those to a class
> -- I
> haven't really learned this fully yet.
>
>

A good reason to extend a class at run-time, either by adding a new
method or by redefining an existing one, is for customization by a
knowledgeable end-user of an application (people like you and me are
sometimes end-users of others'programs, aren't they?). This is for
example the case for scientists (I work with biologists): they are not
programmers, but they have sometimes enough programming experience for
such adaptation. When methods are defined as small and modular, it's
very convenient to be able edit them -- for instance to add a
conditional, or to add a method that you are able to call from a GUI
that opens access to the Python interpreter.

[Another language that can extends a class at run-tim is XOtcl
(www.xotcl.org), developped as an extension of MIT Otcl]

Best,

--
Catherine Letondal -- Institut Pasteur
www.pasteur.fr/~letondal

_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig