[Tutor] Importing from classes or functions

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Mon Nov 3 03:26:26 EST 2003


> > > >>> class sinnum(int):
> > > ...     def __getattr__(self, attr):
> > > ...         def f(*args):
> > > ...             return apply(getattr(__builtins__,
> > > attr),
> > > ...                          (self,) + args)
> > > ...         return f
> > > ###
> > >
> > Would you mind explaining that code? I can't understand it, and it
> > doesn't seem to work if you're using namespaces.

Hi Daniel,


Before we start: can you point out which parts are completely unfamiliar
in the example?  There has to be something there that looks sorta
familiar.  *grin*


But there is a lot of advanced stuff in that example, and I don't want to
tackle it all at once.  So this message is going to be a bit long again,
but this time, I'll try to not jump so far.


For the moment, let's talk about __getattr__().  __getattr__ is a special
method --- it's the one we can write to make a sinnum() appear to know how
to do certain things, even if we haven't explicitely written methods for
them.

It might help if we simplify the example a little:

###
import math

class Test(int):
    def __getattr__(self, attr):
        print "Python's asking me to look for", attr
        return 42
###


Whenever we ask for an attribute out of a Test instance, Python will be
forced to resort to Test.__getattr__(), since there's no other attributes
defined in there:

###
>>> t = Test()
>>> t.tea
Python's asking me to look for tea
42
>>> t.answer()
Python's asking me to look for answer
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: 'int' object is not callable
###

The important thing to see is that the function "methods" of an object are
also looked up as attributes.  So when we say:

    t.answer()

Python first grabs the value of 't.answer':

###
>>> t.answer
Python's asking me to look for answer
42
###

and then tries to execute it as if it were a function:

###
>>> 42()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: 'int' object is not callable
###

That's not going to work: if we want something like:

    t.sin()
    t.cos()
    t.floor()

to work, then we can't just return some number like 42.  Instead, we need
to give back a function.

###
>>> import math
>>> math.sin
<built-in function sin>
###


Let's modify the example again and try to get it to return the 'sin'
function:

###
import math

class Test(int):
    def __getattr__(self, attr):
        return math.sin
###


Let's see how this works now:

###
>>> t2 = Test(1)
>>> t2.sin
<built-in function sin>
>>> t2.sin(3.1415)
9.2653589660490258e-05
###

Almost there, but not quite!  We want to be able to say:

    t2.sin()

and have the system magically call:

    math.sin(t2)


We can make this work if with a little bit of magic:

###
import math

class Test(int):
    def __getattr__(self, attr):
        def dynamic_function():
            return math.sin(self)
        return dynamic_function
###

The magic is not that magical: we're dynamically creating a new function
"dynamic_function", whose sole purpose in life is to call math.sin() on
self.


Let's see if this works:

###
>>> t3 = Test(.5)
>>> t3.sin()
0.0
###


Hmmm... well, the syntax is working, but the answer is wrong.  *grin*



Oh!  That's because we've inheriting from 'int' --- we really should be
using float to avoid truncation.

###
import math

class Test(float):
    def __getattr__(self, attr):
        def dynamic_function():
            return math.sin(self)
        return dynamic_function
###


Here it is in action:

###
>>> t4 = Test(0.5)
>>> t4.sin()
0.47942553860420301
###


This still isn't quite right though, since this version of the class
ALWAYS returns something that sin()s, regardless of what kind of function
we're asking it to do:

###
>>> t4.cos()
0.47942553860420301
>>>
>>>
>>> t4.fangoriously()
0.47942553860420301
>>> t4.gelatinous()
0.47942553860420301
>>> t4.linebacker()
0.47942553860420301
###

so it still needs some work.


Please feel free to ask questions on any of this!  Good luck to you.




More information about the Tutor mailing list