Reputation: 149
I defined a function in a Python interactive console (not idle, not ipython) and I needed to see it again many screenfuls later.
Example:
>>> def myf():
... print 1
... print 2
... print 3
...
>>>
>>> # a lot of scrolling
>>>
Fake example output below (instead of "name 'print_function' is not defined") to demonstrate what I want:
>>> print_function(myf)
myf()
print 1
print 2
print 3
Is there anything like print_function in Python? If not, how would you implement it?
Upvotes: 3
Views: 6154
Reputation: 551
This is how I do it:
import inspect as i
import sys
sys.stdout.write(i.getsource(MyFunction))
Prints it out nicely...
Upvotes: 0
Reputation: 5642
There are many ways to do this:
Without any imports:
print myf.func_code.co_code
But you will most probably just end up with bytecode. This can be solved with: http://www.crazy-compilers.com/decompyle/
You can also go the inspect way:
import inspect
import mymodule
print inspect.getsource(mymodule.myf)
Or you can use dis:
import dis
dis.dis(myf)
Lastly, although not recommendable (Security wise) there is this:
funcstring = """def myf():
print 1
print 2
print 3"""
func = eval(funcstring)
func.source = funcstring
print func.source
Upvotes: 0
Reputation: 35247
While I'd generally agree that inspect
is a good answer (as Martijn Pieters mentions), I'd disagree that you can't get the source code of objects defined in the interpreter. If you use dill.source.getsource
from dill
, you can get the source of functions and lambdas, even if they are defined interactively.
It also can get the code for from bound or unbound class methods and functions defined in curries... however, you might not be able to compile that code without the enclosing object's code.
>>> from dill.source import getsource
>>>
>>> def add(x,y):
... return x+y
...
>>> squared = lambda x:x**2
>>>
>>> print getsource(add)
def add(x,y):
return x+y
>>> print getsource(squared)
squared = lambda x:x**2
>>>
>>> class Foo(object):
... def bar(self, x):
... return x*x+x
...
>>> f = Foo()
>>>
>>> print getsource(f.bar)
def bar(self, x):
return x*x+x
>>>
Upvotes: 2
Reputation:
It's a bit hackish but if this is something that you will be doing a lot, you can use the readline
module and a function decorator.
class PrintableFunction(object):
"""A class that contains a function and its start and end points
in the readline history"""
def __init__(self, func, start, end):
self.start = start
self.end = end
self.func = func
def __call__(self, *args):
self.func(*args)
def __str__(self):
"""Just get all the readline history lines from start to end and
return them"""
lines = []
for i in range(self.start, self.end + 1):
lines.append(readline.get_history_item(i))
return "\n".join(lines)
class SavedFunction(object):
"""The actual function decorator. It takes one argument, the history
line that at which the function definition will begin."""
def __init__(self, start):
"""Take the starting line as an argument to the decorator. The end
line is grabbed from the current history after the function has
been entered"""
self.start = start
self.end = readline.get_current_history_length()
def __call__(self, func):
return PrintableFunction(func, self.start, self.end)
You can add these classes to your PYTHONSTARTUP file, so that every time you load an interpreter, you have them available.
>>> @SavedFunction(readline.get_current_history_length() + 1)
... def foo(bar):
... print(bar)
>>> foo(5)
5
>>> print(foo)
def foo(bar):
print(bar)
I've created a custom PS1 for myself (in my PYTHONSTARTUP file as well) that shows the current readline history number, meaning I can just quickly add it to the @saved_function
argument list, which is easier than fetching it with the readline.get_current_history_length
function as above:
[508] @SavedFunction(509)
(509) def foo(bar):
(510) print(bar)
[511] print(foo)
def foo(bar):
print(bar)
Upvotes: 2
Reputation: 114025
IPython has this lovely history lookup. Therefore, if you know the name of your function, you should be able to type in def myFunc(
and press the up arrow key. This will make IPython recall the buffer and display to you the last inputted code that matches this starting. In your case, it will be the definition of your function, including the full function body. So that should do it for you!
Hope this helps
Upvotes: 0
Reputation: 7822
I was suprised when I've learned this but each function has a code method, so you can try to write something like:
>>> def f():
print "hello world"
>>> f.__code__
<code object f at 02C57188, file "<pyshell#5>", line 1>
As you see it just gives you a reference to an object stored in memory, not an exact code of the function. However you can do something like:
>>> def roman():
print "Oh no!"
>>> roman.__code__ = f.__code__
>>> roman()
hello world
>>>
I am not sure about that, but I think that you can also write the code to file and read it from this file later on. You will need to do some research on your own and tinker around with it, hope this helps somehow.
Upvotes: 0
Reputation: 5381
How formal do you want to be about this? If your response was something to the effect of "not very", then you could just copy the code of a function into the docstring and then print the docstring using doc
For example:
def foo():
""" Prints a sequence of numbers to the terminal:
print 1
print 2
"""
print 1
print 2
print foo()
--> 1
--> 2
print foo.__doc__
--> Prints a sequence of numbers to the terminal:
--> print 1
--> print 2
Upvotes: 0
Reputation: 1123420
You cannot do this in the regular console. iPython keeps a copy of the source in case you want to see it again later on, but the standard Python console does not.
Had you imported the function from a file, you could have used inspect.getsource()
:
>>> import os.path
>>> import inspect
>>> print inspect.getsource(os.path.join)
def join(a, *p):
"""Join two or more pathname components, inserting '/' as needed.
If any component is an absolute path, all previous path components
will be discarded. An empty last part will result in a path that
ends with a separator."""
path = a
for b in p:
if b.startswith('/'):
path = b
elif path == '' or path.endswith('/'):
path += b
else:
path += '/' + b
return path
but, just to be explicit, inspect.getsource()
will fail for functions entered in the interactive console:
>>> def foo(): pass
...
>>> print inspect.getsource(foo)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/mj/Development/Libraries/buildout.python/parts/opt/lib/python2.7/inspect.py", line 701, in getsource
lines, lnum = getsourcelines(object)
File "/Users/mj/Development/Libraries/buildout.python/parts/opt/lib/python2.7/inspect.py", line 690, in getsourcelines
lines, lnum = findsource(object)
File "/Users/mj/Development/Libraries/buildout.python/parts/opt/lib/python2.7/inspect.py", line 538, in findsource
raise IOError('could not get source code')
IOError: could not get source code
because nothing in the interpreter retains the input (other than the readline library, which might save input history, just not in a format directly usable by inspect.getsource()
).
Upvotes: 6