Is there a way to transform a function to accept a callable that is evaluated just before the function?

StackOverflow https://stackoverflow.com/questions/23522234

  •  17-07-2023
  •  | 
  •  

Question

Suppose I have a list of strings:

names = ['Alice', 'Bob', 'Charlie']

And I want to compare all of them to a specific lowercase string using some function foo:

map(lambda name: foo('carol', str.lower(name)), names)

Is there a way to alter foo to accept a callable bar that evaluates just before foo is evaluated? So I can write something like this:

map(partial(foo, 'carol', str.lower), names)  # foo sees the name in lowercase
Was it helpful?

Solution

That's what they made decorators for! Take a look at a decorator that lower cases all arguments pass to some function:

 def lower_args(func):
   @functools.wraps(func)
   def _wrapper(*args, **kwargs):
     l_args = map(str.lower, args)
     l_kwargs = {(name, str.lower(value)) for (name, value) in kwargs}

     return func(*l_args, **l_kwargs)
   return _wrapper

Used like this:

@lower_args
def foo(a, b, c):
  #and your code goes here

To do what you want, change the syntax slightly

def to_lower(mod_func):
  def deco(func):
    @functools.wraps(func)
    def _wrapper(*args, **kwargs):
      return func(*map(mod_func, args), {(k, mod_func(v)) for (k,v) in kwargs})
    return _wrapper
  return deco

Used like:

@to_lower(my_function)
def foo(a, b, c):
  #your code goes here

OTHER TIPS

The pythonic way to do that is just to use a generator expression. Where you use names, instead put:

(name.lower() for name in names)

It will be evaluated as the container is iterated ("lazily"), as you wanted.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top