[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