0

I have scraped some strings from emails into a list. The strings correspond to the names of functions which I want to be able to call later. I cannot call them in their current form so is there a way of converting the list of strings into a list of functions that I can call?

For example:

a = ['SU', 'BT', 'PL']
str = 'sdf sghf sdfgdf SU agffg BL asu'
matches = [x for x in a if x in str]
print(matches)

returns:

 ['SU', 'BL']

But I cannot call functions SU and BL from this list given the format.

Andy
  • 509
  • 2
  • 7
  • You could make a dictionary of functions, where the string is a key, and the function is the value. Then run `your_dict[key]()`. Did you try that? – Mahrkeenerh Oct 13 '21 at 10:05
  • The function name in Python serves as a variable itself. So what you actually want to do is retrieve a variable reference from its name. The way to do it is by running `import sys` and then `getattr(sys.modules[__name__], FUNCTION_NAME)` . It will retrieve the variable for you from the global scope. For example, if you have already defined a function called `myfunc`, then you can do `f = getattr(sys.modules[__name__], 'myfunc')`. Then you can call the function from your variable: `f()`. It will call the function `myfunc()`. – SomethingSomething Oct 13 '21 at 10:17
  • 1
    a suggestion - don't use `str` as a variable. Thank you – Mahrkeenerh Oct 13 '21 at 10:20
  • 1
    @Mahrkeenerh very true. `>>> str(4) ; '4' ; >>> str = 'blalsd' ; >>> str(4) ; Traceback (most recent call last): File "", line 1, in TypeError: 'str' object is not callable` – SomethingSomething Oct 13 '21 at 10:21
  • 1
    Coincidentally, this example illustrates how function names are just like other variables in Python – SomethingSomething Oct 13 '21 at 10:26

1 Answers1

4

With this example:

def my_func1():
    print("ONE")

def my_func2():
    print("TWO")

You can try eval, but it's not a good practise: (explanation)

eval("my_func1")()

Or you can assign this function to a string equivalent (inside a dictionary), and run that:

my_func_dict = {
    "my_func1": my_func1, 
    "my_func2": my_func2
}

my_func_dict["my_func1"]()

Both of these examples will print ONE.

Or closer to your example:

a = [my_func1, my_func2]

matches = [x for x in a if x.__name__ in str]

# matches now has two funcions inside, so you can run either:
matches[0]()
matches[1]()
Mahrkeenerh
  • 1,104
  • 1
  • 9
  • 25
  • 2
    "You can't directly do: `run("my_func")`" this is false, you can do `eval("my_func")()` or similar – KGS Oct 13 '21 at 10:12
  • that's on me, wasn't aware of this. I'll edit the answer. – Mahrkeenerh Oct 13 '21 at 10:13
  • 3
    You can use the `__name__` attribute. Put callables in the list and change `matches = [x for x in a if x in str]` to `matches = [x for x in a if x.__name__ in str]` – MSH Oct 13 '21 at 10:17
  • 1
    Using `eval` can be unsafe and dangerous depending on the string's source. If the input comes from a user form, someone could enter `(lambda: print('I do something evil!'))` which can be executed with `eval("(lambda: print('I do something evil!'))")()`. There are other reasons why you should avoid `eval`: https://stackoverflow.com/a/1832957/42659 – Thomas Oct 13 '21 at 21:44