I'm trying to learn how to increase the speed of my code. This function is supposed to read a really long string and it seems to be a bit on the slow side. The input is a string which sholud be read and return a list depending on the "level" it is placed. I'm wondering if there is a way for me to increase the speed of my code below:
def readexpr1(q): # q=[[16,[[4,4],4]],[[32,[4,16]],64]]```
y = []
lvl = 0
s = ""
for n in q:
# print(y, "-")
if n == "[":
lvl = 1
if s:
y.append(int(s)*(2**(lvl)))
s = ""
elif n == "]":
lvl -= 1
if s:
y.append(int(s)*(2**(lvl)))
s = ""
elif n == ",":
if s:
y.append(int(s)*(2**(lvl)))
s = ""
else:
s = "".join([s, n])
return y```
CodePudding user response:
Provided that each value has to be multiplied by 2**L
where L
is the nesting level, the following code makes the required conversion.
import ast
def collect(l, level):
"""Collect multiplied values from a list at `level`."""
for x in l:
if type(x) == list:
yield from collect(x, level 1)
else:
yield x * 2**level
def readexpr2(q):
"""Convert string to the list of multiplied values."""
s = ast.literal_eval(q)
return list(collect(s, 1))
print(readexpr2("[[16,[[4,4],4]],[[32,[4,16]],64]]"))
# Gives [64, 64, 64, 32, 256, 64, 256, 256]
CodePudding user response:
I wholeheartedly believe that dlask's solution is the correct solution here, however, a fun idea struck me while thinking about this problem, which I'd like to share.
Warning: this approach is definitely hackier, and it suffers from a huge security concern- eval()
can and will run any functions you put in there, so if your input list contains malicious values that are actually legit python code, it'd run those too. This is why dlask's solution uses ast.literal_eval()
- it does not execute functions, and does not suffer from this security problem.
Disclaimers aside, lets get to the code:
def double(nums):
return (2*x for x in nums)
def read_expr(expr):
code = "[" text.replace("]", "])").replace("[", "*double([") "]"
return eval(code)
As you can see, it does work:
>>> expr = "[[16,[[4,4],4]],[[32,[4,16]],64]]"
>>> read_expr(expr)
[64, 64, 64, 32, 256, 64, 256, 256]
Now what's going on? With each list, its replacing that list, say for example, [4, 16]
with the text *double([4, 16])
which means that it calls double()
on our list (doubling each element), and then unpacks that into the enclosing list (flattening the nested lists). This happens for every single list (including the outer most one), which is why I had to surround the expression with [
and ]
explicitly at the end.
Again, this isn't meant to be a good solution that you should copy/paste into production. Its an illustration of another way to view the same problem, using some clever trickery to do less work. For real use, please use dlask's solution.