1

Related to this question Command line arguments in python.

With the SYS module, how can I use a command line argument as a function name and function value, within my code - without importing some other module?

I'd like a solution that uses sys only. Also, please no variable-length params answers. Those are confusing. Assume that just the function name and one function variable are specified at the command line.

import sys

def reversal(aaa): return aaa[::-1]

a = sys.argv[1]
b = sys.argv[2]

print a(b)

At the command line

cpu_location$ python blah.py reversal 'abcdefg'

Traceback (most recent call last):
File "blah.py", line 8, in <module>
print a(b)
TypeError: 'str' object is not callable

I want to know how to make sys.argv[1] be considered a function name, thereby calling the function I have defined.

The other posts I see on this are a mash up of: - dealing with C/C++ and adding some other module - not using sys at all - using the argv items as values for functions, and names of other files, instead of names of functions

Community
  • 1
  • 1
VISQL
  • 1,960
  • 5
  • 29
  • 41

3 Answers3

6

Better than the eval solution would be:

a = globals()[sys.argv[1]]
a(b)

globals() returns a dictionary mapping global variables names to those global variables. So globals()['reversal'] evaluates to the reversal function.

It's safer than the eval function. With your approach you could do something like:

python blah.py 'lambda x: x+"hi"' foobar

Which would print foobarhi, which is unexpected because that's not a function name.

Claudiu
  • 224,032
  • 165
  • 485
  • 680
  • I don't get it. What's wrong with your example? In my case, the first parameter will always be a function I defined in the script itself. That's why I want it to be the function name. – VISQL Apr 24 '15 at 21:26
  • @VISQL: It's just a general guideline to avoid `eval` and `exec` whenever possible. It allows people to make the script do just about whatever they want. With this approach you constrain the behavior to exactly what you want, namely, getting a function from a function name. – Claudiu Apr 24 '15 at 21:50
  • If I do `globals()['funname']` the call is ignored, if I do it the normal `funname()` way it works. Anything I have forgot to consider? – Timo Apr 25 '22 at 10:26
1

2 hours later, I find the answer. I think it's worth it to post it here in a very simple fashion.

Basiclaly there is no "function" data type in Python, but someone did mention a function eval, which is built-in. Execute python commands passed as strings in command line using python -c (No -c is needed for my own example)

The solution, is to change

a = sys.argv[1]

to

a = eval(sys.argv[1])

This will make the passed in word, reversal, be evaluated. It will evaluate to a function. Then the a(b) call will be a perfect call of a function on a string, like how it's defined. Output will be like:

cpu_location$ python blah.py reversal unquoted
detouqnu

cpu_location$ python blah.py reversal 'withquotes'
setouqhtiw
Community
  • 1
  • 1
VISQL
  • 1,960
  • 5
  • 29
  • 41
  • **Please** don't use `eval()`, especially without sanitizing the input. What it does is evaluate the argument as Python code, which is extremely dangerous. Read [this](http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html) for more info. – MattDMo Apr 24 '15 at 18:13
  • Thanks. Article is over my head at the moment. I will keep this in mind if I build something more intricate. This project is just for me, to practice. Easier to test my simple functions at command line, than to put all my tests inside the script and get a screen full of text when I run the script - with output for every single function in there. – VISQL Apr 24 '15 at 21:31
  • (Years later). I also used to this `sys.argv` method to build my own file splitter. It had positional arguments, and could be told to split just one file into `x` lines per file, or to split all files in a directory in the same way. These args were parsed from the command line using `sys.argv`. However, none of the items passed at the command line were evaluated as functions. – VISQL Aug 27 '19 at 14:35
0

use google module: fire

pip install fire

Here's a simple example:

import fire

class Calculator(object):
  """A simple calculator class."""

  def double(self, number):
    return 2 * number

if __name__ == '__main__':
  fire.Fire(Calculator)

Then, from the command line, you can run:

python calculator.py double 10  # 20
python calculator.py double --number=15  # 30
jkzhao
  • 3
  • 3
  • Nice functionality. In this case, `Calculator` can be called only though. Back when I was writing this, I intended to have multiple functions of different names (not ant class) in one file that I would like to call from the command line. `argv` + `eval` works for this purpose. Any name passed as the first argument would be treated as the function name, and subsequent args as arguments to that function. Also, I tended to prefer small examples to be done via built-ins, rather than modules that aren't defaults. – VISQL May 27 '19 at 13:56