parse a string with brackets to arguments - python

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)))

Related

extract list or tuple inside string

I am getting input from an external device connected via Ethernet, and it is passing several values of string type e.g. value = '(2,2)\n'. I would like to assign these values to a list or tuple variable e.g. final_value = (2,2).
The code I am using is the following:
import socket
sock = socket.socket()
value =sock.recv(buffersize=2048)
formatted_value = eval(value)
I read that the eval function I am using at this moment to get the list is not a very safe approach, as the external device could pass a dangerous script. So, I would like to know if there is any alternative, similar to the function int(), which can be used to get an integer from a string.
Use ast module literal_eval method for a safer eval
import ast
formatted_value = ast.literal_eval(value)
If you know the input contains a tuple
from ast import literal_eval as make_tuple
make_tuple(value)
Well to give the alternative approach you can do.
s = '(2, 2)\n'
s = s.strip()
if s.startswith('(') and s.endswith(')'):
tup = tuple(int(i) for i in s[1:-1].split(','))
print(tup)
Or if you want a list
s = '(2, 2)\n'
s = s.strip()
if s.startswith('(') and s.endswith(')'):
lst = [int(i) for i in s[1:-1].split(',')]
print(lst)

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')]

How to return a list in a function without " "

I have a list a = ['type1','type2','type3'] and I have a function. In the function I want to return the list, but without the ' '. Is this possible? When I return I get with the " ".
Python shows you values in containers such as a list as value representations; for strings that means you are shown the literal string value, with the quotes.
That does not mean the string values contain actual quotes.
You can of course print something to the terminal that shows just the string values, joined by commas and surrounded by square brackets:
print '[{0}]'.format(', '.join(yourlist))
but that won't alter the list object, nor should you want to.
As for your code, what is wrong here is that you are modifying a global list, in the module. When you import the module the make_table(tresort_liste, sla_liste, wb) line runs which alters table_liste and lbmer:
>>> import yourmodule
[['b\xc3\xb8k', 155.73786516665265], ['eik', 200.23439807141054], ['rogn', 149.1107219680717], ['poppel', 152.3522594021602], ['furu', 876.0254915624212], ['gran', 350.4101966249685], ['douglasgran', 778.6893258332633]]
>>> yourmodule.table_liste
[['b\xc3\xb8k', 155.73786516665265], ['eik', 200.23439807141054], ['rogn', 149.1107219680717], ['poppel', 152.3522594021602], ['furu', 876.0254915624212], ['gran', 350.4101966249685], ['douglasgran', 778.6893258332633]]
>>> yourmodule.lbmer
[155.73786516665265, 200.23439807141054, 149.1107219680717, 152.3522594021602, 876.0254915624212, 350.4101966249685, 778.6893258332633]
Your test then runs the make_table function again and your test arguments are appended to these global lists. As a result, the method returns more data than you are expecting and your test fails:
>>> names = ['abc', 'def', 'ghi']
>>> sla = [1, 2, 4]
>>> lbm = [15.3, 7.65, 3.825]
>>> zip(*yourmodule.make_table(names, sla, 50000))
[('b\xc3\xb8k', 'eik', 'rogn', 'poppel', 'furu', 'gran', 'douglasgran', 'abc', 'def', 'ghi'), (155.73786516665265, 200.23439807141054, 149.1107219680717, 152.3522594021602, 876.0254915624212, 350.4101966249685, 778.6893258332633, 155.73786516665265, 200.23439807141054, 149.1107219680717)]
Moral of the story: Don't use globals.
Your code could be simplified to:
import math
def compute_lbm(sla, wb):
return (0.3 + math.sqrt(0.0045 * wb)) / sla
def make_table(tresorter, slaer, wb):
return [[sort, compute_lbm(sla, wb)] for sort, sla in zip(tresorter, slaer)]
if __name__ == '__main__':
sla_liste = [0.045, 0.035, 0.047, 0.046, 0.008, 0.020, 0.009]
tresort_liste = ["bøk", "eik", "rogn", "poppel", "furu", "gran", "douglasgran"]
wb = 10000
print make_table(tresort_liste, sla_liste, wb)
which incidentally won't use globals. The print make_table() call at the end will not be executed when used as a module either, only if you run this file directly as the main script.
You're use of assertAlmostEqual() is missing the places or delta argument, you need to specify how close the output needs to match.
The quotes you were chasing are definitely not an issue here.

python convert a string to arguments list

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.

how to deserialize a python printed dictionary?

I have python's str dictionary representations in a database as varchars, and I want to retrieve the original python dictionaries
How to have a dictionary again, based in the str representation of a dictionay?
Example
>>> dic = {u'key-a':u'val-a', "key-b":"val-b"}
>>> dicstr = str(dic)
>>> dicstr
"{'key-b': 'val-b', u'key-a': u'val-a'}"
In the example would be turning dicstr back into a usable python dictionary.
Use ast.literal_eval() and for such cases prefer repr() over str(), as str() doesn't guarantee that the string can be converted back to useful object.
In [7]: import ast
In [10]: dic = {u'key-a':u'val-a', "key-b":"val-b"}
In [11]: strs = repr(dic)
In [12]: strs
Out[12]: "{'key-b': 'val-b', u'key-a': u'val-a'}"
In [13]: ast.literal_eval(strs)
Out[13]: {u'key-a': u'val-a', 'key-b': 'val-b'}
You can use eval() or ast.literal_eval(). Most repr() strings can be evaluated back into the original object:
>>> import ast
>>> ast.literal_eval("{'key-b': 'val-b', u'key-a': u'val-a'}")
{'key-b': 'val-b', u'key-a': u'val-a'}
ast.literal_eval could be the way to do it for simple dicts, BUT you should probably rethink your design and NOT save such text in database at first place. e.g.
import collections
d = {'a':1, 'b': collections.defaultdict()}
import ast
print ast.literal_eval(repr(d))
This will not work and throw ValueError('malformed string') basically you won't be convert back dict if it contains any non basic types.
Better way is to dump dict using pickle or json or something like that e.g.
import collections
d = {'a':1, 'b': collections.defaultdict()}
import json
print json.loads(json.dumps(d))
Summary: serialize using repr, deserialize using ast.literal_eval is BAD, serialize using json.dumps and deserialize using json.loads is GOOD

Categories

Resources