halfdanr
halfdanr

Reputation: 393

Python ValueError when using local function in map

I'm trying to make a list containing data which has been processed by different functions. The below code does not work, because the two functions a and b are not within the scope of the map function

def outer_func():
    def a():
        return "Something"
    def b():
        return "Something else"
    map(lambda x: eval(x)(), ['a', 'b'])

I tried wrapping the functions in a class, and the below code works just fine.

class fcontainer():
    def a(self):
        return "Something"

    def b(self):
        return "Something else"

def test():
    f = fcontainer()
    return map(lambda x: getattr(f, x)(), ['a', 'b'])

Now, my questions are:

  1. Why do a and b not exist within the map function?
  2. Is there a 'right' way to do what I'm trying to do?
  3. Should I finally sit down and wrap my head around decorators? :)

Thank you!

UPDATE: The first example can be made to work by first fetching the functions while within the scope of outer_func, and then using map:

functions = [eval(i) for i in ['a', 'b']]
return map(lambda x: x(), functions)

This works, but it's two lines instead of one >:|

Upvotes: 0

Views: 433

Answers (3)

sam
sam

Reputation: 1896

In python, we have a special feature where you can pass a function just like a variable.

Your first approach is almost correct,.. slightly missed :)

>>> def outer_func():
...     def a():
...         return "Something"
...     def b():
...         return "Something else"
...     print map(lambda x: x(), [a, b])
...
>>> outer_func()
['Something', 'Something else']

eval is really good but like you faced, it has a risk of

If use this for your second approach, it will work.

>>> def test():
...     f = fcontainer()
...     return map(lambda x: x(), [f.a, f.b])
...
>>> test()
['Something', 'Something else']

Now, to your questions :

Q1. Why do a and b not exist within the map function?

MAP function is special. It does nt bother what is outside its scope. This makes it much more faster. check below code to understand.

>>> dir() # used to check local variables/function defined already.
['__builtins__', '__doc__', '__name__', '__package__', 'outer_func']
>>> map( lambda x: dir() , range(5))
[['x'], ['x'], ['x'], ['x'], ['x']]
>>> # as you can see within MAP, outer_func is missing.

Q2. Is there a 'right' way to do what I'm trying to do?

There is a easier way to do it :)

Q3. Should I finally sit down and wrap my head around decorators?

I think you already got answer by now. So for now no decorations

Hope this helps you :)

Upvotes: 0

glglgl
glglgl

Reputation: 91119

Is it necessary to have a list of strings (['a', 'b'])? Maybe you'd better put the function objects directly into the list: [a, b].

return map(lambda x: x(), [a, b])

seems quite easy to me.

If you want to do it as decorators, it won't be much easier:

def outer_func():
    funcs = []
    def funcapp(func): # so that a and b are still in place...
        funcs.append(func)
        return func
    @funcapp
    def a():
        return "Something"
    @funcapp
    def b():
        return "Something else"
    return funcs

Upvotes: 4

Eric
Eric

Reputation: 97641

You need to make sure eval executes in the right context, by giving it the globals and locals inside outer_func().

def outer_func():
    def a():
        return "Something"
    def b():
        return "Something else"
    map(lambda x, g=globals(), l=locals(): eval(x, g, l)(), ['a', 'b'])

Having said that, eval is probably not the solution to your problem

Upvotes: 1

Related Questions