[Tutor] Importing from classes or functions

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Thu Oct 30 18:09:42 EST 2003



On Thu, 30 Oct 2003, Daniel Ehrenberg wrote:

> I'm trying to write a module to make it so that you can use Ruby-style
> methods on various types in Python (similar to the methods on strings,
> but in Ruby, *every* method that would be builtin or in the standard
> library is callable from that variable). I'm starting with an integer
> where, if you call the sin() function, it returns the sine of the
> number. I called this class (for right now) sinnum. So sinnum(5).sin()
> should return -0.95892427466313845.


Hi Daniel,


Yes, it's possible to do something like this, if we do some attribute
lookup trickery:

###
>>> class sinnum(int):
...     def __getattr__(self, attr):
...         def f(*args):
...             return apply(getattr(__builtins__, attr),
...                          (self,) + args)
...         return f
...
>>> x = sinnum(5)
>>> x.abs()
5
>>> x = sinnum(-5)
>>> x.abs()
5
###


So it's sorta working.  But this isn't quite right, though:

###
>>> x.abs().abs()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: 'int' object has no attribute 'abs'
###

The error here shows that the definition above isn't providing "closure",
in the sense that the value of 'x.abs()' isn't a Ruby-style "sinnum", so
we can't compose two operations, like:

    x.abs.abs()


(Tangent note: Terminology is a funny thing, and the term "closure" is
also used to describe something totally different in computer science: in
computer science terms, that embedded 'f' function that's being returned
by the __getattr__() is an example of a "closure" function.  So the bug
here is that the closure function isn't providing mathematical closure.
*grin*)


Anyway, once we see this, we can fix this problem:

###
>>> class sinnum(int):
...     def __getattr__(self, attr):
...         def f(*args):
...             return sinnum(apply(getattr(__builtins__, attr),
...                                 (self,) + args))
...         return f
...
>>> x = sinnum(42)
>>> x.pow(2).pow(2)
3111696
###




> I wanted to import it within the class, since when I'm finished, the
> module should contain more than one datatype (an extended int, an
> extended float, an extended string, etc). But when I try to impliment
> importing within the class, the import doesn't work.

Think of 'import math' as a statement that creates a local variable called
'math'.  Then the error message should be less mysterious:


> >>> class sinnum(int):
> ...     def __init__(s, number):
> ...         import math
> ...         s.num = number
> ...     def sin(s):
> ...         return math.sin(s)
> ...
> >>> x = sinnum(5)
> >>> x.sin()
> Traceback (most recent call last):
>   File "<input>", line 1, in ?
>   File "<input>", line 6, in sin
> NameError: global name 'math' is not defined


'math' here is a local variable within __init__, so it won't be visible
from the 'sin()' function.  So the error is actually not too serious: it's
a scope issue.



> If Ruby-style methods for Python is a bad idea or would be extremely
> inefficient, please tell me.

To tell the truth, I think it might be a bad idea in Python.  *grin*


It's probably not outrageously inefficient --- all Python method access is
done through attribute lookup anyway --- but it's just something that most
Python programmers won't recognize at first.



Good luck to you!




More information about the Tutor mailing list