Why does this function need to be stored in a variable? - python

I originally had a problem with this code as I was missing the 'n =' in the last line of code and, as a result, was stuck in an infinite loop.
At this point, while I understand what needed to be corrected I don't understand why. Why can't 'collatz(n)' be enough to call the function and use n as its variable? If anyone could explain this in simple terms (beginner here), I'd really appreciate it.
def collatz(number):
if number % 2 == 0:
print (number // 2)
return number // 2
elif number % 2 == 1:
print (3 * number + 1)
return 3 * number + 1
print ('Please enter a number.')
n = int(input())
while n != 1:
n = collatz(n)

In Python, functions accept one or more arguments and return a single value or object. Most of the time they don't modify their arguments (and indeed your collatz function doesn't attempt to modify its argument).
As an example, this function accepts a variable x, and returns x**2.
def f(x):
return x**2
This function doesn't modify x in place, and the return value won't automatically get assigned to x. Automatic assignment to x would often be unhelpful, and it would be unclear what to do if your function accepted multiple arguments -- which one should get the return result?
You can call this function in various ways, but if you want to do something with the result, you have to store it to a variable or use it immediately:
y = 2
z = f(y)
z = f(2)
y = 2
print(f(y))
Note that all of these make sense if you think of the function f as an object that converts its argument to something else and returns that, but none of them make sense if you expect f to modify its argument in place (then f(2) would somehow have to convert the number 2 to mean 4 during later references).
For what it's worth, even if you did replace one of the arguments with a new value inside the function, that would not change the value of the corresponding variable outside the function. This is because the variables within the function only point to the corresponding value or object. If you assign a new value to the variable, the local variable within the function will now point to the new value, but the original variable outside the function still points to the old value. On the other hand, you can sometimes modify the underlying value or object rather than creating a new object and pointing the local variable to it. For example, adding an item to a list or dictionary will modify the underlying object, and that change will be visible outside your function.
But your collatz function does neither of these - it just calculates a new value and returns it. If you want to do anything with that value, you have to store the result of the function call explicitly. It won't automatically be stored in the argument variable.

When you pass a variable as an argument to a function, a copy of the variable is sent to the function and not the variable itself.
So in your case n_copy (for example) is sent to your function and not n.
Now when you modify it within the function it remains in the scope of the function (accessible only by the function) and not the main program.
So when the function ends, nothing happens to n because a copy of n was modified.
Now we come to the return function. Because of the above problem, there is a return function. This will return a value from the function to the main program.
As you modified n within your function, you need to return the modified value to the main program.
Once you return it to the main program, it has to be stored in a variable, in your case it is n.
As you have started learning Python, you should read about namespace, scopes also.
Here is the first link from google search
https://matthew-brett.github.io/teaching/global_scope.html

Related

Understanding Python nested function sample

This link talks about Python nested functions.
They have this example:
def num1(x):
def num2(y):
return x * y
return num2
res = num1(10)
print(res(5))
When I run it, it multiplies 10 by 5 and prints out 50. How does it "run"
res = num1(10)
... if the num1() function is only given a single argument of 10? y is not defined when num1(10) is run. The print function only executes when it runs res(5), but how are you "stuffing" two values into x in the parent function?
I'm thinking there's a bigger picture thing I'm not understanding in relation to how the function and order is running.
Thanks for looking at this beginner question. I'm just trying to understand... baby steps.
When you run res = num1(10) it assigns a function to res, but doesn't run it.
You can kind of think of it like this (not valid syntax, only for illustration):
res = def num2(y):
return 10 * y
Then when you call res, it actually runs the function and does the multiplication.
You supply y when you do res(5); y isn't given a value when num1 is run.
num1 returns a function that will require y. y is supplied when that returned function is called.
This is a common technique to delay the need for supplying information to a function. If a function accepts two parameters but you only have one of the arguments handy, you can return a function that wraps a call to the function where the known parameter already passed. You're left with a function that accepts the remaining data when it becomes available.

Why do I not have to define the variable in a for loop using range(), but I do have to in a while loop in Python?

I have the following code using a for loop:
total = 0
for num in range(101):
total = total + num
print(total)
Now the same result using a while loop:
num = 0
total = 0
while num <= 99:
num = num + 1
total = total + num
print(total)
Why is it that I do not have to define num in the first case, but I do have to define it in the second? Are they both not variables?
Well, for is a special statement that automatically defines the variable for you. It would be redundant to require you to declare the variable in advance.
while is a general purpose loop construct. The condition for a while statement doesn't even have to include a variable; for example
while True:
or
while my_function() > 0:
I'd like to approach this question from a slightly different perspective.
If we look at the official Python grammar specification, we can see that (approximately speaking), a while statement takes a test, while a for statement takes an exprlist and testlist.
Conceptually, then, we can understand that a while statement needs one thing: an expression that it can repeatedly evaluate.
On the other hand, a for statement needs two: a collection of expressions to be evaluated, as well as a number of names to bind the results of those evaluations to.
With this in mind, it makes sense that a while statement would not automatically create a temporary variable, since it can accept literals too. Conversely, a for statement must bind to some names.
(Strictly speaking, it is valid, in terms of Python grammar, to put a literal where you would expect a name in a for statement, but contextually that wouldn't make sense, so the language prohibits it.)
In python there is no need, in most cases, to define/declare variables.
The rule is that if you write (assign) a variable then the variable is a local variable of the function; if you only read it instead then it's a global.
Variables assigned at top-level (outside any function) are global... so for example:
x = 12 # this is an assignment, and because we're outside functions x
# is deduced to be a global
def foo():
print(x) # we only "read" x, thus we're talking of the global
def bar():
x = 3 # this is an assignment inside a function, so x is local
print(x) # will print 3, not touching the global
def baz():
x += 3 # this will generate an error: we're writing so it's a
# local, but no value has been ever assigned to it so it
# has "no value" and we cannot "increment" it
def baz2():
global x # this is a declaration, even if we write in the code
# x refers to the global
x += 3 # Now fine... will increment the global
The for statement is simply a loop that writes to a variable: if no declaration is present then the variable will be assumed to be a local; if there is a global or nonlocal declaration then the variable used will have the corresponding scope (nonlocal is used to write to local variable of the enclosing function from code in a nested function: it's not used very frequently in Python).
If you are coming from other programming languages like C, C++ or Java then do not confuse with for in loop of python.
In python, for in loop pick one item from list of items and does something with help of the picked item.
For Loop iterates each element from the list until the given range. So no need of any variable to check condition.
While Loop iterates until the given condition is true. Here we need some variable or value to check the condition, So the variable num is used before the loop.
Python for loops assign the variable and let you use it. We can transform a for loop into a while loop to understand how Python actually does it (hint: it uses iterables!):
iterator = iter(iterable) # fresh new iterator object
done = False
while not done:
try:
item = next(iterator)
except StopIteration:
done = True
else:
# inside code of a for loop, we can use `item` here
pass

Print function calling functions together

I was testing with something and I wrote this code for example purposes.I felt the output I got was weird.I expected that function calls are executed one after the other but according to this code both the print statements are getting executed after each other and then the return values are printed together.What does this mean and what is the flow of the code in this case?
global num
num=5
def demo(num):
num+=1
print("hi")
return(num)
print(demo(num),demo(num))
output-
hi
hi
6 6
I expected that function calls are executed one after the other
That's exactly what happens.
But there's no way the print can happen before the demo calls, because it's trying to print out the values returned by those calls. (You can loosely think of this as a special case of anywhere you see parentheses: 2 * (3+4) can't multiply by 2 until it's added 3+4, and print(demo(num), demo(num)) can't print the results of demo(num) and demo(num) until it's called them. But don't take that too literally.)
So, those demo calls happen left to right, then the print call happens.
In more detail, let's step through how it evaluates this line:
print(demo(num),demo(num))
… Python has to do this:
Evaluate print by looking it up as a builtin name, which finds the builtin print function.
Evaluate the first argument.
Evaluate demo by looking it up as a global name, which finds the global demo function that you defined.
Evaluate num by looking it up as a global name, which finds the global 5 value.
Call the function on the argument.
The parameter num gets the value passed in, 5.
num += 1 updates the local variable (parameters are local variables) num to 6.
print("hi") prints out hi.
return(num) returns the value of the local variable, 6.
Evaluate the second argument.
… same as above, it prints out hi and returns 6.
Call the function returned by evaluating print on the two arguments returned by the two calls, so it prints out 6 6.
If you want the rigorous definition, he details are covered in Calls in the reference documentation. In particular (stripping out irrelevant bits)
call ::= primary "(" [argument_list] ")"
 …
The primary must evaluate to a callable object…. All argument expressions are evaluated before the call is attempted.
according to this code both the print statements are getting executed after each other and then the return values are printed together
Yes. The two function calls have to be executed in order, so that it can get the values to pass to the print function. Executing them prints out Hi twice. Then it has all the values, so it can print them, which prints out 6 6, since both values are 6.
Program evaluation has an order of operations just like arithmetic does. And similarly, it's not always intuitive, especially when we "consume" things left to right, up to down while reading.
So, what gives? Lets become the python interpreter and see why order of operations is important.
# A wild statement appears. Time to compute!
print(demo(num),demo(num))
# I cant't print yet! i'm missing info!
I need to evaluate this first demo(num), and by default im going to do it a closed room away from globals
# evaluating demo(num=5) - num here is a new local variable, it has no relation to the global one defined above
num+=1 # num = 6
print("hi") # [[[PRINT hi]]] ~> to console
return 6 # returns the value 6 filling in another piece of the puzzle
Where are we at now? Almost ready to call this print, just need to do this demo thing again
print(6, demo(num))
# What is num though?
# Well, the only num I know of out here in global space is 5
print(6, demo(5))
# evaluate: demo(5)
This seems familiar!
# evaluating: demo(num=5) - again, a NEW local variable is created just for this function call
num+=1 # num = 6
print("hi") # [[[PRINT hi]]] ~> to console
return 6
Finally, print has all its arguments it needs
print(6, 6) # [[[PRINT 6 6]]] ~> to console
print is not magic, it's just another function! Same as the demo you wrote.
And functions will not evaluate until all their parameters are supplied.
print(a, b) needs the values of a & b before it can do its thing.

Changing the variable given in a function statement

My code so far is:
def errorCheckInt(n):
try:
n = int(n)
checkValue1 = True
except:
checkValue1 = False
It is supposed to take a variable input (which is n) and change it to an integer if possible. It will try to get a different input if it is a string.
However, it hasn't worked, and I think it's because you can't change the variable in the code.
This may not be very clear, so I'm giving an example:
testingNum = "2"
# def errorCheckInt here
errorCheckInt(testingNum)
# Should change testingNum to integer value 2, not string "2"
This would be followed by a while statement, checking whether it was successful (whether checkValue1 is True or False) then possibly asking for a new input and error checking that (depending on the input the user gives).
If this still isn't very clear, just comment a question and I may be able to tell you what I had in mind.
Thanks in advance!
It isn't clear to me what you mean when you say "you can't change the variable in the code". Honestly, I'm going to take a leap here (since I have no idea where you are at in terms of Python understanding).
There are two concepts I feel like you may be having trouble with. (1) is the idea of object mutability, and (2) how variables are used and defined in and out of scope inside of Python.
When you re-assign a value like this:
n = int(n)
You haven't actually modified the value held by the variable outside (testingNum). This is because variables in Python are just references to objects. This means that your variable will always point to the same object unless it is re-assigned explicitly. Where this gets confusing is when you're dealing with the concept of mutability. For example, lists, dictionaries and classes are mutable objects. If you do something like:
t = []
n = t
and then do:
n.extend([1,2,3,4])
you'll see that both n and t now look like:
[1,2,3,4]
This isn't really mysterious behavior when you finally understand what's going on. The integer 4 and the [1,2,3,4] are different objects. The only difference is that [1,2,3,4] was also the same object as the initial [] when it was assigned to t. On the other hand, when you re-assign an integer or a string to a variable, you've simply changed what object the variable is pointing to. That's all.
How does this apply to your example?
In essence, all you were missing was a return statement:
def errorCheckInt(n):
try:
n = int(n)
checkValue1 = True
except:
checkValue1 = False
return n
testingNum = "2"
newNum = errorCheckInt(testingNum) # You can catch it here with newNum.
Understanding the use of the return statement is tantamount. When you assign a reference to a variable inside a function, that variable falls out of scope as soon as you exit the function; in other words, if you try to call it afterwards, you'll get an error. To circumvent this, you need to catch the result of your function in a variable that's in the outer scope. This allows you to keep working with the data you've just calculated inside the function.
Note
A better way of doing what you're asking is to use isinstance. For example,
print isinstance(4, int)
# True
print isinstance('4', int)
# False
This will automatically check if the value is an instance of the int type. It's both clearer and cleaner.
Further clarification
An interesting fact of mutability is that when you pass references to mutable objects inside of functions, modifying the object with, say, [].append(4) will mean that any variable pointing to that object (even from outside) is getting the same update if you will. So in certain cases using the return statement isn't necessary, but being explicit is sometimes worth the effort.
When errorCheckInt is run, variable n is bound to the same value as testingNum. However with n =, you change the binding, not the value so n then points to a different value than testingNum. What you need is to pass a mutable type and change the binding inside it:
def errorCheckInt(n):
try:
n[0] = int(n[0])
checkValue1 = True
except:
checkValue1 = False
testingNum = [ "2" ]
errorCheckInt(testingNum)
# testingNum is now 2 and type(testingNum) is int

about python scopes

I've read about python scopes and browsed questions here on stackoverflow but theres something i'd like to clarify.
I'm extracting a piece of code to a function, from what i used to it should take all the variables it's using as parameters. But, in python the variable address is determined at runtime so theres actually no need for the parameters. Since i'm new to python i wanted to know if there are other implications or conventions i should know about.
x = 5
x += 1
print x
is there any difference between the following refactoring of the above code :
def f(x):
x += 1
return x
x = 5
x = f(x)
print x
and:
def f():
x++
x = 5
f()
print x
If not then, is one of the ways more commonly used or preferred in python ?
It's preferred not to use global variables, if not absolutely necessary. That said, in the second example you'd need global x declared before you refer to x.
So, first way:
f takes an argument x
Increments x
returns x + 1
the global x is not affected
The second way:
def f():
global x
x += 1
x = 1
f()
f has no arguments
Increments the global x
P.S. Python has no ++ operator. x += 1 is used instead
I upvote the question for two reasons:
1)
importing the notation x++ from other languages (C++ and Java) is a venial sin; who has never been absent-minded ?
not testing codes deserves a downvote, that's right, because it denotes that no tests have been performed to try to obtain oneself a certain number of observations and that's baaad
however I find the desire to understand notions concerning scopes, namespaces, global/local more commendable than the faults to be reproached
2)
It is certainly an unsatisfactory situation to be downvoted because of an approximate question while receiving an upvoted answer that contains itself some inadequate terms according to me. Terminology is particularly important in subjects in which controversial debates and confusionning descriptions may happen.
For exemple, for
def f(x):
x++
return x
I wouldn't say that f receives an argument x, but that f receives x as an argument.
x is not incremented; rather: another object is created with a value resulting of the incrementation of the value of initial x
f doesn't returns x+1 , it returns the new object with the incremented value
EDIT 1
# f takes an argument x
In the call f(x) , I wouldn't say that f receives an argument x, because x isn't an argument in the absolute. x "becomes" an argument only relatively to a function, at the moment when it is passed to the function. So I rather say that in the call f(x) , the function f receives x AS an argument.
It may happen that one says "the argument Y" one time, as an understatement to express "Y , considered at a moment when it is passed as an argument to a function". This understatement is shorter; but what I think is that in a precise explanation such an easy way to express must be banned.
#Increments x
Refering to the code written in the question (case 1), this sentence is ambiguous because of the function written with a parameter x: does x refer to the x in the function or the x outside ? You'll say I nitpick, but for a newbie that doesn't know the data model and the working of functions well, the question is valid. What does f(x) mean in Python ? , is the x passed by value or passed by reference ? It could be that the operations inside f could change the x outside, why not, that's precisely what is in discussion, no ? That's why it is preferable to name the parameter of f with a different name than any of the names of the outside objects; I think the same as for the previous sentence: in a precise explanation, only cautious sentences and codes should be employed, to avoid confusionning ambiguities:
def f(N):
N += 1
return N
x = 5
x = f(x)
print x
And it should be explained that the instruction N += 1 triggers the creation of a new object with value incremented and that the local identifier N is rebound to this new object. And at this point, the global identifier x is still bound to the same object as before the call.
# returns x + 1
f doesn't returns x+1 , it returns the new object newly assigned to N with the incremented value
# the global x is not affected
What does it mean ? Before , global x has a value 5. After, global x has a value 6. During the execution of the operations inside f, the global x isn't affected, that's right: it is only when the new object (in fact its address..) is returned to the outside of f, and that the assignement x = f(x) is executed that global x is affected. But this sentence "the global x is not affected" is placed after "returns x + 1
". No really, I understand nothing in such explanations.
End of EDIT 1
.
.
Concerning the second case
def f():
global x
x += 1
I prefer to say that f has no argument than it has no parameter
It doesn't increments the global x, it provokes a new assignement of the global identifier x to a new object with incremented value.
EDIT 2
In this case, it isn't possible to use another name than x inside the function, otherwise the exemple would mean another thing.
def f():
global x
x += 1
x = 5
f()
print x
# f has no arguments
Why the hell did I write "I prefer to say that f has no argument than it has no parameter" to comment that ? I still wonder.
At first, I had written "I prefer to say that f has no parameter than it has no argument" , thinking to the definition of f . I don't know why, I "corrected" by reversing the sentence and the result expresses nothing of what I think. I am completely puzzled.
Precisely, I think that the correct manners to express are :
in the definition of the function, f -> HAS no PARAMETER,
in the call f() , f -> RECEIVES no ARGUMENTS.
# Increments the global x
It doesn't increments the global x, it provokes a new assignement of the global identifier x to a new object with incremented value.
End of EDIT 2
.
It may seem minor nuances , and I am sure that it is a way to express things in a condensed manner.
But for me it is very important because, when a newbie, I had a lot of difficulty because of this kind of imprecision to understand all these questions linked to the peculiar data model of Python that are badly explained in the official docs of Python and that are debated in awkard discussions, such as the one concerning "pass by value or pass by reference" for exemple (that has no sense in Python), in which the terms are floating from right to left and left to right like on a sea of vagueness.

Categories