Local variables in Python - python

I wrote this small program to print multiplication tables. I was understanding the local variables then I added two variables with same name in each function. but it is running and I am not getting any 2 variables with same name error. Please help me understand the idea here -
def printMultiples(n):
i = 1
i = 5
while i <= 10:
print n*i, '\t',
i = i + 1
print
def printMultTable():
i = 1
i = 10
while i <= 10:
printMultiples(i)
i = i + 1

The variable i gets defined as soon as the function is run, it does not wait until the lines
i = 1
i = 10
are run. When the code is compiled, Python looks at every variable that is assigned in the function, and creates definitions for any that aren't declared to be global or nonlocal.
Each i = line will just change the value, it doesn't cause it to be defined again.

Here's what's happening in your script:
i = 1
creates the variable i and assigns the value 1 to it
i = 5
replaces the value held in i with 5
while i <= 10:
starts your loop
print n*i, '\t',
The first iteration of the loop will print n * 5 and a tab character
i = i + 1
increments i by one

There aren't two variables with the same name, there's one variable that is being assigned to twice.
i = 1 # i is declared and assigned to 1
i = 5 # the same i is reassigned to 5

In python, symbols are just names for variables. They are do not allocate memory as they do for say, C/C++. So what you are doing is simply reassigning the values to 'i' in each case.

You're not actually creating two variables with the same name; your second i = is reassigning i to a new value, same as i = i + 1 is setting i to the value of i + 1.
There are more Pythonic ways to deal with looping a fixed number of times, by the way:
for i in range(1, 11):
printMultiples(i)
This will execute printMultiples for values starting in 1 and less than 11.

Related

Recursive behavior

Why does the following executes such that the print statement is called as often as it recursed but the count variable, count, when x == 1 is never reached.
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1 # when x == 1
count_bits(int(n/2), count)
print("counter")
return count
why is it necessary to recurse with the return statement? Because if the recursive call is above the return statement the code
returns the wrong output but with the recursive call called with
return keyword, everything works well. Typically, the print statement
prints 'counter' as often as it recursed showing that the recursive call
works.
On the other hand, if "return" follows after the recursive call, it returns the count from the base condition, correctly.
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1
return count_bits(int(n/2), count)
You have to return recursion result, as reccurent function meaning is to count current step you have to get result of previous step
F_k = F_k-1 * a + b # simple example
Means that you have to get result of F_k-1 from F_k and count current result using it.
I advised you to use snoop package to debug your code more efficient , by this package you can track the executing step by step.
to install it run:
Pip install snoop
Import snoop
add snoop decorator to count_bits()
for about the package see this link
https://pypi.org/project/snoop/
the difference in the output between the two methods is because of the way in which python handles fundamental data types. Fundamental data types such as float, ints, strings etc are passed by value, whereas complex data types such as dict, list, tuple etc are passed by reference. changes made to fundamental data types within a function will therefore only be changed within the local scope of the function, however changes made to a complex data type will change the original object. See below for an example:
x = 5
def test_sum(i : int):
print(i)
i += 5
print(i)
# the integer value in the global scope is not changed, changes in test_sum() are only applied within the function scope
test_sum(x)
print(x)
y = [1,2,3,4]
def test_append(l : list):
print(l)
l.append(10)
print(l)
# the list in the global scope has '10' appended after being passed to test_append by reference
test_append(y)
print(y)
This happens because it's far cheaper computationally to pass a reference to a large object in memory than to copy it into the local scope of the function. For more information on the difference, there are thousands of resources available by searching "what is the difference between by reference and by value in programming".
As for your code, it seems to me the only difference is you should alter your first snippet as follows:
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1 # when x == 1
# assign the returned count to the count variable in this function scope
count = count_bits(int(n/2), count)
print("counter")
return count
The second code snippet you wrote is almost identical, it just doesn't assign the new value to the 'count' variable. Does this answer your question?

How to repeat a function till its return value matches its previous return value?

I have a Python function that returns a value.
def population_gen(P)
...
return fittest
Now, I need to write a piece of code that compares the "fittest" with last iteration of the function call population_gen(P) that returned "fittest" and stops when the current fittest becomes equal to the previous one.
What I have tried is the following:
def population_gen(P)
...
return fittest
gen_count = 0
max_gen_count = 10
while gen_count <= max_gen_count:
population_gen(Pop)
gen_count += 1
I can run this any number of times (11 in this case) and make sure that my code is working. But, I do not want to it run 11 times, rather keep the while loop running till the aforementioned condition is met. I also wanted to mention that this function generates a population from a given initial population. Then, I feed this function again with the population that it generates and successfully keep it running for as many times I want. But I cannot implement the condition of comparing the "fittest" value.
Please help. Thanks in advance!
I assume that you want to keep iterating through the loop if the current and last values of the function returned is not equal.
Theory:
You can declare two variables, one inside loop and one outside. The work of inside one is to get the value of function returned by the function, and the outside one will be used to keep check of the previous value, so that it doesn't get lost (I mean the previous value here).
Now, you know, how simple it is, let's implement it. First, for the example, I would be creating a function that returns a random value from a list.
import random # Importing random module for help in choosing random value
def foo():
_list = [1, 2, 3]
return random.choice(_list)
Now, we have our example function, let's create a loop to keep check. NOTE: The type of loop you are using can be used, but the more efficient one is to use for loop, as you can use range function there as suggested by #Jab.
It's time to declare variables and the loop:
var = None # Outside the loop, will be used as a container for previous returned value
for _ in range(1, 12): # range(min. value, max. value [not included])
a = foo() # Creating a variable 'a', that would be holding current returned value from the function
if var is not None and a == var: # For first iteration, it would be None but the value will be assigned to 'var' in for it's next iteration and checking it whether the prev. and current values are equal or not
break # Break the loop
var = a # Assigns 'var' the value if the loop is still running
Now, the above example can be used as the answer. But, what if you want to check it? So, following is the code provided with the example for debugging purposes:
import random
def func():
l = [1, 2, 3]
val = random.choice(l)
print(val) # Printing the value, for debugging purposes
return val
var = None
for x in range(1, 12):
a = func()
if var is not None and a == var:
print(f'\nSame values after {x} iterations') # It will show, after how many iterations the loop stopped
break
var = a
Now, I ran the above code 3 times, the following is the output:
OUTPUT:
>>> 3
>>> 3
>>>
>>> Same values after 2 iterations
>>> 2
>>> 3
>>> 2
>>> 2
>>>
>>> Same values after 4 iterations
>>> 2
>>> 2
>>>
>>> Same values after 2 iterations
I hope you understood the concept.
Use a new default variable set as maximum in the arguments. Like -
def population_gen(P, prev_fit = 99999): if prev_fit < population_gen(P): return prev_fit
Assuming you want to call the function population_gen until it's last two invocations return the same value. The output of population_gen becomes the input in the next iteration.
import random
def population(limit: int) -> int:
return random.randint(1, limit)
prev = None
cur = population(10)
while prev != cur:
prev = cur
cur = population(prev)
print(prev)

Why does the i in second loop counts as 1 less than the given input by the user?

Second i in loop counts as 1 less than the given input by the user for some reason. I dont know where the i changes it's value
One way i tried to fix it is by changing
for i in range (int(i))
to
for i in range (int(i)+1)
Does fix the problem but idk why it is needed
i = input()
AliceShapeSequence = []
BobShapeSequence = []
for i in range (int(i)):
AliceShape = input()
AliceShapeSequence.append(AliceShape)
for i in range (int(i)):
BobShape = input()
BobShapeSequence.append(BobShape)
print(AliceShapeSequence)
print(BobShapeSequence)
Excepted both lists to have 3 values but AliceShapeSequence has 3 and BobShapeSequence has 2 (depends on the input i used 3 for this example)
This is actually quite an interesting question. Let's step through what's happening here.
i = input()
Say your user enters 3. Now i is 3, as expected.
for i in range (int(i)):
First, range(int(i)) is called. Python's range, when called with one argument, creates a range from 0 (inclusive) to that argument (exclusive). Since i is 3, this creates a range containing the values 0, 1, and 2.
Now, you start iterating, with an iteration variable i.i is first set to 0, then the code block is run, 1, and the code block is run, then 2, and the code block is run for a final time.
However, there's a trick. Due to Python's scoping rules, you iteration variable i is actually the same variable as the global variable i. So during iteration you were updating the global i value to 0, then 1, then 2. So when we start the next loop:
for i in range (int(i)):
range(int(i)) is range(2), which creates a range with the elements 0 and 1—not what you wanted.
To fix this, change the iteration variables to something else—perhaps j, as another answer suggests.
The i in the for loop is updating every time it runs.
range(5) // gives you 0-4
i is set to 4 on the last cycle of the loop. Use a different variable name.
The variable i is global any python function or class defined in this module is able to access this variable:
Your key value and your input variables are at the same scope.
So you input 3 and the loops starts counting at 0 to after the loop i is counted up to 2 after the first loop so the loop will start with 2:
i = input()
AliceShapeSequence = []
BobShapeSequence = []
for x in range (int(i)):
AliceShape = input()
AliceShapeSequence.append(AliceShape)
for x in range (int(i)):
BobShape = input()
BobShapeSequence.append(BobShape)
print(AliceShapeSequence)
print(BobShapeSequence)
Just change upcounting key to other variable name you are fine.
To make it easy to understand just run:
i = 3
AliceShapeSequence = []
BobShapeSequence = []
for i in range (int(i)):
AliceShape = i
AliceShapeSequence.append(AliceShape)
print("first loop: i = {0} after {1} loop cycle".format(i, i+1))
for i in range (int(i)):
BobShape = i
BobShapeSequence.append(BobShape)
print("second loop: i = {0} after {1} loop cycle".format(i, i+1))
print(AliceShapeSequence)
print(BobShapeSequence)
I ran your code, but changed for i in range(int(i)) into for j in range(int(i)) for both cases and then it behaved as expected.
You shouldn't use the same variable names, otherwise you may have issues with unexpected side effects.

Converting "set var%number%=[value]" where 'number' changes value to a Python script

I have recently undertook converting all of my older batch files towards running on Python 3.6 to allow for them to run on multiple OS's. During this process I have hit a wall, my question is how to best convert these following batch commands to something capable of being run in Python. I have looked around extensively and it has all been for naught. I am open to suggestions.
set number=1
set var%number%=[some value here]
I have already tried things similar to this in Python:
number = 1
("var"+str(number)) = [some value here]
As well as this to try to get the interpreter to process the variable I'm going to assign to done before I attempt assignment.
number = 1
var = ("var"+str(number))
(var) = [some value here]
The reason why I am trying to convert the first method is so that the program could initially set a changing value to 'var1' then 'var2' then 'var3' etc. Is this one of those things that is unique to batch files and not languages such as Python?
If you're trying to change the value of number then set the value by
number = 1
And change it again by doing the same
number = 6
Edit: I see what you're trying to do, it is better not to do this in Python and you should try to avoid it for multiple reasons, but anyways here's an example of using exec (assuming number equals to 1):
exec("var"+str(number)+"=0")
Which would run
var1=0
Like this answer says, you could just do:
numbers = [1, 2, 3]
for n in numbers:
exec('var_%d = [20]' % n)
print(var_1) # prints [20]
print(var_2) # prints [20]
print(var_3) # prints [20]
However, there are arguments against the usage of exec in that answer, so you should consider a dictionary. You can define as many keys (var1, var2, etc...) as you need.
if __name__ == '__main__':
v = {}
numbers = [1, 2, 3]
for n in numbers:
v['var' + str(n)] = 10 # initial value 10
print(v['var1']) # 10
print(v['var2']) # 10
print(v['var3']) # 10
v['var2'] = 20 # update value to 20
print(v['var2']) # 20
Or if you need more customization, maybe consider defining your own class?
class MyVar(object):
def set(self, number, value):
setattr(self, 'var' + str(number), value)
if __name__ == '__main__':
number = 1
v = MyVar()
v.set(number , 20)
print(v.var1) # prints 20
v.set(number , 40)
print(v.var1) # now prints 40
v.var1 = 50
print(v.var1) # now prints 50
It's hard to recommend anything else without knowing more about what you want to do.

I am getting the error "Can't assign to Operator"

I am not sure why it is giving me this error... the part it says is giving me the error is the previousFirst + previousSecond = previousSecond. If you are wondering the goal is to print out as many fibonnaci numbers that the user wants to print out.
def fibbonaci():
fibbNumber = input("How many Fibonacci numbers should I print for you?")
fibbNumber = int(fibbNumber)
global counter
counter = 0
global previousFirst
previousFirst = 0
global previousSecond
previousSecond = 1
global previousSaved
previousSaved = 1
while (counter < fibbNumber):
previousSaved = previousSecond
previousFirst + previousSecond = previousSecond
print (previousFirst)
print (previousSecond)
counter += 1
fibbonaci()
1. You have the assignment turned around. The format is
<i>variable</i> = <i>new value</i>
so make that:
previous_second = previous second + previous_first
2. A more normal (non-Python) way to do this is:
next = current + previous
previous = current
current = next
where "next" is a temporary variable to compute the next in sequence.
3. Python has the ability to do multiple assignments, eliminating the need for temporary variables in this an many other cases. You can do all of the above with:
current, previous = current+previous, current
Both computations on the right are done before any assigning happens. The new value of current is the sum of the old values current+previous, and the new value of previous is the old value of current. Put that in a "for xyz in range(n):" loop, after initializing current=0, previous=1, and you get a loop that works for all non-negative n (including 0). The final value of current is your result.
4. Its "Fibonacci", not "Fibbonaci", and the guy's real name was Leonardo.
The left hand side of an assignment statement must be a valid name, not an expression. (The + is an operator, which means its included in expressions)
This is the line of the culprit,
previousFirst + previousSecond = previousSecond
Also, your code does have a little bit of formatting issues, namely the indentation is bad for the first function. Normally you can get away with this, but being Python, it's part of the language syntax. It also might just be how you copied and pasted it to Stack, take a look at the tips in How to Edit and How to Format bars, when editing your post.
The line should be
previousSecond = previousFirst + previousSecond
That's the problem
UPDATE:
There are some logic error to calculate fibbonaci number, the while part coule be:
while (counter < fibbNumber):
previousSaved = previousSecond
previousSecond = previousFirst + previousSecond
previousFirst = previousSaved
print (previousFirst)
print (previousSecond)
counter += 1

Categories

Resources