Reputation: 1672
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.
Upvotes: 10
Views: 5984
Reputation: 12980
The variant I would recommend:
mappings = {'A': 1, 'B': 2} # Or whatever ...
def evaluate(match):
return str(eval(match.group(0)[2:-1], mappings))
newstring = sub(r'\#\{([^#]+)\}', evaluate, string)
i.e. simply put the def evaluate(..)
just before the sub()
, as a local function.
Upvotes: 0
Reputation: 523544
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))
Upvotes: 2
Reputation: 67197
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)
Upvotes: 16
Reputation: 523544
You could use a function object.
class A(object):
def __init__(self, mappings):
self.mappings = mappings
def __call__(self, match):
return str(eval(match.group(0)[2:-1], self.mappings))
evaluate = A({'A': 1, 'B': 2})
Upvotes: 1