Determine Function Name from Within That Function (Without Using Traceback)

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.

How to get a method name in Python

As @AChampion pointed out the solution is

self.meth.__name__

Or can be used

Test.meth.__name__

How to get a function name as a string?

my_function.__name__

Using __name__ is the preferred method as it applies uniformly. Unlike func_name, it works on built-in functions as well:

>>> import time
>>> time.time.func_name
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'builtin_function_or_method' object has no attribute 'func_name'
>>> time.time.__name__
'time'

Also the double underscores indicate to the reader this is a special attribute. As a bonus, classes and modules have a __name__ attribute too, so you only have remember one special name.

Print function name in the function itself

you could use a decorator:

def start_finish(f):
def new_f(*args, **kwargs):
print("starting", f.__name__)
f(*args, **kwargs)
print("finished", f.__name__)
return new_f

@start_finish
def function():
print('function body')

function()

this prints:

starting function
function body
finished function

Calling multiple variables from a function to multiple other functions

Yes, usually you would write a function that returns a product with its information.
If you dont want to use that in this basic example, you can also do something like that.
Store all product information in a dictionary and use .get() to access the parameters.

PRODUCTS = {
"apple": {"price": 3.5, "stock": 134},
"banana": {"price": 6.82, "stock": 52}
}

if __name__ == "__main__":
while True:
product = PRODUCTS.get(input("Enter product name: "))
if not product:
print("Sorry, no such product. Try again.")
continue

num = int(input("Enter number of products ordered: "))
stock = product.get("stock", 0)
if num > stock:
print("Sorry, not enough stock. Try again.")
continue

price_per_unit = product.get("price", 0)
print("Ordervalue: ", price_per_unit * num)

How to tell a Python Exception where the function that raised it came from?

An arguably friendlier approach would be to produce a forced exception and save it into the object during initialization, and then raise the saved exception when handling an exception that actually occurs during the execution of the delayed call:

class Delay:
def __init__(self, fn, *args, **kwargs):
self.partial = (fn, args, kwargs)
self.result = None
try:
# artificially create an exception that can be re-raised later
raise ValueError(f'{fn.__name__} failed with args {args} and kwargs {kwargs}')
except ValueError as e:
self.init_exception = e

def __call__(self):
if self.partial is not None:
(fn, args, kwargs) = self.partial
try:
self.result = fn(*args, **kwargs)
except Exception:
# raise the saved exception
raise self.init_exception
self.partial = None
return self.result

def load_historical_data(filename):
with open(filename, 'r') as f:
return f.read()

def init_runtime(filename):
runtime = Delay(load_historical_data, filename)
return runtime

def _analyze_history_job_firstpass(runtime):
return runtime()

_analyze_history_job_firstpass(init_runtime('foobar'))

This produces an error output with tracebacks of both the call stack that created fn and the call stack that called fn:

Traceback (most recent call last):
File "main.py", line 15, in __call__
self.result = fn(*args, **kwargs)
File "main.py", line 23, in load_historical_data
with open(filename, 'r') as f:
FileNotFoundError: [Errno 2] No such file or directory: 'foobar'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "main.py", line 33, in <module>
_analyze_history_job_firstpass(init_runtime('foobar'))
File "main.py", line 31, in _analyze_history_job_firstpass
return runtime()
File "main.py", line 18, in __call__
raise self.init_exception
File "main.py", line 7, in __init__
raise ValueError(f'{fn.__name__} failed with args {args} and kwargs {kwargs}')
ValueError: load_historical_data failed with args ('foobar',) and kwargs {}

Demo: https://replit.com/@blhsing/PassionateKnowingAlgorithms#main.py

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.

What is the correct syntax when calling a variable name from within a function

Based on your comment/reply to my question, I understand years is intended to be a logical. Here is one possible function:

calulate_relative_dev <- function(DT, varA="Income", varB="Income_proxy", groups, year=FALSE) {
dt <- copy(DT)
setnames(dt, old = c(varA, varB), new = c("varA", "varB"))
for (i in seq_along(groups)) {
out_names <- paste0("rel_deviation_", groups)
if(year) out_names <- paste0(out_names, "_by_year")
dt[, c(out_names[i]) := 100*mean((varA - varB) / varA), by=c(groups[i], if(year){"year"})]
}
setnames(dt, old = c("varA", "varB"), new = c(varA, varB))
return(dt[])
}

calulate_relative_dev(DT, groups = c("Group","some_type"))
calulate_relative_dev(DT, groups = c("Group","some_type"), year=TRUE)

I did temporary renames to make the data.table code simpler to read/write. Returning dt[] ensures the data.table is printed after the function is evaluated.



Related Topics



Leave a reply



Submit