Having a lambda function that uses a regex:
import re def make_func(pattern = "black.*"): func = lambda x: re.fullmatch(pattern, x) return func
I would like to retrieve the source function including its pattern
for error reporting. The expected output would be something like:
func = lambda x: re.fullmatch("black.*", x)
Knowing of the inspect module and the function getsource, I managed to solve part of my problem, but the pattern
variable is not evaluated:
from inspect import getsource print(getsource(func))
which yields func = lambda x: re.fullmatch(pattern, x)
. How can I get the concrete pattern
that was captured by the function as well?
Advertisement
Answer
I’m not sure what you’re looking for exactly, but I think there are several ways to achieve what you want.
For example, you could store the pattern inside the lambda function object that you return, or update its docstring:
def make_func(pattern='black.*'): func = lambda x: re.fullmatch(pattern, x) # Option 1: func.pattern = pattern # Option 2: func.__doc__ = f'lambda x: re.fullmatch({pattern!r}, x)' return func
Using either of this options, the used pattern is still available in the function object so it can be used for error reporting. Note that this is bit hackish, and I’m not sure if this might cause problems in the long run.
By the way: assigning a lambda expression is an anti-pattern (see pycodestyle/flake8 rule E731 and/or PEP-8). You could just as well define and return a function:
import re def make_func(pattern='black.*'): def func(x): return re.fullmatch(pattern, x) func.__doc__ = f're.fullmatch({pattern!r}, x)' return func func = make_func() print(func.__doc__) # prints: re.fullmatch('black.*', x)
I think a nicer and more Pythonic solution is to create a callable object, which could be used as a function but also incorporates the original pattern:
import re class FullPatternMatcher: def __init__(self, pattern): self.pattern = pattern def __call__(self, x): return re.fullmatch(self.pattern, x) def __str__(self): return f're.fullmatch({self.pattern!r}, x)' def make_func(pattern='black.*'): return FullPatternMatcher(pattern) func = make_func() # just to demonstrate that the function is working: assert func('white sheep') is None assert func('black sheep') is not None print(str(func)) # prints: re.fullmatch('black.*', x)
Finally, you could also use functools.partial
, which has roughly the same effect as above solution, but using the standard library instead of a custom class. In this case the pattern is stored as args[0]
inside the returned object:
import functools import re def pattern_match(pattern, x): return re.fullmatch(pattern, x) def make_func(pattern='black.*'): return functools.partial(pattern_match, pattern) func = make_func() print(repr(func.args[0])) # prints: 'black.*'