2

I have the following minimal example that reproduces the issue:

def f1(a, b):
    print(1)
    return a + b


def f2(a, b):
    print(2)
    return a * b


funcs = f1, f2


_funcs = []
for func in funcs:

    def _func(x):
        return func(*x)

    _func.__name__ = func.__name__
    _funcs.append(_func)
funcs = _funcs


for func in funcs:
    print(func([2, 3]))

This throws a TypeErro:

TypeError: _func() takes 1 positional argument but 2 were given

with the following traceback:

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-93-e24e67b5e525> in <module>()
     22 
     23 for func in funcs:
---> 24     print(func([2, 3]))

<ipython-input-93-e24e67b5e525> in _func(x)
     14 
     15     def _func(x):
---> 16         return func(*x)
     17 
     18     _func.__name__ = func.__name__

TypeError: _func() takes 1 positional argument but 2 were given

I am not sure why as it seems to me that _func() is always called with a single argument.

I suspect what I want to do would be done with functools, but still I fail to see why the above code works this way. Any hint?

1
  • Very strange, and when you type print(funcs[0]([2, 4])) and print(funcs[1]([2, 4])) (so outside the for loop) in both case, I get 2 and 8. Commented Sep 30, 2020 at 20:59

1 Answer 1

3

You are closing over the free-variable func when you define:

def _func(x):
    return func(*x)

However, in your loop, you re-using this name,

for func in funcs:
    print(func([2, 3]))

So func now refers to the single-argument functions you defined, even inside _func! So of course, when that function calls func(*x) it's actually recursively calling itself. Use a different name:

for f in funcs:
    print(f([2, 3]))

Or better yet, don't rely on a free-variable you don't intend to be free:

def func_maker(func):
    def _func(x):
        return func(*x)
    return _func

And use that:

...

_funcs = []
for func in funcs:

    _func = func_maker(func)

    _func.__name__ = func.__name__
    _funcs.append(_func)
funcs = _funcs

...
Sign up to request clarification or add additional context in comments.

2 Comments

@0x5453 yes, it's the same fundamental issue, although, I think in this case it isn't as obvious. I had to look it over a couple of times.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.