How to Access the Name of the Variable Assigned to the Result of a Function Within the Function

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



Leave a reply



Submit