1

I am an engineer and I use a python REPL as an advanced calculator while working. I use the "previous output" feature of the REPL, which is _, often.

>>>45*0.344
15.48
>>>_*2
30.96

Something like that.

For some reason I find it kind of a chore to type the underscore though. I've used the Haskell REPL, which uses "it" as the previous output variable, which I find easier to type.

I learned that iPython lets you define macros that execute some code. So I popped open an iPython shell and defined a %macro called "it" that mimics the "_".

In [1]: _
Out[1]: ''

In [2]: %macro it 1
Macro `it` created. To execute, type its name (without quotes).
=== Macro contents: ===
_

I can call "it" like so:

In [1]: 4
Out[1]: 4

In [2]: it
Out[2]: 4

However, when I try to do something with "it" like it*4, I get the error unsupported operand type(s) for *: 'Macro' and 'int'.

I want "it" to be the type it's returning instead of the type Macro, so that I can use it just like I use "_".

rspears69
  • 435
  • 1
  • 7
  • 19

2 Answers2

1

Instead of going the macro route you could also change the code where _ gets assigned. sys.displayhook does this and can be changed:

The display of these values can be customized by assigning another one-argument function to sys.displayhook.

So we can simply save in __builtins__.it or whatever name you like:

import sys
ipython_displayhook = sys.displayhook
def mydisplayhook(value):
    if value is not None:
        __builtins__.it = value
    return ipython_displayhook(value)
sys.displayhook = mydisplayhook

This works in the normal python shell, but somehow ipython prevents with tampering with sys.displayhook. The code in ipython that handles the displayhook is in IPython.core.displayhook.DisplayHook so we can monkey patch that:

import IPython.core.displayhook
__call__ = IPython.core.displayhook.DisplayHook.__call__

def mycall(self, value):
    if value is not None:
        __builtins__.it = value
    __call__(self, value)

IPython.core.displayhook.DisplayHook.__call__ = mycall

This is a bit hacky. Things may break, but after testing this for 5 seconds it seems that it works.

You probably don't want to type that everytime you start ipython, but we can automate that. This answer says you can simply put a file into ~/.ipython/profile_default/startup/ which gets then executed everytime.

syntonym
  • 7,134
  • 2
  • 32
  • 45
  • I like this idea but, a) should be displayhook not displayhandler, but I changed that, and b) I'm getting a NameError that "it" is not defined. And I'm not quite good enough at python to figure out exactly what's wrong. – rspears69 Nov 20 '17 at 18:15
  • Somehow mixed "handler" and "hook", sorry. I tested in the normal python shell, and there this solution actually works. IPython though somehow prevents changing the displayhook, but through monkeypatching we can still achieve what we want. See my edit. – syntonym Nov 20 '17 at 19:42
0

When I try that I get

In [329]: it.value
Out[329]: '_\n'

The macro includes the '\n'. So a stand alone it behaves ok. But it can't be used in-line.

hpaulj
  • 221,503
  • 14
  • 230
  • 353