There is no closure, no, because the inner function has it's own local exp variable; you gave it a parameter by that name. The parameter masks the name in the outer scope, so no closure is created for it. The function that is returned requires two arguments, and the argument to raise_to() is simply ignored:
>>> from inspect import signature >>> def raise_to(exp): ... def raise_to_exp(x, exp): ... return pow(x, exp) ... return raise_to_exp ... >>> signature(raise_to(2)) <Signature (x, exp)> >>> square = raise_to(2) >>> square(5) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: raise_to_exp() missing 1 required positional argument: 'exp' >>> square(5, 3) 125 >>> raise_to('This is ignored, really')(5, 3) 125
Remove the exp parameter from the inner function if you wanted that to be taken from the outer function:
def raise_to(exp): def raise_to_exp(x): return pow(x, exp) return raise_to_exp
Now exp is a closure:
>>> def raise_to(exp): ... def raise_to_exp(x): ... return pow(x, exp) ... return raise_to_exp ... >>> raise_to(2).__closure__ (<cell at 0x11041a978: int object at 0x10d908ae0>,) >>> raise_to.__code__.co_cellvars ('exp',)
The co_cellvars attribute on a code object gives you the names of any closed-over variable in the outer scope.
The function that is returned takes one argument, and the argument to raise_to() is now actually used:
>>> raise_to(2)(5) 25 >>> raise_to('Incorrect type for pow()')(5) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in raise_to_exp TypeError: unsupported operand type(s) for ** or pow(): 'int' and 'str'