Helper function within a main function - python

I have a main function, and helper functions inside the main function that will retrieve values from an excel spreadsheet
Ex.
import openpyxl
def main():
dict = {}
dname = 'Animal list'
animal = {}
while opt in ['0', '1']:
if opt == '0':
print('goodbye')
if opt == '1':
file_open(dname, dict, animal)
return
def file_open(dname, dict, animal):
open_excel(dname)
create_animal_dict(data, dict)
def open_excel(default_name):
filename = dname
try:
workbook = openpyxl.load_workbook(filename)
except:
print("No file")
return
sheet = workbook.active
columns = sheet.max_column
rows= str(sheet.max_row)
max_column_letter = chr(ord("A")+ number_of_columns -1 )
raw= sheet['A1': max_column_letter+number_of_rows]
data= []
for row in raw:
sublist = []
for cell in raw:
sublist.append(cell.value)
data.append(sublist)
return(data) #creates a list of list
The error i get is "builtins.NameError: name 'data' is not defined"
Im having trouble, taking 'data' from open_file(dname) and having it being a parameter for create_animal_dict, I was wondering how to to bring a variable from one helper function to another.
Thanks for any help or tips!

You are not receiving the output from your open_excel function, which then I assume needs to be passed to the create_animal_dict function, e.g.
def file_open(dname, dict, animal):
data = open_excel(dname)
create_animal_dict(mainlist, dict, data)
I assume the error is coming from you calling data from inside create_animal_dict, which this would solve.
But you should also take the output from create_animal_dict and pass it back to main function if you are structuring your program this way.

Related

How to know the name of a classs loade like parameter on other class - Pyhton [duplicate]

This question already has answers here:
Getting the name of a variable as a string
(32 answers)
Closed 4 months ago.
Is it possible to get the original variable name of a variable passed to a function? E.g.
foobar = "foo"
def func(var):
print var.origname
So that:
func(foobar)
Returns:
>>foobar
EDIT:
All I was trying to do was make a function like:
def log(soup):
f = open(varname+'.html', 'w')
print >>f, soup.prettify()
f.close()
.. and have the function generate the filename from the name of the variable passed to it.
I suppose if it's not possible I'll just have to pass the variable and the variable's name as a string each time.
EDIT: To make it clear, I don't recommend using this AT ALL, it will break, it's a mess, it won't help you in any way, but it's doable for entertainment/education purposes.
You can hack around with the inspect module, I don't recommend that, but you can do it...
import inspect
def foo(a, f, b):
frame = inspect.currentframe()
frame = inspect.getouterframes(frame)[1]
string = inspect.getframeinfo(frame[0]).code_context[0].strip()
args = string[string.find('(') + 1:-1].split(',')
names = []
for i in args:
if i.find('=') != -1:
names.append(i.split('=')[1].strip())
else:
names.append(i)
print names
def main():
e = 1
c = 2
foo(e, 1000, b = c)
main()
Output:
['e', '1000', 'c']
To add to Michael Mrozek's answer, you can extract the exact parameters versus the full code by:
import re
import traceback
def func(var):
stack = traceback.extract_stack()
filename, lineno, function_name, code = stack[-2]
vars_name = re.compile(r'\((.*?)\).*$').search(code).groups()[0]
print vars_name
return
foobar = "foo"
func(foobar)
# PRINTS: foobar
Looks like Ivo beat me to inspect, but here's another implementation:
import inspect
def varName(var):
lcls = inspect.stack()[2][0].f_locals
for name in lcls:
if id(var) == id(lcls[name]):
return name
return None
def foo(x=None):
lcl='not me'
return varName(x)
def bar():
lcl = 'hi'
return foo(lcl)
bar()
# 'lcl'
Of course, it can be fooled:
def baz():
lcl = 'hi'
x='hi'
return foo(lcl)
baz()
# 'x'
Moral: don't do it.
Another way you can try if you know what the calling code will look like is to use traceback:
def func(var):
stack = traceback.extract_stack()
filename, lineno, function_name, code = stack[-2]
code will contain the line of code that was used to call func (in your example, it would be the string func(foobar)). You can parse that to pull out the argument
You can't. It's evaluated before being passed to the function. All you can do is pass it as a string.
#Ivo Wetzel's answer works in the case of function call are made in one line, like
e = 1 + 7
c = 3
foo(e, 100, b=c)
In case that function call is not in one line, like:
e = 1 + 7
c = 3
foo(e,
1000,
b = c)
below code works:
import inspect, ast
def foo(a, f, b):
frame = inspect.currentframe()
frame = inspect.getouterframes(frame)[1]
string = inspect.findsource(frame[0])[0]
nodes = ast.parse(''.join(string))
i_expr = -1
for (i, node) in enumerate(nodes.body):
if hasattr(node, 'value') and isinstance(node.value, ast.Call)
and hasattr(node.value.func, 'id') and node.value.func.id == 'foo' # Here goes name of the function:
i_expr = i
break
i_expr_next = min(i_expr + 1, len(nodes.body)-1)
lineno_start = nodes.body[i_expr].lineno
lineno_end = nodes.body[i_expr_next].lineno if i_expr_next != i_expr else len(string)
str_func_call = ''.join([i.strip() for i in string[lineno_start - 1: lineno_end]])
params = str_func_call[str_func_call.find('(') + 1:-1].split(',')
print(params)
You will get:
[u'e', u'1000', u'b = c']
But still, this might break.
You can use python-varname package
from varname import nameof
s = 'Hey!'
print (nameof(s))
Output:
s
Package below:
https://github.com/pwwang/python-varname
For posterity, here's some code I wrote for this task, in general I think there is a missing module in Python to give everyone nice and robust inspection of the caller environment. Similar to what rlang eval framework provides for R.
import re, inspect, ast
#Convoluted frame stack walk and source scrape to get what the calling statement to a function looked like.
#Specifically return the name of the variable passed as parameter found at position pos in the parameter list.
def _caller_param_name(pos):
#The parameter name to return
param = None
#Get the frame object for this function call
thisframe = inspect.currentframe()
try:
#Get the parent calling frames details
frames = inspect.getouterframes(thisframe)
#Function this function was just called from that we wish to find the calling parameter name for
function = frames[1][3]
#Get all the details of where the calling statement was
frame,filename,line_number,function_name,source,source_index = frames[2]
#Read in the source file in the parent calling frame upto where the call was made
with open(filename) as source_file:
head=[source_file.next() for x in xrange(line_number)]
source_file.close()
#Build all lines of the calling statement, this deals with when a function is called with parameters listed on each line
lines = []
#Compile a regex for matching the start of the function being called
regex = re.compile(r'\.?\s*%s\s*\(' % (function))
#Work backwards from the parent calling frame line number until we see the start of the calling statement (usually the same line!!!)
for line in reversed(head):
lines.append(line.strip())
if re.search(regex, line):
break
#Put the lines we have groked back into sourcefile order rather than reverse order
lines.reverse()
#Join all the lines that were part of the calling statement
call = "".join(lines)
#Grab the parameter list from the calling statement for the function we were called from
match = re.search('\.?\s*%s\s*\((.*)\)' % (function), call)
paramlist = match.group(1)
#If the function was called with no parameters raise an exception
if paramlist == "":
raise LookupError("Function called with no parameters.")
#Use the Python abstract syntax tree parser to create a parsed form of the function parameter list 'Name' nodes are variable names
parameter = ast.parse(paramlist).body[0].value
#If there were multiple parameters get the positional requested
if type(parameter).__name__ == 'Tuple':
#If we asked for a parameter outside of what was passed complain
if pos >= len(parameter.elts):
raise LookupError("The function call did not have a parameter at postion %s" % pos)
parameter = parameter.elts[pos]
#If there was only a single parameter and another was requested raise an exception
elif pos != 0:
raise LookupError("There was only a single calling parameter found. Parameter indices start at 0.")
#If the parameter was the name of a variable we can use it otherwise pass back None
if type(parameter).__name__ == 'Name':
param = parameter.id
finally:
#Remove the frame reference to prevent cyclic references screwing the garbage collector
del thisframe
#Return the parameter name we found
return param
If you want a Key Value Pair relationship, maybe using a Dictionary would be better?
...or if you're trying to create some auto-documentation from your code, perhaps something like Doxygen (http://www.doxygen.nl/) could do the job for you?
I wondered how IceCream solves this problem. So I looked into the source code and came up with the following (slightly simplified) solution. It might not be 100% bullet-proof (e.g. I dropped get_text_with_indentation and I assume exactly one function argument), but it works well for different test cases. It does not need to parse source code itself, so it should be more robust and simpler than previous solutions.
#!/usr/bin/env python3
import inspect
from executing import Source
def func(var):
callFrame = inspect.currentframe().f_back
callNode = Source.executing(callFrame).node
source = Source.for_frame(callFrame)
expression = source.asttokens().get_text(callNode.args[0])
print(expression, '=', var)
i = 1
f = 2.0
dct = {'key': 'value'}
obj = type('', (), {'value': 42})
func(i)
func(f)
func(s)
func(dct['key'])
func(obj.value)
Output:
i = 1
f = 2.0
s = string
dct['key'] = value
obj.value = 42
Update: If you want to move the "magic" into a separate function, you simply have to go one frame further back with an additional f_back.
def get_name_of_argument():
callFrame = inspect.currentframe().f_back.f_back
callNode = Source.executing(callFrame).node
source = Source.for_frame(callFrame)
return source.asttokens().get_text(callNode.args[0])
def func(var):
print(get_name_of_argument(), '=', var)
If you want to get the caller params as in #Matt Oates answer answer without using the source file (ie from Jupyter Notebook), this code (combined from #Aeon answer) will do the trick (at least in some simple cases):
def get_caller_params():
# get the frame object for this function call
thisframe = inspect.currentframe()
# get the parent calling frames details
frames = inspect.getouterframes(thisframe)
# frame 0 is the frame of this function
# frame 1 is the frame of the caller function (the one we want to inspect)
# frame 2 is the frame of the code that calls the caller
caller_function_name = frames[1][3]
code_that_calls_caller = inspect.findsource(frames[2][0])[0]
# parse code to get nodes of abstract syntact tree of the call
nodes = ast.parse(''.join(code_that_calls_caller))
# find the node that calls the function
i_expr = -1
for (i, node) in enumerate(nodes.body):
if _node_is_our_function_call(node, caller_function_name):
i_expr = i
break
# line with the call start
idx_start = nodes.body[i_expr].lineno - 1
# line with the end of the call
if i_expr < len(nodes.body) - 1:
# next expression marks the end of the call
idx_end = nodes.body[i_expr + 1].lineno - 1
else:
# end of the source marks the end of the call
idx_end = len(code_that_calls_caller)
call_lines = code_that_calls_caller[idx_start:idx_end]
str_func_call = ''.join([line.strip() for line in call_lines])
str_call_params = str_func_call[str_func_call.find('(') + 1:-1]
params = [p.strip() for p in str_call_params.split(',')]
return params
def _node_is_our_function_call(node, our_function_name):
node_is_call = hasattr(node, 'value') and isinstance(node.value, ast.Call)
if not node_is_call:
return False
function_name_correct = hasattr(node.value.func, 'id') and node.value.func.id == our_function_name
return function_name_correct
You can then run it as this:
def test(*par_values):
par_names = get_caller_params()
for name, val in zip(par_names, par_values):
print(name, val)
a = 1
b = 2
string = 'text'
test(a, b,
string
)
to get the desired output:
a 1
b 2
string text
Since you can have multiple variables with the same content, instead of passing the variable (content), it might be safer (and will be simpler) to pass it's name in a string and get the variable content from the locals dictionary in the callers stack frame. :
def displayvar(name):
import sys
return name+" = "+repr(sys._getframe(1).f_locals[name])
If it just so happens that the variable is a callable (function), it will have a __name__ property.
E.g. a wrapper to log the execution time of a function:
def time_it(func, *args, **kwargs):
start = perf_counter()
result = func(*args, **kwargs)
duration = perf_counter() - start
print(f'{func.__name__} ran in {duration * 1000}ms')
return result

Python function not picking updated list values

I have two functions, updates_list updates values to the list altered_source_tables while the other picks the values in that list. The issue is, even though the first function updates the list, the second function use_list still has the list as empty.
altered_source_tables = []
table_name = 'table_1'
def updates_list(table_name, **kwargs):
# CODE
for e in job.errors:
fullstring = e['message']
substring = "No such field"
if search(substring, fullstring):
altered_source_tables.append(table_name)
print("altered_source_tables list ", altered_source_tables) # output from fn call: altered_source_tables = ['table_1']
else:
print('ERROR: {}'.format(e['message']))
def use_list(**kwargs):
print("altered_source_tables list ", altered_source_tables) # output from fn call: altered_source_tables = []
if len(altered_source_tables) > 0:
# Loop through all altered tables
for table_name in altered_source_tables:
# CODE
What I'm I missing?

Python 3.x: User Input to Call and Execute a Function

There are many questions similar to this out there but none of the answers solved my issue.
I have defined several functions that parse large data sets. First I call the data, then I organize the data (represented as rows and columns in a .txt) into lists which I will index for individual data entries. After that I establish my functions that will work through the lists one at a time. The code looks like:
f = open(fn)
for line in iter(f):
entries = [i for i in line.split() if i]
def function_one():
if entries[0] == 150:
# do something
def function_two():
if entries[1] == 120:
# do something else
def function_three():
if len(entries) > 10:
# do something else
etc. etc.
I have attempted to prompt the user asking what function they would like to execute as each function returns different things about the data set. My attempt is as follows:
f_call = input('Enter Function Name: ')
if f_call in locals().keys() and callable(locals()['f_call']):
locals()['f_call']()
else:
print('Function Does Not Exist')
When I run the script, I am prompted to 'Enter Function Name:' and if I type in 'function_one' and return, it prints 'Function Does Not Exist'. I want to see that, if entered correctly, the script will execute only the function that the user entered. If the user input is correct, the function should run and print the parsed data.
I have also attempted using a dict to store the functions but I have not had success.
Any help would be greatly appreciated.
Based on your comments, I think you're trying to achieve something like this:
def function_one(data):
if data[0] == 150:
pass # do something
def function_two(data):
if data[1] == 120:
pass # do something else
def function_three(data):
if len(data) > 10:
pass # do something entirely different
This defines your functions that accept arguments so you can re-use them later on. Then you'd like to ask the user which function to use when processing the data, so:
while True: # loop while we don't get a valid input
user_function = input('Enter a function name to use: ') # ask the user for input
if user_function in locals() and callable(locals()[user_function]): # if it exists...
user_function = locals()[user_function] # store a pointer to the function
break # break out of the while loop since we have our valid input
else:
print('Invalid function name, try again...')
Finally, you can load your file, read it line by line, split it up and process it by the function decided by the user:
with open(file_name, "r") as f:
for line in f:
entries = line.split() # no need to check for empty elements
user_function(entries) # call the user selected function and pass `entries` to it
Of course, you can do further processing afterwards.
UPDATE - Here's a simple test for the above code, given the file test_file.txt containing:
tokenized line 1
tokenized line 2
tokenized line 3
and file_name = "test_file.txt" defined in the file, while the functions are defined as:
def function_one(data):
print("function_one called: {}".format(data))
def function_two(data):
print("function_two called: {}".format(data))
def function_three(data):
print("function_three called: {}".format(data))
If you execute the code this is the output/trace:
Enter a function name to use: bad_name_on_purpose
Invalid function name, try again...
Enter a function name to use: function_two
function_two called: ['tokenized', 'line', '1']
function_two called: ['tokenized', 'line', '2']
function_two called: ['tokenized', 'line', '3']
to make your code work, just keep the variable f_call without '' when you call it
f_call = input('Enter Function Name: ')
if f_call in locals().keys() and callable(locals()[f_call]):
locals()[f_call]()
else:
print('Function Does Not Exist')
It may not be the most efficient way to fix it, but you could use something like this:
f_call = raw_input('Enter Function Name: ')
if f_call == "function_one":
function_one()
if f_call == "function_two":
function_two()
if f_call == "function_three":
function_three()
else:
print('Function Does Not Exist')

Python function call within a property of a class

I am trying to use the "setx" function of a Property in a Class to do some processing of date information that I get from excel. I have a few of my own functions that do the data processing which I tested outside the class, and they worked just fine. But when I move them into the class they suddenly become invisible unless I use the self. instance first. When I use the self.My_xldate_as_tuple() method I get an error:
My_xldate_as_tuple() takes 1 positional argument but 2 were given
Even though the code is EXACTLY what i used outside the class before and it worked.
Before moving into the Property Set block, I was doing the processing of date data outside of the class and setting the variables from outside of the class. That gets clunky when I have about 15 different operations that are all based on when the NumDates Property change. I'm showing shortened versions of both the working set of code and the non-working set of code. What is going on with the self. call that changes how the function takes inputs?
Broken Code:
class XLDataClass(object):
_NumDates = []
TupDates = []
def getNumDates(self): return self._NumDates
def setNumDates(self, value):
self._NumDates = value
self.TupDates = list(map(self.My_xldate_as_tuple,value)) #Error here
#This version doesn't work either, since it can't find My_xldate_as_tuple anymore
self.TupDates = list(map(My_xldate_as_tuple,value))
def delNumDates(self):del self._NumDates
NumDates = property(getNumDates,setNumDates,delNumDates,"Ordinal Dates")
#exact copy of the My_xldate_as_tuple function that works outside the class
def My_xldate_as_tuple(Date):
return xlrd.xldate_as_tuple(Date,1)
#Other code and functions here
#end XlDataClass
def GetExcelData(filename,rowNum,titleCol):
csv = np.genfromtxt(filename, delimiter= ",")
NumDates = deque(csv[rowNum,:])
if titleCol == True:
NumDates.popleft()
return NumDates
#Setup
filedir = "C:/Users/blahblahblah"
filename = filedir + "/SamplePandL.csv"
xlData = XLDataClass()
#Put csv data into xlData object
xlData.NumDates= GetExcelData(filename,0,1)
Working Code:
class XLDataClass(object):
NumDates = []
TupDates = []
#Other code and functions here
#end XlDataClass
#exact copy of the same function outside of the class, which works here
def My_xldate_as_tuple(Date):
return xlrd.xldate_as_tuple(Date,1)
def GetExcelData(filename,rowNum,titleCol):
csv = np.genfromtxt(filename, delimiter= ",")
NumDates = deque(csv[rowNum,:])
if titleCol == True:
NumDates.popleft()
return NumDates
#Setup
filedir = "C:/Users/blahblahblah"
filename = filedir + "/SamplePandL.csv"
xlData = XLDataClass()
#Put csv data into xlData object
xlData.NumDates = GetExcelData(filename,0,1)
#same call to the function that was inside the Setx Property of the class, but it works here.
xlData.TupDates = list(map(self.My_xldate_as_tuple,value))
Instance methods in Python require an explicit self in the argument list. Inside the class, you need to write your method definition like:
def My_xldate_as_tuple(self, Date):

Passing a dictionary to another function

I have built a function to create a dictionary and return it. This function is called get_values, and is structured as follows:
def initiate_values(directory):
for file in glob.glob(os.path.join(directory, '*.[xX][lL][sS]')):
title = os.path.basename(file).lower()
if title == 'etc.xls':
wb = xlrd.open_workbook(file)
wb = wb.sheet_by_name(u'Sheet1')
get_values(file, wb)
def get_values():
info_from_etc = dict()
# build dict
return info_from_etc
It works, in that it creates the dictionary and then when I try and print it, it prints the correct values. However, when I try and call this get_values function from another function, the dictionary returns as "None". This is my function to call get_values -
def packager():
info_from_etc = initiate_values()
print info_from_etc # this prints "None"
What am I doing incorrectly here, and how would I return the proper dictionary here -- that is, a dictionary that is not None.
You need to return the dictionary from initiate_values:
def initiate_values(directory):
for file in glob.glob(os.path.join(directory, '*.[xX][lL][sS]')):
title = os.path.basename(file).lower()
if title == 'etc.xls':
wb = xlrd.open_workbook(file)
wb = wb.sheet_by_name(u'Sheet1')
return get_values(file, wb) # added `return'
return {} # or some other value
info_from_etc = initiate_values()
initiate_values does not return anything, so by Python default, it returns None. You should be able to figure out where to put the return statement based on what you want to do.
I agree you do need to return the dictionary in your initiate_values() function, but you are also giving get_values two parameters (file, wb) in the initiate_values function, and you aren't giving it any in the declaration. It seems that there might be a problem there too.

Categories

Resources