Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a variable to a re.sub callback?

I am using a re.sub callback to replace substrings with random values, but I would like the random values to be the same across different strings. Since the re.sub callback does not allow arguments, I am not sure how to do this.

Here is a simplified version of what I'm doing:

def evaluate(match):
    mappings = {'A': 1, 'B': 2}
    return str(eval(match.group(0)[2:-1], mappings))

# variables = {'A':[1,2,3,4,5], 'B':[1,2,3,4,5]}    
# mappings2 = {k:v[random.randint(0,len(v)-1)] for k, v in variables.items()}
string_one: "#{A} + #{B}"
string_two: "#{A+B}"
newstring_one = sub(r'\#\{([^#]+)\}', evaluate, string_one)
newstring_two = sub(r'\#\{([^#]+)\}', evaluate, string_two)

Now, as it stands, the strings will be properly evaluated: newstring_one is "1 + 2" and newstring_two is "3". But I want to be able to pick the values randomly, and still have them replaced in both strings. This would involve deleting the 'mappings' line in 'evaluate', and using something like the two commented lines. How, though, can I get my randomly chosen mappings2 to be used when eval-ing both strings, if I cannot pass it as an argument in the re.sub callback function?

Many thanks.

like image 240
Chris Avatar asked Jul 10 '10 07:07

Chris


2 Answers

The easiest way I guess is to make use of functools.partial, which allows you create a "partially evaluated" function:

from functools import partial

def evaluate(match, mappings):
    return str(eval(match.group(0)[2:-1], mappings))

mappings = {'A': 1, 'B': 2}  # Or whatever ...

newstring = sub(r'\#\{([^#]+)\}', partial(evaluate, mappings=mappings), string)
like image 51
Ferdinand Beyer Avatar answered Sep 18 '22 06:09

Ferdinand Beyer


You could create a closure.

def evaluator(mappings):
  def f(match):
    return str(eval(match.group(0)[2:-1], mappings))
  return f

evaluate = evaluator({'A': 1, 'B': 2})

Since f is just a single statement, you could simply use lambda:

def evaluator(mappings):
  return lambda match: str(eval(match.group(0)[2:-1], mappings))
like image 29
kennytm Avatar answered Sep 20 '22 06:09

kennytm