Luhuimiaomiao
Luhuimiaomiao

Reputation: 33

Python decorator: TypeError: function takes 1 positional argument but 2 were given

I am trying to test the utility of decorators in Python. When I write the following codes, there is an error:

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

I first define a function log_calls(fn) as

def log_calls(fn):
    ''' Wraps fn in a function named "inner" that writes
    the arguments and return value to logfile.log '''
    def inner(*args, **kwargs):
        # Call the function with the received arguments and
        # keyword arguments, storing the return value
        out = fn(args, kwargs)

        # Write a line with the function name, its
        # arguments, and its return value to the log file
        with open('logfile.log', 'a') as logfile:
            logfile.write(
               '%s called with args %s and kwargs %s, returning %s\n' %
                (fn.__name__,  args, kwargs, out))

        # Return the return value
        return out
    return inner

After that, I use log_calls to decorate another function as:

@log_calls
def fizz_buzz_or_number(i):
    ''' Return "fizz" if i is divisible by 3, "buzz" if by
        5, and "fizzbuzz" if both; otherwise, return i. '''
    if i % 15 == 0:
        return 'fizzbuzz'
    elif i % 3 == 0:
        return 'fizz'
    elif i % 5 == 0:
        return 'buzz'
    else:
        return i

When I run the following code

for i in range(1, 31):
    print(fizz_buzz_or_number(i))

The error TypeError: fizz_buzz_or_number() takes 1 positional argument but 2 were given comes.

I don't know what is wrong with this decorator, and how to fix this.

Upvotes: 3

Views: 7379

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121744

You are passing in 2 arguments to your decorated function here:

out = fn(args, kwargs)

If you wanted to apply the args tuple and kwargs dictionary as variable arguments, echo the function signature syntax, so use * and ** again:

out = fn(*args, **kwargs)

See the Call expressions reference documentation:

If the syntax *expression appears in the function call, expression must evaluate to an iterable. Elements from these iterables are treated as if they were additional positional arguments.

[...]

If the syntax **expression appears in the function call, expression must evaluate to a mapping, the contents of which are treated as additional keyword arguments.

Upvotes: 4

Related Questions