Finding out which functions are called within a given function
There must be better ways out there, but here's my attempt:
listFunctions <- function(function.name, recursive = FALSE,
checked.functions = NULL){
# Get the function's code:
function.code <- deparse(get(function.name))
# break code up into sections preceding left brackets:
left.brackets <- c(unlist(strsplit(function.code,
split="[[:space:]]*\\(")))
called.functions <- unique(c(unlist(sapply(left.brackets,
function (x) {
# Split up according to anything that can't be in a function name.
# split = not alphanumeric, not '_', and not '.'
words <- c(unlist(strsplit(x, split="[^[:alnum:]_.]")))
last.word <- tail(words, 1)
last.word.is.function <- tryCatch(is.function(get(last.word)),
error=function(e) return(FALSE))
return(last.word[last.word.is.function])
}))))
if (recursive){
# checked.functions: We need to keep track of which functions
# we've checked to avoid infinite loops.
functs.to.check <- called.functions[!(called.functions %in%
checked.functions)]
called.functions <- unique(c(called.functions,
do.call(c, lapply(functs.to.check, function(x) {
listFunctions(x, recursive = T,
checked.functions = c(checked.functions,
called.functions))
}))))
}
return(called.functions)
}
And the results:
> listFunctions("listFunctions", recursive = FALSE)
[1] "function" "deparse" "get" "c"
[5] "unlist" "strsplit" "unique" "sapply"
[9] "tail" "tryCatch" "is.function" "return"
[13] "if" "do.call" "lapply" "listFunctions"
> system.time(all.functions <- listFunctions("listFunctions", recursive = TRUE))
user system elapsed
92.31 0.08 93.49
> length(all.functions)
[1] 518
As you can see, the recursive version returns a lot of functions. The problem with this is it returns every function called in the process, which obviously adds up as you go. In any case, I hope you can use this (or modify it) to suit your needs.
get the lists of functions used/called within a function in python
This seems to do the work:
import dis
def list_func_calls(fn):
funcs = []
bytecode = dis.Bytecode(fn)
instrs = list(reversed([instr for instr in bytecode]))
for (ix, instr) in enumerate(instrs):
if instr.opname=="CALL_FUNCTION":
load_func_instr = instrs[ix + instr.arg + 1]
funcs.append(load_func_instr.argval)
return ["%d. %s" % (ix, funcname) for (ix, funcname) in enumerate(reversed(funcs), 1)]
Example:
>>> list_func_calls(calculate)
['1. add', '2. subtract']
What's happening here is:
- we make a Bytecode object of the function
- we reverse the list of instructions, since the function name will
follow the function call we step through the list, and for each CALL_FUNCTION instruction,
we use the instructions
arg
parameter to tell us how many
arguments we're gettingwe look one past that to find the instruction that loads the function
we're callingwe add that function's name (
instr.argval
) to a list which we then
reverse, enumerate, and return in the requested format
Note that since Python 3.6, there are three CALL_FUNCTION
instructions, so you'll have to check the documentation to extend this example to be fully functional with current python
How to get the list of in-built functions used within a function
You could use all.vars()
to get all the variable names (including functions) that appear inside the body of Fun1
, then compare that with some prepared list of functions. You mention in-built functions, so I will compare it with the base package object names.
## full list of variable names inside the function body
(vars <- all.vars(body(Fun1)[-1], functions = TRUE))
# [1] "sum" "x" "y" "mean" "c" "print"
## compare it with the base package object names
intersect(vars, ls(baseenv()))
# [1] "sum" "mean" "c" "print"
I removed the first element of the function body because presumably you don't care about {
, which would have been matched against the base package list.
Another possibility, albeit a bit less reliable, would be to compare the formal arguments of Fun1
to all the variable names in the function. Like I said, likely less reliable though because if you make assignments inside the function you will end up with incorrect results.
setdiff(vars, names(formals(Fun1)))
# [1] "sum" "mean" "c" "print"
These are fun though, and you can fiddle around with them.
Determine function name from within that function (without using traceback)
Python doesn't have a feature to access the function or its name within the function itself. It has been proposed but rejected. If you don't want to play with the stack yourself, you should either use "bar"
or bar.__name__
depending on context.
The given rejection notice is:
This PEP is rejected. It is not clear how it should be implemented or what the precise semantics should be in edge cases, and there aren't enough important use cases given. response has been lukewarm at best.
Find Out If a Function has been Called
If it's OK for the function to know its own name, you can use a function attribute:
def example():
example.has_been_called = True
pass
example.has_been_called = False
example()
#Actual Code!:
if example.has_been_called:
print("foo bar")
You could also use a decorator to set the attribute:
import functools
def trackcalls(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
wrapper.has_been_called = True
return func(*args, **kwargs)
wrapper.has_been_called = False
return wrapper
@trackcalls
def example():
pass
example()
#Actual Code!:
if example.has_been_called:
print("foo bar")
List all functions inside a given function
There are two possible meanings to "all functions inside a given function".
If you mean all functions called from a given function, use
codetools::findGlobals(fn, merge = FALSE)$functions
. This will list everything referenced from withinfn
that wasn't defined there.If you mean all functions defined within a given function, that's a little harder. I think there are other
codetools
functions that would let you put this together, but I don't know the details.
how to get function's name from within the function (or kind of self reference to the function)?
Perhaps you should decorate each function that you're calling with an onlyonce
decorator? That would be more pythonic. A proof of concept follows.
called = set()
def onlyonce(fn):
def decorated(*largs, **kargs):
if fn not in called:
called.add(fn)
print "Calling"
fn(*largs, **kargs)
else:
print "Already called"
return decorated
@onlyonce
def test_function():
print "I am getting called now"
test_function()
test_function()
test_function()
test_function()
Also, functions are "immutable" and can be stored as dictionary keys. You don't have to rely on the names. This might be an advantage (or a disadvantage) depending on your use.
Related Topics
Group Integer Vector into Consecutive Runs
Printing Newlines with Print() in R
Same Function Over Multiple Data Frames in R
How to Plot a Hybrid Boxplot: Half Boxplot with Jitter Points on the Other Half
How to Fix Corrupted Dates in R
Saving Multiple Ggplots from Ls into One and Separate Files in R
Insert Blanks into a Vector For, E.G., Minor Tick Labels in R
How to Reorder a Legend in Ggplot2
Sorting Each Row of a Data Frame
How to Randomize (Or Permute) a Dataframe Rowwise and Columnwise
Ggplot Centered Names on a Map
Conditional Coloring of Cells in Table
Add Number of Observations Per Group in Ggplot2 Boxplot