Not too long ago, I helped someone with some research work, and one of the research tools was also used from the Python interactive prompt.
The interface used was handy enough for development: intuitive, readable, short, and useful. Still, for interactive research it wasn’t handy enough.
To improve the script, I decided to use a trick I heard about a long time ago – using __repr__:
class NoParens(object): def __init__(self, obj): self.obj = obj def __call__(self, *args, **kwargs): return self.obj(*args, **kwargs) def __repr__(self): result = self.obj() return str(result) |
And using it looks like this:
In [2]: import noparens In [3]: def test(): ...: return 4**4 ...: In [4]: t = noparens.NoParens(test) In [5]: t Out[5]: 256 In [6]: t,t,t Out[6]: (256, 256, 256) |
For research consoles that interact with other programs or devices, such as debuggers and sniffers I found this useful, and made work a bit less annoying.
hey man
glad you are back to write posts :)
can you give a more practical example maybe?
Thanks :)
The best example would be a debugger:
Given a single_step() function, use s as a shorthand, and maybe r for run(), and p for print_state().
Given these, debugging with a Python interactive prompt gets a little bit easier. Especially when you can do:
s,s,s,p,r
Why not just use %autocall from the IPython magic commands? You wouldn’t be able to do everything that you can do with your trick (just calling “t” would work the same, but not “t,t,t”), but it would require a whole lot less effort.
Knorby:
Two reasons:
1. I didn’t know about it, thanks :)
2. Even if I had, I couldn’t have used it. I was using an embedded Python shell, that doesn’t work with IPython. Although integrating it is possible, that work wasn’t done.
That made using this shell even more painful, because I didn’t have all the fancy IPython features, such as tab-completion. That pain made this trick even more useful than it normally is.
Could work nicely as a decorator too:
def noparens(func):
class ReprFunc(object):
def __init__(self, f):
self.f = f
def __repr__(self):
return str(self.f())
def __call__(self, *args, **kwargs):
return self.f(*args, **kwargs)
return ReprFunc(func)
Usage:
@noparens
def foo():
return 6*3
>>> foo
18
hmarr:
Nice idea!
However, to do what you propose you don’t need to change the code of NoParens, you can already use it as is. Any callable object may be used as a decorator.
Is this your script’ purpose as I think? like below:
>>> def test():
… return 4**4
…
>>> t = test()
>>> t
256
>>>
llr:
No, it isn’t.
In your code snippet, t holds the result of a single call to test().
However, in the interactive prompt when you do
>>> x [enter]
x.__repr__() gets called.
So, in my version, each time you try to view t, you actually call NoParens.__repr__, and that means a new call to test. I believe this would be clearer if test() returned time.time():
[python]
In [2]: import noparens
In [3]: import time
In [4]: def test():
…: return time.time()
…:
In [5]: t = noparens.NoParens(test)
In [6]: t
Out[6]: 1228124415.74
In [7]: t
Out[7]: 1228124416.37
[/python]
lorg:
Of course! Clearly I didn’t have my head screwed on properly when I wrote that!