简体   繁体   中英

Evolution process of eval(str) in python

I find a code writen as follow:

nav_str = "'\\'!@34QWer\\''"
native_structure = eval(nav_str, {'__builtins__':{'set' : set}}, {})

>>> native_structure
>>> '!@34QWer'

I just wonder how eval(nav_str, {'__builtins__':{'set' : set}}, {}) transfer "'\\\\'!@34QWer\\\\''" to '!@34QWer'

update :
I have tried to understand @user2357112 and @Blender for minutes,but I still can not realize this process.
As the answser,I think eval("'\\\\'!@34QWer\\\\''") should return '\\\\'!@34QWer\\\\'' , but '\\\\'!@34QWer\\\\'' is invalid.
What is the evolution process of "'\\\\'!@34QWer\\\\''" in eval()?

Let's take a look at the contents of nav_str , without the string-literal escaping:

>>> print nav_str
'\'!@34QWer\''

These are the literal characters that nav_str contains. A single quote, a backslash, a single quote, an exclamation mark, etc. These characters form a valid Python string literal. eval evaluates the string literal, returning a string.

First, let's look at this part:

eval(nav_str, {'__builtins__':{'set' : set}}, {})

Normally, eval evaluates the expression you gave it in the normal environment, so these do the same thing:

>>> def foo(): print 'Hi'
>>> foo()
Hi
>>> eval('foo()')
Hi

But sometimes you don't want that. You probably don't want to take an arbitrary string off the web and eval it in your normal environment, unless you want to let people, eg, __import__('os').system('rm -rf /') . So, you can customize the environment the expression is evaluated in. The exact details are described in the docs above, but here's a quick example:

>>> def foo(): print 'Hi!'
>>> def bar(): print 'Bye!'
>>> eval('foo()')
Hi!
>>> eval('foo()', {'__builtins__': {}}, {})
NameError: name 'foo' is not defined
>>> eval('foo()', {'__builtins__': {'foo': bar}}, {})
Bye!

So, why would you ever want an environment that has no names in it except for set ?

Most likely because you wanted something like ast.literal_eval , but that also provided some way to handle empty sets. literal_eval only evaluates Python literals. But there is no literal for the empty set; you have to write set() instead. And ast.literal_eval can't handle set() . But the convoluted call to eval in your example can.

Of course this isn't perfect. Using eval allows all kinds of expressions that don't use names—math, list comprehensions, generator expressions, …


Now, let's look at the string you're evaluating:

nav_str = "'\\'!@34QWer\\''"

Well, there are no calls to set there. Or references to any names at all. So the extra complexity was just a red herring. You'd get the exact same thing with just eval(nav_str) .

So, what's the deal with that string?

Well, compare:

>>> print '\'!@34QWer\''
'!@34QWer'
>>> print eval(r"'\'!@34QWer\''")
'!@34QWer'
>>> print eval("'\\'!@34QWer\\''")
'!@34QWer'

The only reason you need

The only reasons it looks so complicated are that (a) whoever wrote the expression insisted on using single quotes even though the string itself has single quotes inside, necessitating backslash escaping, and (b) whoever wrote the code that stored the expression in a variable insisted on not using a raw string, necessitating doubling those backslashes.

You could write an equivalent expression with no backslashes:

>>> nav_str = '''"'!@34QWer'"'''

(It's not the same string literal expression, but it's a string literal expression that evaluates to the same value.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM