问题
So I've got a string that looks like "012 + 2 - 01 + 24"
for example. I want to be able to quickly (less code) evaluate that expression...
I could use eval() on the string, but I don't want 012
to be represented in octal form (10), I want it to be represented as an int (12).
My solution for this works, but it is not elegant. I am sort of assuming that there is a really good pythonic way to do this.
My solution:
#expression is some string that looks like "012 + 2 - 01 + 24"
atomlist = []
for atom in expression.split():
if "+" not in atom and "-" not in atom:
atomlist.append(int(atom))
else:
atomlist.append(atom)
#print atomlist
evalstring = ""
for atom in atomlist:
evalstring+=str(atom)
#print evalstring
num = eval(evalstring)
Basically, I tear appart the string, and find numbers in it and turn them into ints, and then I rebuild the string with the ints (essentially removing leading 0's except where 0 is a number on its own).
How can this be done better?
回答1:
I'd be tempted to use regular expressions to remove the leading zeroes:
>>> re.sub(r'\b0+(?!\b)', '', '012 + 2 + 0 - 01 + 204 - 0')
'12 + 2 + 0 - 1 + 204 - 0'
This removes zeroes at the start of every number, except when the number consists entirely of zeroes:
- the first
\b
matches a word (token) boundary; - the
0+
matches one or more consecutive zeroes; - the
(?!\b)
(negative lookahead) inhibits matches where the sequence of zeroes is followed by a token boundary.
One advantage of this approach over split()
-based alternatives is that it doesn't require spaces in order to work:
>>> re.sub(r'\b0+(?!\b)', '', '012+2+0-01+204-0')
'12+2+0-1+204-0'
回答2:
You can do this in one line using lstrip() to strip off any leading zeros:
>>> eval("".join(token.lstrip('0') for token in s.split()))
37
回答3:
I'd like to do it this way:
>>> s = '012 + 2 + 0 - 01 + 204 - 0'
>>> ' '.join(str(int(x)) if x.isdigit() else x for x in s.split())
'12 + 2 + 0 - 1 + 204 - 0'
Use float()
if you want to handle them too :)
回答4:
int
does not assume that a leading zero indicates an octal number:
In [26]: int('012')
Out[26]: 12
Accordingly, you can safely evalute the expression with the following code
from operator import add, sub
from collections import deque
def mapper(item, opmap = {'+': add, '-': sub}):
try: return int(item)
except ValueError: pass
return opmap[item]
stack = deque()
# if item filters out empty strings between whitespace sequences
for item in (mapper(item) for item in "012 + 2 - 01 + 24".split(' ') if item):
if stack and callable(stack[-1]):
f = stack.pop()
stack.append(f(stack.pop(), item))
else: stack.append(item)
print stack.pop()
Not a one-liner, but it is safe, because you control all of the functions which can be executed.
来源:https://stackoverflow.com/questions/9843033/pythonic-way-to-eval-all-octal-values-in-a-string-as-integers