Would it be possible to use a variable as a function name in python?
For example:
list = [one, two, three]
for item in list:
def item():
some_stuff()
The trick is to use globals():
globals()['use_variable_as_function_name']()
will be equivalent to
use_variable_as_function_name()
found at: George Sakkis https://bytes.com/topic/python/answers/792283-calling-variable-function-name
The following is a useful application of the above questioning I needed right now (that's why I came here): apply special functions to URLs depending on their nature:
l = ['condition1', 'condition2', 'condition3']
I used to write
if 'condition1.' in href:
return do_something_condition1()
if 'condition2.' in href:
return do_something_condition2()
if 'condition3.' in href:
return do_something_condition3()
and so on - my list has 19 members by now and keeps growing.
While investigating the subject and developing, the function code had been quite naturally part of the main function making it soon horrible to read, so relocating the working code into functions was a great relief already.
This clumsy code above can be substituted by:
for e in l: # this is my condition list
if e + '.' in href: # this is the mechanism to choose the right function
return globals()['do_something_' + e]()
This way the main code stays simple and legible no matter how long the list of conditions may grow.
Those functions corresponding to the condition labels have to be declared conventionally, of course, depending on the nature of the type of the URL in question:
def do_something_condition1(href):
# special code 1
print('========1=======' + href)
def do_something_condition2(href):
# special code 2
print('========2=======' + href)
def do_something_condition3(href):
# special code 3
print('========3=======' + href)
Test:
>>> href = 'https://google.com'
>>> for e in l:
... globals()['do_something_' + e](href)
...
========1=======https://google.com
========2=======https://google.com
========3=======https://google.com
Or, to model it closer to the above scenario:
success = '________processed successfully___________ '
def do_something_google(href):
# special code 1
print('========we do google-specific stuff=======')
return success + href
def do_something_bing(href):
# special code 2
print('========we do bing-specific stuff=======')
return success + href
def do_something_wikipedia(href):
# special code 3
print('========we do wikipedia-specific stuff=======')
return success + href
Test:
l = ['google', 'bing', 'wikipedia']
href = 'https://google.com'
def test(href):
for e in l:
if e + '.' in href:
return globals()['do_something_' + e](href)
>>> test(href)
========we do google-specific stuff=======
'________processed successfully___________ https://google.com'
Result:
Further elaboration on the problem now just amounts to augment the condition list one by one and write the corresponding functions depending on the argument. The above mechanism will pick the right one thereafter.
You can't define a function using a variable but you can rebind the function to the variable name. Here is an example to add them to the module's global namespace.
one = 'one'
two = 'two'
three = 'three'
l = [one, two, three]
def some_stuff():
print("i am sure some stuff")
for item in l:
def _f():
some_stuff()
globals()[item] = _f
del _f
one()
two()
three()
Functions in Python are objects that have a name referencing them, so you can pass them around, store in lists and dictionaries (common use when creating jump-tables).
I.e. this works:
def one():
print "1"
def two():
print "2"
def three():
print "3"
l = [one, two, three]
for item in l:
item()
Will print:
1
2
3
Don't use list as variable name, because this way you redefine buildin.
def is the statement that is also executed, unlike function defenitions in compiled languages. So when you call def item(): you don't define function for one, two, three, but redefine item name.
In general it is not quite clear what you're trying to do, but it doesn't look like a good idea. May be explain what you try to accomplish, or rethink the way you want to do it.
Here is a workaround wrapped in a class. It uses a dictionary for the mapping:
class function_class:
def __init__(self,fCase):
fDic = {'A':self.A, # mapping: string --> variable = function name
'B':self.B,
'C':self.C}
self.fActive = fDic[fCase]
def A(self): print('here runs function A')
def B(self): print('here runs function B')
def C(self): print('here runs function C')
def run_function(self):
self.fActive()
#---- main ----------
fList = ['A','B','C'] # list with the function names as strings
for f in fList: # run through the list
g = function_class(f)
g.run_function()
The output is:
here runs function A
here runs function B
here runs function C
You can do this:
from types import FunctionType
from copy import copy
def copy_function(fn):
return FunctionType(copy(fn.func_code), copy(fn.func_globals), name=item,
argdefs=copy(fn.func_defaults),
closure=copy(fn.func_closure))
list = ['one', 'two', 'three']
for item in list:
def _fn():
print(item)
globals()[item] = copy_function(_fn)
list = map(eval, list)
The short answer is no. When you declare a variable, you have bound a name to an object. The same is true when you declare a function. You can try it out for yourself in a python console and see what happens:
>name=1
>name
1
>def name(x): print(x+1)
>name
function name at 0x000001CE8B8122F0
Here is an updated answer from #enrico.basis:
from types import FunctionType
from copy import copy
def copy_function(fn):
return FunctionType(
code=copy(fn.__code__),
globals=copy(fn.__globals__),
name=fn.__name__,
argdefs=copy(fn.__defaults__),
closure=copy(fn.__closure__)
)
items = ['print_one', 'print_two', 'print_three']
for ele in items:
def _f():
return 'String from {}() function'.format(ele)
_f.__name__ = ele
locals()[ele] = copy_function(_f)
del _f
for ele in items:
_f = eval(ele)
print('- {}'.format(_f()))
This will give the following output:
- String from print_one() function
- String from print_two() function
- String from print_three() function
Related
In python you can do fname.__code__.co_names to retrieve a list of functions and global things that a function references. If I do fname.__code__.co_varnames, this includes inner functions, I believe.
Is there a way to essentially do inner.__code__.co_names ? by starting with a string that looks like 'inner', as is returned by co_varnames?
In Python 3.4+ you can get the names using dis.get_instructions. To support nested functions as well you need to recursively loop over each code object you encounter:
import dis
import types
def get_names(f):
ins = dis.get_instructions(f)
for x in ins:
try:
if x.opcode == 100 and '<locals>' in next(ins).argval\
and next(ins).opcode == 132:
yield next(ins).argrepr
yield from get_names(x.argval)
except Exception:
pass
Demo:
def func():
x = 1
y = 2
print ('foo')
class A:
def method(self):
pass
def f1():
z = 3
print ('bar')
def f2():
a = 4
def f3():
b = [1, 2, 3]
def f4():
pass
print(list(get_names(func)))
Outputs:
['f1', 'f2', 'f3', 'f4']
I don't think you can inspect the code object because inner functions are lazy, and their code-objects are only created just in time. What you probably want to look at instead is the ast module. Here's a quick example:
import ast, inspect
# this is the test scenario
def function1():
f1_var1 = 42
def function2():
f2_var1 = 42
f2_var2 = 42
def function3():
f3_var1 = 42
# derive source code for top-level function
src = inspect.getsource(function1)
# derive abstract syntax tree rooted at top-level function
node = ast.parse(src)
# next, ast's walk method takes all the difficulty out of tree-traversal for us
for x in ast.walk(node):
# functions have names whereas variables have ids,
# nested-classes may all use different terminology
# you'll have to look at the various node-types to
# get this part exactly right
name_or_id = getattr(x,'name', getattr(x,'id',None))
if name_or_id:
print name_or_id
The results are: function1, function2, f1_var1, function3, f2_var1, f2_var2, f3_var1. Obligatory disclaimer: there's probably not a good reason for doing this type of thing.. but have fun :)
Oh and if you only want the names of the inner functions?
print dict([[x.name,x] for x in ast.walk(ast.parse(inspect.getsource(some_function))) if type(x).__name__=='FunctionDef'])
I want to make a function that takes makes a list and adds an item to that list when I run it, so basically I would be passing this function two arguments, the first is the name I want the list to have, the second is an item I want to add to the list.
I want to do this as a learning excercise and I've built a function that almost does what I want it to do.
def addlst(l, item):
"""add an item to a list"""
l = list()#this line essentially does nothing.
if type(item) == str:
l.append(item.capitalize())
return l
print(l)
else:
l.append(item)
return l
print(l)
if I pass this something like:
addlst(people, 'kev')
I get the error:
NameError: name 'people' is not defined
but obviously, if I define people as an empty list it works fine.
Is what I'm doing actually possible? I know that as it stands the line
l = list()
would just empty the list first and so the append function would be useless (I'd have to add another clause to check if the list exists already) but my question is really about initialising a blank list within a function and then returning it to the global scope.
Putting aside the discussion regarding whether it is a good practice, (which can make sens if your main goal is about improving your understanding), you could simply use the global keyword to do what you describe. Say
def f(el):
global l
l.append(el)
Then
>>> l = []
>>> f(2)
>>> l
[2]
>>> f(3)
>>> l
[2, 3]
As it reads above, l has to be declared before using f.
Dealing with your peculiarities, something you could do is:
def addlst(item):
"""add an item to a list"""
global l#
if isinstance(item, str): # type(item) == str is not recommanded
item = item.capitalize()
l.append(item)
But actually, note that doing so will "bind" your function to deal exclusively with the list named l in the global scope. And it looks like this is not what you want, since it appears that you want to be able to pass multiple list objects to your function. The best approach here is
def addlst(list_, item):
"""add an item to a list"""
if isinstance(item, str):
item = item.capitalize()
list_.append(item)
return list_
First off: a function should never inject a new name into the calling scope.
If the function works with a global variable, that needs to be documented and the caller has to ensure the global exists before calling it.
If the function takes an argument, there are two options. One, you can mutate it and have your function return None, or you can create a new value based on the argument and return that, leaving the argument unchanged. Very rarely, if ever, should your function modify an argument and return a value.
If you have your function return a new list, you can optionally take a list to modify, or create a brand new list inside your function.
Unrelated, but you shouldn't care what the type of item is, only that it is something that has a capitalize method that you can call. Just try it; if it doesn't, it will raise an AttributeError that you can catch, in which case you can simply use item as is.
Putting all this together, I recommend the third approach. add_to_list will take an item as the first argument, and an optional list as the second argument. If no list is given, the function will create a new list. In either case, you'll append the appropriately modified item to the list and return it.
def add_to_list(item, l=None):
# Note: this doesn't change the item you pass; it just rebinds
# the local name item to a *new* string if it succeeds.
try:
item = item.capitalize()
except AttributeError:
pass
if l is None:
l = []
return l + [item]
Then you can use
people = add_to_list('kev') # people == ['Kev']
people = add_to_list('bob') # people == ['Bob'], Kev is gone!
people = add_to_list('kev', people) # people == ['Bob', 'Kev'], Kev is back.
The more efficient version mentioned in the second approach modifies l in place; in this case, though, you have to provide a list; you can't create a new list.
def add_to_list(item, l):
try:
item = item.capitalize()
except AttributeError:
pass
l.append(item)
people = [] # Create the list in the *calling* scope, not in add_to_list
add_to_list('kev') # TypeError, missing an argument
add_to_list('kev', people) # people == ['Kev']
add_to_list('bob', people) # people == ['Kev', 'Bob']
The first approach is pretty poor; it restricts your function to working with a specific list whose name is hard-coded in the function, but I'll mention it here for completeness. Since the list is hard-coded, we'll change the name of the function to reflect that.
def add_to_people(item):
global people
try:
item = item.capitalize()
except AttributeError:
pass
people.append(item)
Now add_to_list can work with the global list people, but no other list.
people = []
add_to_people('kev')
add_to_people('bob')
And finally, in the interest of full disclosure, yes, add_to_people can create the list if it hasn't already:
def add_to_people(item):
global people
try:
people # Simply try to evaluate the name
except NameError:
people = []
# ...
However, if using a global name in the first place is bad, autovivifying it like this is worse. Avoid this style of programming wherever possible.
When you write addlst(people, 'kev'), you're telling your code that you want to execute the addlst function with a variable named people as first parameter.
Problem is: you never set this variable!
There are many ways to this; You could either initialize an empty list before calling the function:
people = []
addlst(people, 'kev')
Or make the parameter optional with a default value:
def addlst(item, l = None):
"""add an item to a list"""
if l is None:
l = []
if type(item) == str:
l.append(item.capitalize())
return l
else:
l.append(item)
return l
But that could be tricky, since lists are mutable objects.
NB: In your function, print will never be called because it stands after the return statement.
A last way
Eventually, you could also shorten your code by doing something like that:
mylist = []
item = "test"
mylist.append(item.capitalize() if type(item) == str else item)
In Python I have a for loop which calls a class, which in turn calls another class and so on, with classes manipulating data, performing sql inserts etc. The final class contains a list of all the files which have been created. I want to access this list from outside the class but I cannot work out how to!
(I know there is also a loop issue-will explain more below!)
A basic example is:
#A class to create the list
class Create_list():
def list(self,j):
l=j+1
#pass this variable to another class, get_list
Get_list().input(l)
#class get_list receives the number from create_list and appends it to mylist
class Get_list():
def input(self,l):
mylist=[]
mylist.append(l)
#print mylist
# loop through a list of numbers and feed them into the create_list class
j=10
for k in range(j):
Create_list().list(k)
#I want to access the list here. I have tried all of the below
readlist=Get_list().input().mylist # with ()
readlist=Get_list.input.mylist # without ()
x=Create_list() # create an object with class name
mylist=x.list().mylist #use above object
I have tried all the approaches in the last block of code.
I can't use the first two as the function list requires an input, which comes from the preceding class. (the error says that list() requires two arguments, only one is provided (self I assume)).
I have tried assigning the class to an object but this too does not work.
I realise that the for loop means that if I were to print mylist within def input there is only the value from that value of j.
I basically would like to access mylist, which has a list of values (l) from all of the values in j after that for loop has run.
Lots of stuff is wrong here so I'll just show a simple way to do it:
class Create_list(object):
def __init__(self):
self.list = []
def input_list(self, x):
l = x + 1
self.list.append(l)
j=10
cl = Create_list()
for k in xrange(j):
cl.input_list(k)
print cl.list
Another possibility is to return the list:
def add_one_to(j):
l=j+1
return(l)
def store(mylist, l):
mylist.append(l)
return(mylist)
Usage:
>>> mylist = []
>>> myintegerplusone = add_one_to(1)
>>> store(mylist, myintegerplusone)
>>> print(mylist)
[2]
In that case you could imagine a function as a craftsman, you give him something to fix/manipulate and he then returns the fixed/manipulated good back to you.
I think what you want is to store inside the class object the list using the "self" method and then access it from outside.
Try this code:
class CreateList():
def __init__(self):
self.list = []
if __name__ == "__main__":
c = CreateList()
c.list.append(4)
print c.list
I want to find the name of the function as it was called ... i.e. the name of the variable that called the function. Using the basic recipes i.e. with __name__, func_name, or inspecting the basic stack does not work for me. For example
def somefunc():
print "My name is: %s" % inspect.stack()[1][3]
a = somefunc
a()
# would output: out: "My name is: somefunc"
# whereas I want it to output: "My name is: a"
My gut says I can do this, but I can't figure it out. Any python guru's out there?
It's probably impossible to do this 100% correctly, but you could give the following a try:
import inspect
import parser
# this flatten function is by mike c fletcher
def flatten(l, ltypes=(list, tuple)):
ltype = type(l)
l = list(l)
i = 0
while i < len(l):
while isinstance(l[i], ltypes):
if not l[i]:
l.pop(i)
i -= 1
break
else:
l[i:i + 1] = l[i]
i += 1
return ltype(l)
# function we're interested in
def a():
current_func = eval(inspect.stack()[0][3])
last_frame = inspect.stack()[1]
calling_code = last_frame[4][0]
syntax_tree = parser.expr(calling_code)
syntax_tree_tuple = parser.st2tuple(syntax_tree)
flat_syntax_tree_tuple = flatten(syntax_tree_tuple)
list_of_strings = filter(lambda s: type(s)==str,flat_syntax_tree_tuple)
list_of_valid_strings = []
for string in list_of_strings:
try:
st = parser.expr(string)
list_of_valid_strings.append(string)
except:
pass
list_of_candidates = filter(lambda s: eval(s)==current_func, list_of_valid_strings)
print list_of_candidates
# other function
def c():
pass
a()
b=a
a(),b(),c()
a(),c()
c(),b()
This will print:
['a']
['a', 'b']
['a', 'b']
['a']
['b']
It's pretty ugly and complicated, but might work for what you need. It works by finding all variables used in the line that called this function and comparing them to the current function.
The problem here is indirection. You could probably do something complicated like inspect the stack, get the code for the module that called the function, parse the line number from the stack to find the label used to call the function in the local context, and then use that, but that won't necessarily give you what you want anyway. Consider:
def func(x):
print get_label_function_called_with()
def func_wrapper(func_in_func_wrapper):
return func_in_func_wrapper
func_label = func
func_from_func_wrapper = func_wrapper(func_label)
func_from_func_wrapper()
Should this print func, func_in_func_wrapper, func_label, or func_from_func_wrapper? It might seem like an obvious answer at first, but given that you never really know what sort of indirection is going on inside code you are calling, you really can't know for sure.
I would like to do the following:
print "CC =",CC
but as a function so that i only have to write the variable CC once. I can't work out how to do this in a function as it always evaluates CC as a floating point number (which it is).... Is there a way to accept the input to a function as both a string and floating point number?
I tried this:
def printme(a):
b='%s' % a
print b
return b
but of course it only prints the value of a, not its name.
You could use the inspect module (see also this SO question):
def printme(x):
import inspect
f = inspect.currentframe()
val = f.f_back.f_locals[x]
print x, '=', val
CC = 234.234
printme('CC') # <- write variable name only once
# prints: CC = 234.234
Perhaps a dictionary is a better approach to the problem. Assuming you have several name-value pairs that you want to use, you can put them in a dict:
params = {"CC": 1.2345, "ID": "Yo!", "foo": "bar"}
Then, for example, you could print all the names and values nicely formatted like this:
for key in params:
print "{0} = {1}".format(key, params[key])
But since it is still unclear why you are trying to do this, it's hard to tell whether this is the right way.
I think this is your required solution:
def printme(x):
keys_list = [key for key, value in globals().iteritems() if value == x]
print keys_list
for key in keys_list:
if id(globals()[key]) == id(x):
result = "%s = %s" %(key, x)
print result
break
return result
for example if you declare a variable:
>>> c=55.6
then result of printme(c) will be
>>> 'c = 55.6'
Note: This solution is based on globally unique id matching.
Not exactly what you want, but easy to do:
def printme(**kwargs):
for key, value in kwargs.items():
print '%s=%s' % (key, value)
return value
In [13]: printme(CC=1.23, DD=2.22)
CC=1.23
DD=2.22
Out[13]: 1.23
If I understand you correctly you want something like this?
def f(a):
print('{0}: = {1}'.format(locals().keys()[0], a))
Update:
I am aware that the example doesn't make a lot of sense, as it's basically the same as:
def f(a):
print('a: {0}'.format(a))
I merely wanted to point the OP to locals() as I didn't quite understand what he's trying to accomplish.
I guess this is more what he's looking for:
def f(**kwargs):
for k in kwargs.keys():
print('{0}: {1}'.format(k, kwargs[k]))
f(a=1, b=2)
If I understand you correctly you want a shorthand for printing a variable name and its value in the current scope? This is in general impossible without using the interpreters trace function or sys._getframe, which should in general only be used if you know what you're doing. The reason for this is that the print function has no other way of getting the locals from the calling scope:
def a():
x = 1
magic_print("x") #will not work without accessing the current frame
What you CAN do without these is explicitly pass the locals to a function like this:
def printNameAndValue(varname, values):
print("%s=%s" % (varname, values[varname]))
def a():
x = 1
printNameAndValue("x", locals()) #prints 'x=1'
EDIT:
See the answer by catchemifyoutry for a solution using the inspect module (which internally uses sys._getframe). For completeness a solution using the trace function directly - useful if you're using python 2.0 and inspect isn't available ;)
from sys import settrace
__v = {} #global dictionary that holds the variables
def __trace(frame, event, arg):
""" a trace function saving the locals on every function call """
global __v
if not event == "call":
return __trace
__v.update(frame.f_back.f_locals)
def enableTrace(f):
""" a wrapper decorator setting and removing the trace """
def _f(*a, **kwa):
settrace(__trace)
try:
f(*a, **kwa)
finally:
settrace(None)
return _f
def printv(vname):
""" the function doing the printing """
global __v
print "%s=%s" % (vname, __v[vname])
Save it in a module and use like this:
from modulenamehere import enableTrace, printv
#enableTrace
def somefunction():
x = 1
[...]
printv("x")
used a global variable to achieve this,func.__globals__.keys() contains all the variables passed to func, so I filtered out the name startin with __ and stored them in a list.
with every call to func() the func.__globals__.keys() gets updated with the new variable name,so compare the new varn with the older glo results in the new variable that was just added.
glo=[]
def func(x):
global glo
varn=[x for x in func.__globals__.keys() if not x.startswith('__') and x!=func.__name__]
new=list(set(varn)^set(glo))
print("{0}={1}".format(new[0],x))
glo=varn[:]
output:
>>> a=10
>>> func(a)
a=10
>>> b=20
>>> func(20)
b=20
>>> foo='cat'
>>> func(foo)
foo=cat
>>> bar=1000
>>> func(bar)
bar=1000