Using eval with substitutions

Discussion in 'Python' started by abhishek@ocf.berkeley.edu, Aug 31, 2006.

  1. Guest

    >>> a,b=3,4
    >>> x="a+b"
    >>> eval(x)

    7
    >>> y="x+a"


    Now I want to evaluate y by substituting for the evaluated value of x.
    eval(y) will try to add "a+b" to 3 and return an error. I could do
    this,
    >>> eval(y.replace("x",str(eval(x))))

    10

    but this becomes unwieldy if I have
    >>> w="y*b"

    and so on, because the replacements have to be done in exactly the
    right order. Is there a better way?

    Thanks,
    Abhishek
     
    , Aug 31, 2006
    #1
    1. Advertising

  2. wrote:

    >>>> a,b=3,4
    >>>> x="a+b"
    >>>> eval(x)

    > 7
    >>>> y="x+a"

    >
    > Now I want to evaluate y by substituting for the evaluated value of x.
    > eval(y) will try to add "a+b" to 3 and return an error. I could do
    > this,
    >>>> eval(y.replace("x",str(eval(x))))

    > 10
    >
    > but this becomes unwieldy if I have
    >>>> w="y*b"

    > and so on, because the replacements have to be done in exactly the
    > right order. Is there a better way?


    instead of

    x = "a+b"

    do

    x = "a+b"
    x = eval(x)


    or

    x = eval("a+b")

    (I'm afraid I don't really understand the point of your examples; what
    is it you're really trying to do here ?)

    </F>
     
    Fredrik Lundh, Aug 31, 2006
    #2
    1. Advertising

  3. Guest

    Fredrik Lundh wrote:
    > (I'm afraid I don't really understand the point of your examples; what
    > is it you're really trying to do here ?)


    A function is passed a bunch of string expressions like,
    x = "a+b"
    y= "x*a"
    z= "x+y"

    where a and b are assumed to have been assigned values in the local
    namespace. Now I have to evaluate another string such as,
    "z+y+x"

    So as you say, I could do:
    x=eval(x), y=eval(y), z=eval(z) and finally eval("z+y+x") but the
    problem is that the initial strings are in no particular order, so I
    don't know the sequence in which to perform the first 3 evaluations. I
    was wondering if there was a simple way to 'pattern-match' so that the
    required substitutions like z->x+y->x+x*a->(a+b)+(a+b)*a could be done
    automatically.

    I apologise if this is still not quite clear.

    Abhishek
     
    , Aug 31, 2006
    #3
  4. Peter Otten Guest

    wrote:

    > Fredrik Lundh wrote:
    >> (I'm afraid I don't really understand the point of your examples; what
    >> is it you're really trying to do here ?)

    >
    > A function is passed a bunch of string expressions like,
    > x = "a+b"
    > y= "x*a"
    > z= "x+y"
    >
    > where a and b are assumed to have been assigned values in the local
    > namespace. Now I have to evaluate another string such as,
    > "z+y+x"
    >
    > So as you say, I could do:
    > x=eval(x), y=eval(y), z=eval(z) and finally eval("z+y+x") but the
    > problem is that the initial strings are in no particular order, so I
    > don't know the sequence in which to perform the first 3 evaluations. I
    > was wondering if there was a simple way to 'pattern-match' so that the
    > required substitutions like z->x+y->x+x*a->(a+b)+(a+b)*a could be done
    > automatically.


    Here is something to start with:

    class EvalDict(object):
    def __init__(self, namespace):
    self.namespace = namespace
    def __getitem__(self, key):
    value = self.namespace[key]
    if isinstance(value, str):
    self.namespace[key] = value = eval(value, {}, self)
    return value

    def smart_eval(expr, namespace):
    return eval(expr, {}, EvalDict(namespace))

    def f():
    a = 2
    b = 3
    x = "a+b"
    y = "x*a"
    z = "x+y"

    return smart_eval("x + y + z", locals())

    if __name__ == "__main__":
    print f()

    Beware of infinite recursion:

    # RuntimeError: maximum recursion depth exceeded
    smart_eval("a + b", dict(a="b", b="a"))

    Peter
     
    Peter Otten, Aug 31, 2006
    #4
  5. Duncan Booth Guest

    wrote:

    > So as you say, I could do:
    > x=eval(x), y=eval(y), z=eval(z) and finally eval("z+y+x") but the
    > problem is that the initial strings are in no particular order, so I
    > don't know the sequence in which to perform the first 3 evaluations. I
    > was wondering if there was a simple way to 'pattern-match' so that the
    > required substitutions like z->x+y->x+x*a->(a+b)+(a+b)*a could be done
    > automatically.
    >

    Sounds a bit of an odd thing to do, but:

    >>> def evaluate(var, names):

    expr = names[var]
    names['__builtins__'] = {}
    if isinstance(expr, basestring):
    del names[var]
    code = compile(expr, "<%s>"%var, "eval")
    for v in code.co_names:
    evaluate(v, names)
    names[var] = eval(code, names, names)
    del names['__builtins__']
    return names[var]

    >>> names = { 'a': 3, 'b': 2, 'x': 'a+b', 'y': 'x*a', 'z': 'x+y' }
    >>> evaluate('z', names)

    20
    >>> names

    {'a': 3, 'b': 2, 'y': 15, 'x': 5, 'z': 20}
    >>>
     
    Duncan Booth, Aug 31, 2006
    #5
  6. Abhishek,

    I hesitate to propose this idea, because there's got to be a better (more conventional) way of doing this. Anyway consider
    this:

    >>> x = "a+b"; y = "x*a; z = "x+y" # Your definitions
    >>> import SE
    >>> def f (x, y, z):

    substitutions = 'z=(%s) | y=(%s) | x=(%s)' % (z, y, x)
    print 'substitutions:', substitutions
    Formula_Expander = SE.SE (substitutions)
    formula = 'x + y + z'
    expanded_formula = Formula_Expander (formula)
    print 'expanded_formula:', expanded_formula
    a = 3; b = 4
    print 'eval (expanded_formula):', eval (expanded_formula)
    w = eval (Formula_Expander ('y * b'))
    print 'w', w

    >>> f (x, y, z)

    substitutions: z=(x+y) | y=(x*a) | x=(a+b)
    expanded_formula: (a+b) + ((a+b)*a) + ((a+b)+((a+b)*a))
    eval (expanded_formula): 56
    w 84

    Well--- it's kind of neat the way it expands the formula. But why not just write functions? As you say, the expansion has to be done
    in exactly the right order to complete.
    If you want to run this example you'll find SE here: http://cheeseshop.python.org/pypi/SE/2.2 beta

    Regards

    Frederic



    ----- Original Message -----
    From: <>
    Newsgroups: comp.lang.python
    To: <>
    Sent: Thursday, August 31, 2006 1:15 PM
    Subject: Using eval with substitutions


    > >>> a,b=3,4
    > >>> x="a+b"
    > >>> eval(x)

    > 7
    > >>> y="x+a"

    >
    > Now I want to evaluate y by substituting for the evaluated value of x.
    > eval(y) will try to add "a+b" to 3 and return an error. I could do
    > this,
    > >>> eval(y.replace("x",str(eval(x))))

    > 10
    >
    > but this becomes unwieldy if I have
    > >>> w="y*b"

    > and so on, because the replacements have to be done in exactly the
    > right order. Is there a better way?
    >
    > Thanks,
    > Abhishek
    >
    > --
    > http://mail.python.org/mailman/listinfo/python-list
     
    Anthra Norell, Aug 31, 2006
    #6
  7. Guest

    Thanks very much for all your suggestions - Peter, Duncan and Frederic.
    It was a great help.

    Incidentally the question arose while I was trying to solve the 2-nots
    problem,
    http://www.inwap.com/pdp10/hbaker/hakmem/boolean.html#item19

    Part of my program takes a set of boolean expressions in 3 variables
    and keeps concatenating them with 'and' and 'or' until all unique
    functions have been found. Sometimes it is good to use substitutions
    (though not required) to make the patterns apparent. Anyway, the
    listing is here,

    http://www.ocf.berkeley.edu/~abhishek/twonot3.py

    Abhishek
     
    , Sep 2, 2006
    #7
  8. Carl Banks Guest

    wrote:
    > >>> a,b=3,4
    > >>> x="a+b"
    > >>> eval(x)

    > 7
    > >>> y="x+a"

    >
    > Now I want to evaluate y by substituting for the evaluated value of x.
    > eval(y) will try to add "a+b" to 3 and return an error. I could do
    > this,
    > >>> eval(y.replace("x",str(eval(x))))

    > 10
    >
    > but this becomes unwieldy if I have
    > >>> w="y*b"

    > and so on, because the replacements have to be done in exactly the
    > right order. Is there a better way?


    Careful. Here be dragons.

    There are two legitimate uses for eval and exec: A. deliberately giving
    the user the power to run arbitrary Python code, and B. evaluating
    expressions constructed within the program using only trusted and
    carefully verified input. Make sure your use case is one of these.

    Anyways, although what you want to do is even more error prone and
    dangerous than simple uses of eval, it can be done. The thing to do is
    to get your list of active symbol definitions into some kind of dict;
    for example:

    x = "a+b"
    y = "x+b"
    exprs = { "x": x, "y": y }

    Then, if you want to evaluate y, you recursively expand any variables
    you find within it. For example:

    def expand_sym(sym):
    expr = exprs[sym]
    for subsym in exprs:
    if subsym in expr:
    subexpr = expand_sym(subsym)
    expr = expr.replace(subsym,"(%s)" % subexpr)
    return expr

    Then you can eval the expanded expression:

    eval(expand_sym("y"))


    However, this is just a big potentially dangerous hack. It's probably
    ok for a little calculator you intend only for personal use, but
    anything more I highly recommend your script parses the expression and
    does symbolic expansion itself, without relying on eval. That,
    however, is quite hard.


    Carl Banks
     
    Carl Banks, Sep 2, 2006
    #8
  9. ----- Original Message -----
    From: "Carl Banks" <>
    Newsgroups: comp.lang.python
    To: <>
    Sent: Saturday, September 02, 2006 6:33 AM
    Subject: Re: Using eval with substitutions


    > wrote:
    > > >>> a,b=3,4
    > > >>> x="a+b"

    ....
    > Careful. Here be dragons.

    ....
    > However, this is just a big potentially dangerous hack. It's probably

    ....

    > Carl Banks


    -----------------------

    Dragons indeed!
    To wit, I did get carried away a little with my suggestions and derived a class Formlua_Expander from SE. A few hours later it
    worked just beautifully:

    >>> FE = FORMEX.Formula_Expander ()
    >>> FE.define ('l=102.8')
    >>> FE.define ('d=8.5')
    >>> FE.define ('pi=3.14159')
    >>> FE.define ('r=d/2')
    >>> FE.define ('section=r*r*pi')
    >>> FE.define ('pipe_volume=l*section')
    >>> FE ('pipe_volume')

    5833.3828517499996
    >>> FE.expand ('pipe_volume')

    '((102.8)*(((8.5)/2)*((8.5)/2)*(3.14159)))'

    The expansion cascade:

    Data Chain
    ----------------------------------------------------------------------------------
    pipe_volume
    0 --------------------------------------------------------------------------------
    (l*section)
    1 --------------------------------------------------------------------------------
    (l*(r*r*pi))
    2 --------------------------------------------------------------------------------
    (l*((d/2)*(d/2)*pi))
    3 --------------------------------------------------------------------------------
    ((102.8)*(((8.5)/2)*((8.5)/2)*(3.14159)))
    ----------------------------------------------------------------------------------

    Wonderful! Some more:

    >>> FE.define ('m_per_foot=0.3048')
    >>> FE.define ('pipe_volume_metric=pipe_volume*m_per_foot')
    >>> FE ('pipe_volume_metric')


    Traceback (most recent call last):
    File "<pyshell#199>", line 1, in -toplevel-
    FE ('pipe_volume_metric')
    File "c:\i\sony\fre\src\python\FORMEX.py", line 175, in __call__
    try: return eval (expression)
    File "<string>", line 1
    (((102.8)*(((8.5)/2)*((8.5)/2)*(3.14159)))*m_pe((8.5)/2)_foot(102.8)e)
    ^
    Oops!

    >>> FE.show (1)

    ....
    (1st pass) ...
    1: |pipe_volume_metric|->|(pipe_volume*m_per_foot)|
    (2nd pass) ...
    1: |pipe_volume|->|(l*section)|
    (3rd pass) ...
    1: |section|->|(r*r*pi)|
    (4th pass) ...
    1: |r|->|(d/2)|
    (5th pass) ...
    1: |d|->|(8.5)|
    2: |l|->|(102.8)|
    3: |m_per_foot|->|(0.3048)|
    4: |pi|->|(3.14159)|

    Data Chain
    ----------------------------------------------------------------------------------
    pipe_volume_metric
    0 --------------------------------------------------------------------------------
    (pipe_volume*m_per_foot)
    1 --------------------------------------------------------------------------------
    ((l*section)*m_per_foot)
    2 --------------------------------------------------------------------------------
    ((l*(r*r*pi))*m_per_foot)
    3 --------------------------------------------------------------------------------
    ((l*((d/2)*(d/2)*pi))*m_pe(d/2)_foot)
    4 --------------------------------------------------------------------------------
    (((102.8)*(((8.5)/2)*((8.5)/2)*(3.14159)))*m_pe((8.5)/2)_foot(102.8)e)
    ----------------------------------------------------------------------------------

    An unwanted substitution occurs in pass 4 when the 'r' in 'm_per_foot' is replaced
    by the substitution meant for 'r' (radius).

    Conclusion: Forget it! - Forget this approach anyway. There are probably formula parsers out there that are more intelligent than
    this hack.

    Frederic

    (Learning by making mistakes is faster than by avoiding mistakes and if it's fun on top of it all the better.)
     
    Anthra Norell, Sep 2, 2006
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. RJGraham
    Replies:
    6
    Views:
    505
    RJGraham
    Jun 28, 2004
  2. Chris Goller
    Replies:
    4
    Views:
    385
    Jordan Stewart
    Mar 9, 2005
  3. Tony Eva
    Replies:
    1
    Views:
    497
    Jeff Epler
    Nov 9, 2003
  4. Vivien Mallet

    Backslash substitutions

    Vivien Mallet, Sep 29, 2004, in forum: Python
    Replies:
    1
    Views:
    318
    Peter Otten
    Sep 29, 2004
  5. Replies:
    7
    Views:
    134
    Charles DeRykus
    May 23, 2006
Loading...

Share This Page