The function has been called 10 times but the decorator has only been called once. Why? - python-3.x

def call_counter(func):
print('Called\n')
def helper(x):
helper.calls+=1
return func(x)
helper.calls=0
return helper
#call_counter
def succ(x):
return x+1
print(str(succ.calls)+'\n')
for i in range(10):
print(succ.calls)
succ(i)
print('\n')
print(succ.calls)

You commented:
I don't understand why the decorator is called on the function only once. It should be called 10 times, right? One time for every iteration in the loop and therefore 'Called' should be printed 10 times. What am I missing?
I think you are confusing runs of the decorator for runs of the wrapper function (named helper in your code). The decorator is only called once, when it gets passed the original function and returns the wrapper function, which is what gets stored as succ in your module. The wrapper is what gets called ten times by your loop.
When I run your code, this is the output I get:
Called
0
0
1
2
3
4
5
6
7
8
9
10
Only the first two lines ("Called" and the empty line after it) come from the decorator call. You can see this if you run only the function definition, and not the loop. You'll see "Called" printed out immediately, even if you never call the function at all.
The zero at the top and the ten at the bottom are from the print statements you have at the top level. The numbers printed close together are the output of the loop, which prints the count of previous calls just before it makes each new call to the wrapped function.

Related

Why does this recursive addition return none

This is a weird one print return 9 and then it prints 1 , also i checked debugger in pycharm and the (stuff) keeps counting down for some reason
def repeater(stuff):
if stuff != 9:
stuff += 1
print(stuff)
repeater(stuff)
return stuff
print(repeater(0))
When you call repeater(stuff), you're not passing the variable stuff, you're passing a copy of the variable stuff. When you say stuff += 1, you're not modifying the stuff you called the function with, you're modifying a copy of it. That change isn't reflected in the original when you exit the function.
Then, when the function exits, you don't do anything with the returned value of stuff - which is, again, copied out of the function in reverse. Python does let you call the function without using its return value, but it looks like your intent here is to apply the returned value of repeater(stuff) to stuff.
To accomplish that, simply change the line
repeater(stuff)
to
stuff = repeater(stuff)
The reason for that additional 1 that is coming in the end is that repeater(stuff) is returning the value of stuff which is being received by your print statement i.e. print(repeater(0)). When all the recursive calls return back, none of the values are stored/used but the very first call that was made by print(repeater(0)) obtains a value of 1 because repeater(stuff) returns the value of stuff which would be 1 after stuff += 1 during the first call.
You can read more about how recursion works for more clarity.

Flow of a factorial function using recursion

I'm learning the fundamentals of Python right now by reading Thinking Python.
As I was reading a chapter about "fruitful" function, that is, a function that "returns" a value, I came across this problem. I can more or less figure out how the code of the factorial function works, but not exactly. I have trouble in understanding precisely the flow of a function involving self-calling or recursion. Btw, the printer statments in the code shown as below are scaffolding, a device used to track the flow of the function.
Without further ado, here is the code.
def factorial(n):
space=' '*(2*n)
print(space, 'factorial', n)
if n==0:
print(space, 'returning 1')
return 1
else:
recurse = factorial(n-1)
result=n*recurse
print(space, 'Returning', result)
return result
factorial(3)
The result or the output of this code is shockingly a v-shape. I, for the life of me, cannot figure out why the computer (or the compiler?) runs the program the way it does here. It seems like it firstly prints the first line and the last line, and then goes on to print the second line and the second to the last line...This is strange. You would expect that it always goes top down.
To illustrate the oddity of this issue, I hereby attach another piece of code that prints numbers as you count down. The code is similar and simpler, and you would expect it to behave similarly to the factorial function before. But it doesn't. There is no oddity in the order in which the lines of outcome are displayed or printed off.
def f(n):
print(n)
if n==0:
return 0
else:
print(3*n)
f(n-1)
return
I would really appreciate it if you could help me with this!
What might confuse you is that the recursive call does not happen at the end of the function as it is the case with your example f(). Which means, you have code that is executed before stepping into the recursion and code that is executed after stepping out of the recursion. Let's have a look at what happens in factorial:
The recursive step
Pick any recursion step i you like, which is not a base case. And then look at what the function does, without following through with the recursion.
For your factorial function, let's take the call factorial(i), where iis different from 0. That call does three things:
print 2*i spaces and "factorial i"
call factorial(i-1)
print 2*i spaces and "returning i"
So, the output is going to be:
(2*i spaces) factorial i
# everything factorial(i-1) outputs
(2*i spaces) returning i
You'll see that the output of factorial(i-1) gets sandwiched between the outputs of factorial(i). To perform the recursion, all you need to do is blow the pattern up. Let's take another step i-1 into account:
(2*i spaces) factorial i
(2*(i-1) spaces) factorial i-1
# everything factorial(i-1) outputs
(2*(i-1) spaces) returning i-1
(2*i spaces) returning i
So, you see that the indentation decreases with the calls since i decreases, which accounts for the v-shape.
The base case
It just prints
factorial 0
returning 1
Putting it all together
Now, you can find a verbal expression for what factorial(i) does:
factorial(i) produces the v-shape output
factorial i
factorial i-1
...
factorial 0
returning 1
...
returning i-1
returning i
With it being true for the case i == 0, conclude from there that is is true for i+1 if you assume that is is correct for i using the list in the section recursive step. Done that, you can be sure that is how factorial(n) works for any n >= 0.
Here's your code a bit restructured to make it easier to see:
def factorial(n):
space = ' '*(2*n)
print(space, 'factorial', n)
result = 1 if n == 0 else n*factorial(n-1)
print(space, 'Returning', result)
return result
And that's the output for factorial(3) with the "V shape" you mentioned:
factorial 3
factorial 2
factorial 1
factorial 0
Returning 1
Returning 1
Returning 2
Returning 6
It looks correct and does the job of calculating the factorial. To understand the order of prints seen here, think of the following:
The prints start with the call for 3, since that's your entry point
The indentation is then 3x2 = 6 spaces
Since for the calculation of factorial(3) the reuslt of factioral(2) is needed, this call comes next, with an indentation of 2x2 = 4 spaces. And so on for all further factorials. That's where the V shape of the first half of prints comes from.
All of these function calls are now "waiting" for a result to return, at the position of the recursive call in the code. Since only factorial(0) can deliver that return value without another recursion, it will print the first return value. With an indentation of 0.
With the above return value, the function call to factorial(1) can now calculate its result by multiplying the returned 1 with its own n, which gives 1. That's the next print, with an indentation of 2.
And so on for all the higher recursion steps, from the inside to the outside. And that's how the second half of the V shape is formed.
The vital thing to understand is that the result values of the functions (and thereby the print) can only occur at the END of the complete recursion tree that comes below it. That's why the call print factorial 3 is in the first line, while its corresponding Returning 6 print is at the last line - after the whole inner tree is finished.
Hard to say if I picked up the crunchpoint you had trouble understanding with, but I hope this somehow helps.
EDIT: infinite recursion
As an answer to your question, here's an example that shows how each recursive function call will have its own variable scope and therefore not know about the variables set in an another instance of the same function, ending up in a RecursionError when the maxiumum recursion depth of the call stack is reached.
def foo():
try:
print("a = ", a)
return # break out of loop
except UnboundLocalError:
print("no variable a. defining one now.")
a = 3
foo() # try again
foo()

Receiving function as another function´s parameter

Let´s say I have 2 functions like these ones:
def list(n):
l=[x for x in range(n)]
return l
def square(l):
l=list(map(lambda x:x**2,l))
print(l)
The first one makes a list from all numbers in a given range which is "n" and the second one receives a list as a parameter and returns the squared values of this list.
However when I write:
square(list(20))
it raises the error "map object cannot be interpreted as an integer" and whenever I erase one of the functions above and run the other one it runs perfectly and I have no idea what mistake I made.
You redefined the standard function list()! Rename it to my_list() and clean the code accordingly.
As a side note, your function list() is doing exactly what list(range(n)) would do. Why do you need it at all? In fact, for most purposes (including your example), range(n) alone is sufficient.
Finally, you do not pass a function as a parameter. You pass the value generated by another function. It is not the same.

Why UnboundLocalError doesn't occur with lists?

I have a question regarding a tutorial problem.
Write a function make_monitored that takes as input a function, f, that itself takes one input. The result returned by make_monitored is a third function, say mf, that keeps track of the number of times it has been called by maintaining an internal counter. If the input to mf is the special string "how-many-calls?", then mf returns the value of the counter. If the input is the special string "reset-count", then mf resets the counter to zero. For any other input, mf returns the result of calling f on that input and increments the counter.
I have the following solution which works, surprisingly.
def make_monitored(f):
calls=[0]
def helper(n):
if n=="how-many-calls?":
return calls[0]
elif n=="reset-count":
calls[0]=0
else:
calls[0]+=1
return f(n)
return helper
I recalled reading about UnboundLocalError here: UnboundLocalError in Python
My question would be why won't calls[0]+=1 trigger that error? I made an assignation to a variable outside the local scope of the third function helper , and it seems a similar solution that uses instead calls.append(1) (the rest of the code correspondingly becomes len(calls) and calls.clear()) also bypasses that error.

A Function That Counts The Number of Loops

Write a function multisplit that consumes two positive integers total and split and produces the number of times total is repeatedly divided into split even pieces before each piece is of size at most 1. For example, the value returned by multisplit(8, 2) will be 3, since 8 can be split into 2 pieces of size 4, which are then each split into 2 pieces of size 2, which are then each split into 2 pieces of size 1 (at which point no further splitting takes place since the pieces are of size at most 1).
total= int(input("Total:"))
split= int(input("Split:"))
def multisplit(total,split):
x=o
while value>=1:
value= total//split
x= x+1
return x
print(x)
It's telling me that the name 'x' is not defined
There are several issues with the code you posted:
In python, the contents of a function must be indented.
def myfunction():
# code inside the function goes here
# code after you've unindented is not in the function
You didn't define your value variable before using it.
Assuming that your final line gets appropriately unindented, so that it won't be completely ignored because of being inside the function, but after the return statement:
You're trying to print the value of a variable that was defined in a different scope. Specifically, you defined x inside the function, and now you're trying to look at it outside the function.
You never called your function...
If I understand what you're trying to do, you want to call the function inside print. i.e.: print(multisplit(total, split))

Resources