2

I have a Python expression that looks like the following:

var1 = 'GOOGLE'
var2 = '5'
expr = 'df[df[var1]>=var2]'

In my workspace var1 and var2 are well defined so I can evaluate expr as follows:

eval(expr)

However, I want to pass this expr (as string) to another function with values of var1 and var2 substituted in it. I do not want to pass the variables var1 and var2, as I can have any number of variables, not just two. How do I accomplish this?

11
  • en.wikipedia.org/wiki/XY_problem Commented Nov 22, 2022 at 6:58
  • I'd use a dictionary or a custom object to collect all the variables you want to pass to your function in one input argument. Would this work for your problem? Commented Nov 22, 2022 at 7:01
  • What is your question? As of now your question is not clear. Do you want to know how to create a function that takes varying (any amount) arguments or what? Commented Nov 22, 2022 at 7:03
  • 1
    @Zing Simply use f-string Commented Nov 22, 2022 at 7:07
  • 1
    @Zing Did I answer your question? Commented Nov 22, 2022 at 7:13

3 Answers 3

2

You can parse the expression with ast.parse and use a subclass of ast.NodeTransformer to convert Name nodes to the corresponding values as Constant nodes, and then convert the AST back to code with ast.unparse:

import ast

var1 = 'GOOGLE'
var2 = '5'
expr = 'df[df[var1]>=var2]'

class NamesToConstants(ast.NodeTransformer):
    def visit_Name(self, node):
        if node.id in globals(): # feel free to use your own dict instead of globals()
            value = globals()[node.id]
            try: # convert value to integer if viable
                value = int(value)
            except:
                pass
            return ast.Constant(value=value)
        return node

tree = ast.parse(expr)
NamesToConstants().visit(tree)
print(ast.unparse(tree))

This outputs:

df[df['GOOGLE'] >= 5]

ast.unparse requires Python 3.10 or later. If you're using an earlier version, you can use astunparse.unparse from the astunparse package instead.

Demo: https://trinket.io/python3/18cc1182d0

Sign up to request clarification or add additional context in comments.

Comments

1

You can simply use Python f-string as demonstrated below

expr = f'df[df[{var1}] >= {var2}]'

2 Comments

This is a possible solution, but will require me to add curly braces around variables that I wanted to avoid.
The whole point was not to alter the original expression, but to produce a literal expression without evaluating it at the end. If you are parsing arbitrary expressions or statements (e.g. from a file), you don't know in advance which variables exist and where.
0

The "proper" way would be to do use ast.parse, as @blhsing recommended. Still, in many cases this may be an overkill. In your simple case and similar cases, this is what I would do:

import re

expression = "3*x + function_x(y)"
values = {'x': 2, 'y': 5}  # feel free to use a subset of `globals()` or `locals()` here, to reflect your workspace

# Substitute variable symbols with their values
for var, val in values.items():
    pattern = r'\b' + re.escape(var) + r'\b'
    expression = re.sub(pattern, str(val), expression)
    
assert(expression == "3*2 + function_x(5)")

The r'\b' pattern ensures that you only match whole-word occurences, in case a variable exists as a substring of another variable or function/built-in name etc (like x here exists in function_x).

The re.escape() function is necessary when you have variable symbols that contain special characters that have a special meaning in regular expressions. For example, the variable symbol itself could contain a special character, such as a .: y.z.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.