python convert a string to arguments list - python

Can I convert a string to arguments list in python?
def func(**args):
for a in args:
print a, args[a]
func(a=2, b=3)
# I want the following work like above code
s='a=2, b=3'
func(s)
I know:
list can, just use *list, but list can't have an element like: a=2
and eval can only evaluate expression
which would be like:
def func2(*args):
for a in args:
print a
list1=[1,2,3]
func2(*list1)
func2(*eval('1,2,3'))

You could massage the input string into a dictionary and then call your function with that, e.g.
>>> x='a=2, b=3'
>>> args = dict(e.split('=') for e in x.split(', '))
>>> f(**args)
a 2
b 3

You want a dictionary, not an 'argument list'. You also would be better off using ast.literal_eval() to evaluate just Python literals:
from ast import literal_eval
params = "{'a': 2, 'b': 3}"
func(**literal_eval(params))
Before you go this route, make sure you've explored other options for marshalling options first, such as argparse for command-line options, or JSON for network or file-based transfer or persistence.

You can use the string as an argument list directly in an call to eval, e.g.
def func(**args):
for a in args:
print( a, args[a])
s = 'a=2, b=3'
eval('func(' + s + ')')
>>>b 3
>>>a 2
Note that func needs to be in the namespace for the eval call to work like this.

Related

How to read inputs separated by spaces in python, as different data types dynamically preferably onto a List?

Is there a way to insert first input as str the next input as int and the next as float onto a list. Assuming the three inputs are separated by spaces are taken as input.
data = map(str,int,float,input.split()) # Something like this, I know the syntax here is wrong
You can do it simply:
task = input().split()
task[1] = int(task[1])
task[2] = float(task[2])
or in a more convoluted way:
task = [ f(x) for (f, x) in zip([str, int, float], input().split()) ]
Yup, you can do this. Try this:
>>> task = input().split()
hello 3 42.3
>>> task # task is a list of strings
['hello', '3', '42.3']
# get the 3 parts of task
>>> string = task[0]
>>> int = int(task[1])
>>> float = float(task[2])
>>> string, int, float
('hello', 3, 42.3)
There isn't some already available way to do it but you can write your own function for it; such as this:
def my_input(data_types):
user_input = input()
split_user_input = user_input.split()
converted_input_tokens = []
for input_token, data_type in zip(split_user_input, data_types):
converted_input_tokens.append(data_type(input_token))
return converted_input_tokens
It will do exactly(no more, no less) what you showed in the example you gave. You can use it this way:
>>> my_input((str, int, float))
1 2 3
Which will return:
['1', 2, 3.0]
It can be made much more generic, of course. For example, you could add arguments for the input prompt, the sep and maxsplit for the str.split method used in the function, etc.
If you are in doubt about how to do that, have a look at the official documentation for input, str.split and also do some research on type-conversions in Python.
you can explicitly define each input as particular type.

parse a string with brackets to arguments

I need to parse some strings into function variable lists.
I can have a simple string, such as vars = '3,5,1'
I parse it using args = [int(arg) if arg.isdigit() else arg for arg in vars.split(',')]
However I might get a string such as vars = [1, 2, 5, 4], 1, 5
And I want my result to be [[1,2,5,4],1,5]
How can I modify my parsing to support this case?
You can use the built-in ast module:
import ast
result = ast.literal_eval(f'[{vars}]')
This will treat vars as an ordinary list literal.
import ast
print(list(ast.literal_eval(vars)))

Convert tuple-strings to tuple of strings

My Input is:
input = ['(var1, )', '(var2,var3)']
Expected Output is:
output = [('var1', ), ('var2','var3')]
Iterating over input and using eval/literal_eval on the tuple-strings is not possible:
>>> eval('(var1, )')
>>> NameError: name 'var1' is not defined
How can I convert an item such as '(var1, )' to a tuple where the inner objects are treated as strings instead of variables?
Is there a simpler way than writing a parser or using regex?
For each occurrence of a variable, eval searches the symbol table for the name of the variable. It's possible to provide a custom mapping that will return the key name for every missing key:
class FakeNamespace(dict):
def __missing__(self, key):
return key
Example:
In [38]: eval('(var1,)', FakeNamespace())
Out[38]: ('var1',)
In [39]: eval('(var2, var3)', FakeNamespace())
Out[39]: ('var2', 'var3')
Note: eval copies current globals to the submitted globals dictionary, if it doesn't have __builtins__. That means that the expression will have access to built-in functions, exceptions and constants, as well as variables in your namespace. You can try to solve this by passing FakeNamespace(__builtins__=<None or some other value>) instead of just FakeNamespace(), but it won't make eval 100% safe (Python eval: is it still dangerous if I disable builtins and attribute access?)
Try this:
tuples = [tuple(filter(None, t.strip('()').strip().split(','))) for t in input]
For example:
In [16]: tuples = [tuple(filter(None, t.strip('()').strip().split(','))) for t in input]
In [17]: tuples
Out[17]: [('var1',), ('var2', 'var3')]
We're iterating through our list of tuple strings, and for each one, removing the ()s, then splitting our string into a list by the ,, and then converting our list back into a tuple. We use filter() to remove empty elements.
I like vaultah's solution. Here's another one with ast.literal_eval and re if eval is not an option:
>>> import re
>>> from ast import literal_eval
>>> [literal_eval(re.sub('(?<=\(|,)(\w+)(?=\)|,)', r'"\1"', x)) for x in input]
[('var1',), ('var2', 'var3')]

variadic / arbitrary number of *args or ** kwargs in str.format() in Python

I'm making a socket client and need to define a function which sends the following kind of message to server: sometext,[name_1],[name_2],...,[name_n]. Actually the message is more complex, but for the sake of an example I simplified it.
Where:
... - represents the rest of the names (comma delimited) between name_2 and the last name_n
there could be arbitrary number of names each time
I know there are *args and **kwargs, but how to correctly use them in this case with str.format()? I need something like this:
def send_names(*args):
print('sometext,{*args},'.format(*args)) # as an example we just print
I know this code doesn't work because {*args} is illegal here.
The code works when I know the number of *args beforehand, like in here:
def send_names(*args):
print('sometext,{0},{1},{2}'.format(*args)) # as an example we just print
What am I missing?
You can join the *args to accomplish what you want:
def send_names(*args):
print('sometext, {0}'.format(', '.join(args)))
send_names('message1', 'message2', 'message3')
result:
sometext, message1, message2, message3
You cannot use *args or **kwargs to apply to a variable number of slots, no. You'd have to create the slots yourself based on the length:
','.join(['{}'] * len(args)).format(*args)
You can then interpolate the result of that into another template as needed. The above works with any type of argument normally accepted by a formatting slot.
Demo:
>>> args = ('foo', 'bar')
>>> ','.join(['{}'] * len(args)).format(*args)
'foo,bar'
>>> args = ('foo', 'bar', 'baz')
>>> ','.join(['{}'] * len(args)).format(*args)
'foo,bar,baz'
>>> args = (1, 2, 3)
>>> ','.join(['{}'] * len(args)).format(*args)
'1,2,3'
I think you should avoid using .format and just ', '.join the args as needed. You didn't mention any specific reason why you need to .format the strings in the question. Just like the solution by #Selcuk

Get functions called in a Python expression

I have a database that holds the name of Python functions and a string for their code. I want the user to be able to enter a Python code and see the result. The problem is that I need to know the names of the functions they call in order to retrieve the code from the database. For instance, if they enter cubic_fit(1, 2, get_data()), I need a way to get the function names cubic_fit and get_data. Is there a good way to do this?
The built-in function compile will do that for you exactly:
>>> compile("cubic_fit(1, 2, get_data())", '<string>', 'eval').co_names
('cubic_fit', 'get_data')
And it is safe to run. No code is actually being executed just compiled.
A quick example to you started. Note that you'll be expecting valid python semantics for this to work.
You can extend this to also parse your arguments...
import token, tokenize, StringIO
def extract_names(src):
rawstr = StringIO.StringIO(unicode(src))
tokens = tokenize.generate_tokens(rawstr.readline)
for i, item in enumerate(tokens):
toktype, toktext, (srow,scol), (erow,ecol), line = item
if token.tok_name[toktype] == 'NAME':
print 'name:', toktext
extract_names("cubic_fit(1, 2, get_data())")
# --> output:
# name: cubic_fit
# name: get_data
If you just want the names, then the compile() and co_names method will work best.
You also might take advantage of the capability of eval() to use any mapping object as its locals parameter. You could create a mapping object to look up and compile the objects from your database as needed by eval().
Example:
class LookitUp(object):
def __init__(self):
# simulate some data
self.d = { "foo": "def foo(a):\n return a + 2"}
def __getitem__(self,key):
localdict = {}
c = compile(self.d.get(key,""),"<string>","exec")
eval(c,globals(),localdict)
return localdict[key]
d = LookitUp()
def bar(a):
return a - 1
print "foo from database :",eval("foo(3)",globals(), d)
print "bar from globals():",eval("bar(3)",globals(), d)
print "foo(bar(3)) :",eval("foo(bar(3))",globals(), d)
Result:
foo from database : 5
bar from globals(): 2
foo(bar(3)) : 4
You may need to modify based on what your source in the database looks like, but it's a place to start.

Categories

Resources