How do I access the name of the variable assigned to the result of a function within the function?
I think that it's not strictly possible, as other solutions explained, and the reasonable alternative is probably Yosi's answer.
However we can have fun with some ideas, starting simple and getting crazier gradually.
1 - define an infix operator that looks similar
`%<-add_str%` <- function(e1, e2) {
e2_ <- e2
e1_ <- as.character(substitute(e1))
eval.parent(substitute(e1 <- paste0(e1_,e2_)))
}
a %<-add_str% "b"
a
# "ab"
2 - Redefine :=
so that it makes available the name of the lhs to the rhs through a ..lhs()
function
I think it's my favourite option :
`:=` <- function(lhs,rhs){
lhs_name <- as.character(substitute(lhs))
assign(lhs_name,eval(substitute(rhs)), envir = parent.frame())
lhs
}
..lhs <- function(){
eval.parent(quote(lhs_name),2)
}
add_str <- function(x){
res <- paste0(..lhs(),x)
res
}
a := add_str("b")
a
# [1] "ab"
There might be a way to redefine <-
based on this, but I couldn't figure it out due to recursion issues.
3 - Use memory address dark magic to hunt lhs (if it exists)
This comes straight from: Get name of x when defining `(<-` operator
We'll need to change a bit the syntax and define the function fetch_name
for this purpose, which is able to get the name of the rhs from a *<-
function, where as.character(substitute(lhs))
would return "*tmp*"
.
fetch_name <- function(x,env = parent.frame(2)) {
all_addresses <- sapply(ls(env), pryr:::address2, env)
all_addresses <- all_addresses[names(all_addresses) != "*tmp*"]
all_addresses_short <- gsub("(^|<)[0x]*(.*?)(>|$)","\\2",all_addresses)
x_address <- tracemem(x)
untracemem(x)
x_address_short <- tolower(gsub("(^|<)[0x]*(.*?)(>|$)","\\2",x_address))
ind <- match(x_address_short, all_addresses_short)
x_name <- names(all_addresses)[ind]
x_name
}
`add_str<-` <- function(x,value){
x_name <- fetch_name(x)
paste0(x_name,value)
}
a <- NA
add_str(a) <- "b"
a
4- a variant of the latter, using .Last.value
:
add_str <- function(value){
x_name <- fetch_name(.Last.value)
assign(x_name,paste0(x_name,value),envir = parent.frame())
paste0(x_name,value)
}
a <- NA;add_str("b")
a
# [1] "ab"
Operations don't need to be on the same line, but they need to follow each other.
5 - Again a variant, using a print method hack
Extremely dirty and convoluted, to please the tortured spirits and troll the others.
This is the only one that really gives the expected output, but it works only in interactive mode.
The trick is that instead of doing all the work in the first operation I also use the second (printing). So in the first step I return an object whose value is "b"
, but I also assigned a class "weird"
to it and a printing method, the printing method then modifies the object's value, resets its class, and destroys itself.
add_str <- function(x){
class(x) <- "weird"
assign("print.weird", function(x) {
env <- parent.frame(2)
x_name <- fetch_name(x, env)
assign(x_name,paste0(x_name,unclass(x)),envir = env)
rm(print.weird,envir = env)
print(paste0(x_name,x))
},envir = parent.frame())
x
}
a <- add_str("b")
a
# [1] "ab"
(a <- add_str("b")
will have the same effect as both lines above. print(a <- add_str("b"))
would also have the same effect but would work in non interactive code, as well.
Calling variable defined inside one function from another function
One approach would be to make oneFunction
return the word so that you can use oneFunction
instead of word
in anotherFunction
:
def oneFunction(lists):
category = random.choice(list(lists.keys()))
return random.choice(lists[category])
def anotherFunction():
for letter in oneFunction(lists):
print("_", end=" ")
Another approach is making anotherFunction
accept word
as a parameter which you can pass from the result of calling oneFunction
:
def anotherFunction(words):
for letter in words:
print("_", end=" ")
anotherFunction(oneFunction(lists))
And finally, you could define both of your functions in a class, and make word
a member:
class Spam:
def oneFunction(self, lists):
category=random.choice(list(lists.keys()))
self.word=random.choice(lists[category])
def anotherFunction(self):
for letter in self.word:
print("_", end=" ")
Once you make a class, you have to instantiate an instance and access the member functions:
s = Spam()
s.oneFunction(lists)
s.anotherFunction()
Is it possible to get the function name by using the variable declared inside the function
You can use arguments.callee.name
, like this
function abcd() {
console.log("name of the function using", arguments.callee.name);
};
abcd();
# name of the function using abcd
Note: arguments.callee.name
will work only with the functions defined with a name. It will not work with anonymous functions. For example, if you had defined your function, like this
var abcd = function () {
console.log("name of the function using", arguments.callee.name);
};
abcd()
would just print name of the function using
. Because arguments.callee.name
would be an empty string in this case.
Access a function variable outside the function without using global
You could do something along these lines (which worked in both Python v2.7.17 and v3.8.1 when I tested it/them):
def hi():
# other code...
hi.bye = 42 # Create function attribute.
sigh = 10
hi()
print(hi.bye) # -> 42
Functions are objects in Python and can have arbitrary attributes assigned to them.
If you're going to be doing this kind of thing often, you could implement something more generic by creating a function decorator that adds a this
argument to each call to the decorated function.
This additional argument will give functions a way to reference themselves without needing to explicitly embed (hardcode) their name into the rest of the definition and is similar to the instance argument that class methods automatically receive as their first argument which is usually named self
— I picked something different to avoid confusion, but like the self
argument, it can be named whatever you wish.
Here's an example of that approach:
def add_this_arg(func):
def wrapped(*args, **kwargs):
return func(wrapped, *args, **kwargs)
return wrapped
@add_this_arg
def hi(this, that):
# other code...
this.bye = 2 * that # Create function attribute.
sigh = 10
hi(21)
print(hi.bye) # -> 42
Note
This doesn't work for class methods. Just use the instance argument, named self
by convention, that's already passed to methods instead of the method's name. You can reference class-level attributes through type(self)
. See Function's attributes when in a class.
Class instance variable assigned inside function
Yes, you're just assigning objects. Now, you should be very careful with the way you've written this. It's a bad idea to rely on the scope you have set out -- it will work, but I could easily see someone losing track of "a" and it never getting passed down to func(). A better implementation of func, in my opinion:
def func(a, val=5):
class C:
def __init__(self):
self.var = [[] for _ in range(2)]
c = C()
c.var[0] = val
a.var = c.var
class A:
def __init__(self):
self.id = 0
self.var = [[] for _ in range(2)]
if __name__ == "__main__":
a = A()
func(a)
aa = A()
func(aa, val=10)
Since lists can contain any objects, you could do some even weirder stuff like.
aa.var[1] = a
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.
Related Topics
Changing Styles When Selecting and Deselecting Multiple Polygons with Leaflet/Shiny
How to Import Only One Function from Another Package, Without Loading the Entire Namespace
R Dynamically Build "List" in Data.Table (Or Ddply)
How to Speed Up or Vectorize a for Loop
How to Calculate the Distance Between Latitude and Longitude Along Rows of Columns in R
How to Sort Data by Column in Descending Order in R
Avoid Ggplot2 to Partially Cut Axis Text
Rename Columns Using 'Starts_With()' Where New Prefix Is a String
Quickest Way to Read a Subset of Rows of a CSV
Downloading Files from Ftp with R
Display Frequency Instead of Count with Geom_Bar() in Ggplot
User Defined Colour Palette in R and Ggpairs
Does Installing Blas/Atlas/Mkl/Openblas Will Speed Up R Package That Is Written in C/C++
Suppress Automatic Output to Console in R
Extend Axis Limits Without Plotting (In Order to Align Two Plots by X-Unit)